forked from M-Labs/artiq-zynq
Compare commits
250 Commits
master
...
merge-rpc-
Author | SHA1 | Date | |
---|---|---|---|
|
df4988c774 | ||
800c12e794 | |||
d36899b485 | |||
6b3fa98d70 | |||
4a522ecb3b | |||
6be5ffe4e4 | |||
44ef13d1c0 | |||
|
8e0229d265 | ||
|
2ddb4d259f | ||
|
5c054cc901 | ||
c281505aa0 | |||
db0e41af6d | |||
a07ebb4dc0 | |||
d5402d899f | |||
bbecead9a3 | |||
c834e4f503 | |||
dc862a9051 | |||
19e60073de | |||
a546d0f95b | |||
d6ae646790 | |||
38f4d6cd2e | |||
f3310324d7 | |||
4a4f7b0ddc | |||
0812f22423 | |||
014ff23daf | |||
b638fce069 | |||
ac4887ea33 | |||
edf1999bb2 | |||
9ec6a1feab | |||
8e144e41de | |||
512b6bac12 | |||
e3ed41ff32 | |||
97a63ca8d0 | |||
d652f01379 | |||
d6ef5fd064 | |||
f0febe0ee4 | |||
dce8c974eb | |||
01339c9e78 | |||
fa1f300067 | |||
191a22f506 | |||
3e3fb207a5 | |||
abeaf5aca7 | |||
e20c77650d | |||
7a8f96dbd9 | |||
596edb480c | |||
4f457d9c24 | |||
24df52268e | |||
48c9b43171 | |||
57d7f01b04 | |||
efc432352e | |||
88ffd3b77b | |||
07210c7b09 | |||
1cd704c390 | |||
715a2dd04c | |||
def4d989cd | |||
f9a8c76654 | |||
1d731a3589 | |||
3cf86a6335 | |||
78bc162749 | |||
b974d7ddee | |||
2c5de32a4c | |||
5a7dbb3f29 | |||
d27d06f960 | |||
14f7778732 | |||
dcfb28ce61 | |||
433a9cdaf1 | |||
a79bef2243 | |||
7b21889055 | |||
c6ef9b117c | |||
dcfaf587ec | |||
a92561b9d3 | |||
dc54d5f9b6 | |||
161044e78f | |||
32f3c636c5 | |||
50cafad18b | |||
426500d2f9 | |||
ebdb08180d | |||
0530e596ba | |||
7502f3a765 | |||
fa237088a0 | |||
ad557edd58 | |||
f5fa5532b6 | |||
ae0d724bf8 | |||
6c834899e9 | |||
a22b13cc46 | |||
85e5c08d7f | |||
3c17362fad | |||
4f2a0986da | |||
4a2218641f | |||
9a06cd9d27 | |||
b56b50b147 | |||
f38117774f | |||
880ba6b206 | |||
90ef57f62c | |||
accac99f48 | |||
412ae98266 | |||
8a89f2b62c | |||
cc5fdb64c7 | |||
663fdcbabf | |||
f83ab5a662 | |||
6f5ba46e89 | |||
8923feceac | |||
97ca72f7f1 | |||
acaf388dbb | |||
8788d6458e | |||
efe315c21d | |||
84becfe2c0 | |||
a4fbb96296 | |||
64fecf09b7 | |||
31fb2b388a | |||
e045837b67 | |||
ada3f2e704 | |||
8be5048cd3 | |||
e8db2a4b49 | |||
4218354e65 | |||
2376f9ab5e | |||
0b27349ec4 | |||
21eb1cab1a | |||
3096daaaee | |||
4fbfccf575 | |||
5c40115945 | |||
a5e3580d18 | |||
3582af564d | |||
742ce9fdde | |||
c4de1c261a | |||
219c075931 | |||
d04a7decfe | |||
0efa83e956 | |||
4fa824f42b | |||
ab0c205dd2 | |||
8d2bb09149 | |||
41295b0e01 | |||
aaec0abdf6 | |||
e241957419 | |||
50262b3f0c | |||
827c6c1306 | |||
e6863263b4 | |||
d7f45d473e | |||
35250b3f56 | |||
2ed2ffe417 | |||
18e05c91e1 | |||
e3d3cb2311 | |||
f543501012 | |||
111ac0c716 | |||
8128dc0b56 | |||
cbcda286dc | |||
dcb6129b0e | |||
f5933092c9 | |||
f25e261bdd | |||
44c2c0fe4d | |||
4c2c23fcdd | |||
480a80cab7 | |||
2ba4d8935d | |||
8c8a5d53b9 | |||
852123b42a | |||
f1fd55dee5 | |||
21d98711c1 | |||
0ae2138034 | |||
ce9d38827b | |||
1b474d2dd4 | |||
4f1689f254 | |||
506c741238 | |||
8815f76114 | |||
ef18fa4c6d | |||
faf9714e10 | |||
c90cb7adad | |||
8d4e42be32 | |||
dcd3cbc488 | |||
fcb38fae6c | |||
bfd8343876 | |||
4039431533 | |||
3f9bd06468 | |||
bb65074254 | |||
c2a6fb72f7 | |||
faa335461d | |||
be01fbd943 | |||
9e8b554c6d | |||
b4ff6dda24 | |||
35204d4716 | |||
93493397ae | |||
a5ccabb8e6 | |||
e5207b86db | |||
28fe61b061 | |||
ce55e2ed23 | |||
cb9dae1951 | |||
07b425a67a | |||
57ae8619f8 | |||
32048ead20 | |||
113c8eb0b8 | |||
9259cffeb2 | |||
|
7c336f7770 | ||
291a782db0 | |||
479e6afd12 | |||
c865c82883 | |||
7dbffadf08 | |||
1695076baf | |||
b7155c9ded | |||
7f75dbd87e | |||
5c62d6a141 | |||
74a453b3a5 | |||
eab839aed0 | |||
dffe00990b | |||
a374d8a02f | |||
03d9827a5a | |||
01093f02cf | |||
86b9045417 | |||
7e26a87aed | |||
a277e89b3a | |||
a99ee3ec32 | |||
c3f9a76f2a | |||
4dfd82f6ec | |||
8bb1727e64 | |||
1e742cc390 | |||
2fe73505c8 | |||
36d8ffec3b | |||
91ed035bef | |||
d5a91a7697 | |||
5da76f2abb | |||
5e4bf8bbf7 | |||
cdc8ad8aee | |||
1931957bc0 | |||
805f1d4eff | |||
ae07c05db4 | |||
fc285fcd13 | |||
b0706f470d | |||
6ede148810 | |||
ccf8ae5b5d | |||
653d143784 | |||
050b2457a4 | |||
eb78e4e2da | |||
afecc83ecf | |||
04437e876c | |||
42f94487cf | |||
d474cf58a5 | |||
71427f8ec8 | |||
d6ab23de1f | |||
949adbd90a | |||
49689dedf1 | |||
538c012bc4 | |||
ba162b3997 | |||
321a8e1522 | |||
0fb278f7cb | |||
fa187fb37a | |||
e592efb2b8 | |||
2faf74f708 | |||
bb35d6b46a | |||
760f46a115 | |||
|
6ccd0cb389 | ||
|
63250240d2 | ||
|
9f898dd2b8 |
31
README.md
31
README.md
@ -4,7 +4,7 @@ ARTIQ on Zynq
|
||||
How to use
|
||||
----------
|
||||
|
||||
1. Install ARTIQ-6 or newer.
|
||||
1. Install ARTIQ-7 or newer.
|
||||
2. Select the latest successful build on Hydra: https://nixbld.m-labs.hk/jobset/artiq/zynq
|
||||
3. Search for the job named ``<board>-<variant>-sd`` (for example: ``zc706-nist_clock-sd`` or ``zc706-nist_qc2-sd``).
|
||||
4. Download the ``boot.bin`` "binary distribution" and place it at the root of a FAT-formatted SD card.
|
||||
@ -24,48 +24,47 @@ The following configuration keys are available:
|
||||
- ``ip``: IPv4 address.
|
||||
- ``ip6``: IPv6 address.
|
||||
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
||||
- ``rtioclk``: source of RTIO clock; valid values are ``external`` and ``internal``.
|
||||
- ``rtio_clock``: source of RTIO clock; valid values are ``ext0_bypass`` and ``int_125``.
|
||||
- ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only.
|
||||
|
||||
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
||||
not implemented as it seems not very useful.
|
||||
|
||||
Development instructions
|
||||
------------------------
|
||||
|
||||
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
|
||||
ARTIQ on Zynq is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
|
||||
Pure build with Nix and execution on a remote JTAG server:
|
||||
|
||||
```shell
|
||||
nix-build -A zc706-simple-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-jtag
|
||||
nix build .#zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
|
||||
./remote_run.sh
|
||||
```
|
||||
|
||||
Impure incremental build and execution on a remote JTAG server:
|
||||
|
||||
```shell
|
||||
nix-shell
|
||||
nix develop
|
||||
cd src
|
||||
gateware/zc706.py -g ../build/gateware # build gateware
|
||||
make # build firmware
|
||||
gateware/zc706.py -g ../build/gateware -V <variant> # build gateware
|
||||
make GWARGS="-V <variant>" <runtime/satman> # build firmware
|
||||
cd ..
|
||||
./remote_run.sh -i
|
||||
```
|
||||
|
||||
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.
|
||||
- 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.
|
||||
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``flake.lock`` in sync.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (C) 2019-2020 M-Labs Limited.
|
||||
Copyright (C) 2019-2022 M-Labs Limited.
|
||||
|
||||
ARTIQ is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
|
File diff suppressed because it is too large
Load Diff
99
default.nix
99
default.nix
@ -1,99 +0,0 @@
|
||||
{
|
||||
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
Normal file
60
demo.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"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": {
|
||||
"host": "192.168.1.52",
|
||||
"ref_period": 1e-9,
|
||||
"ref_multiplier": 1,
|
||||
"ref_multiplier": 8,
|
||||
"target": "cortexa9"
|
||||
}
|
||||
},
|
||||
@ -22,30 +22,18 @@ device_db = {
|
||||
"module": "artiq.coredevice.dma",
|
||||
"class": "CoreDMA"
|
||||
},
|
||||
# led? are common to all variants
|
||||
|
||||
"i2c_switch": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.i2c",
|
||||
"class": "PCA9548"
|
||||
},
|
||||
|
||||
"led0": {
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLOut",
|
||||
"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}
|
||||
"arguments": {"channel": 41},
|
||||
},
|
||||
}
|
||||
|
||||
@ -55,9 +43,22 @@ for i in range(40):
|
||||
"type": "local",
|
||||
"module": "artiq.coredevice.ttl",
|
||||
"class": "TTLInOut",
|
||||
"arguments": {"channel": 4+i}
|
||||
"arguments": {"channel": 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
|
||||
device_db.update(
|
||||
loop_out="ttl0",
|
||||
|
235
flake.lock
generated
Normal file
235
flake.lock
generated
Normal file
@ -0,0 +1,235 @@
|
||||
{
|
||||
"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": 1672816435,
|
||||
"narHash": "sha256-cH2i+1eoJ+K9rAxctVjUR5oNWi54USjbtXPYj5a0j7A=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "1be7e2a2e1d142802a52792865b19c8874fd0e9d",
|
||||
"revCount": 8257,
|
||||
"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": 1672878308,
|
||||
"narHash": "sha256-0+fl6PHokhtSV+w58z2QD2rTf8QhcOGsT9o4LwHHZHE=",
|
||||
"owner": "mozilla",
|
||||
"repo": "nixpkgs-mozilla",
|
||||
"rev": "d38863db88e100866b3e494a651ee4962b762fcc",
|
||||
"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": "refs/heads/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
|
||||
}
|
373
flake.nix
Normal file
373
flake.nix
Normal file
@ -0,0 +1,373 @@
|
||||
{
|
||||
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-2020-04-11";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "peteut";
|
||||
repo = "ramda.py";
|
||||
rev = "d315a9717ebd639366bf3fe26bad9e3d08ec3c49";
|
||||
sha256 = "sha256-bmSt/IHDnULsZjsC6edELnNH7LoJSVF4L4XhwBAXRkY=";
|
||||
};
|
||||
|
||||
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.5.5
|
||||
'';
|
||||
};
|
||||
|
||||
migen-axi = pkgs.python3Packages.buildPythonPackage {
|
||||
pname = "migen-axi";
|
||||
version = "unstable-2023-01-06";
|
||||
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "peteut";
|
||||
repo = "migen-axi";
|
||||
rev = "27eaa84a70a3abfe1930c86c36c4de2cd652da35";
|
||||
sha256 = "sha256-3Y9W5ns+1wbVd14iePzgSBzE+LxnGMUDtUw3BccFt80=";
|
||||
};
|
||||
|
||||
format = "pyproject";
|
||||
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [ setuptools click numpy toolz jinja2 ramda artiqpkgs.migen artiqpkgs.misoc ];
|
||||
|
||||
checkInputs = with pkgs.python3Packages; [ pytest pytest-timeout pytest-flake8 ];
|
||||
checkPhase = "pytest";
|
||||
|
||||
# migen/misoc version checks are broken with pyproject for some reason
|
||||
postPatch = ''
|
||||
substituteInPlace pyproject.toml \
|
||||
--replace '"migen@git+https://github.com/m-labs/migen",' ""
|
||||
substituteInPlace pyproject.toml \
|
||||
--replace '"misoc@git+https://github.com/m-labs/misoc.git",' ""
|
||||
'';
|
||||
};
|
||||
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;
|
||||
|
||||
};
|
||||
}
|
60
kasli-soc-master.json
Normal file
60
kasli-soc-master.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
60
kasli-soc-satellite.json
Normal file
60
kasli-soc-satellite.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
43
local_run.sh
43
local_run.sh
@ -2,10 +2,21 @@
|
||||
|
||||
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
|
||||
load_bitstream=1
|
||||
board_type="kasli_soc"
|
||||
fw_type="runtime"
|
||||
|
||||
while getopts "h:il" opt; do
|
||||
while getopts "ilb:t:f:" opt; do
|
||||
case "$opt" in
|
||||
\?) exit 1
|
||||
;;
|
||||
@ -13,20 +24,38 @@ while getopts "h:il" opt; do
|
||||
;;
|
||||
l) load_bitstream=0
|
||||
;;
|
||||
b) board_host=$OPTARG
|
||||
;;
|
||||
t) board_type=$OPTARG
|
||||
;;
|
||||
f) fw_type=$OPTARG
|
||||
;;
|
||||
esac
|
||||
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=""
|
||||
|
||||
cd openocd
|
||||
build_dir=`pwd`/build
|
||||
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 [ $load_bitstream -eq 1 ]; then
|
||||
load_bitstream_cmd="pld load 0 ../build/gateware/top.bit;"
|
||||
load_bitstream_cmd="-g $build_dir/gateware/top.bit"
|
||||
fi
|
||||
openocd -f zc706.cfg -c "$load_bitstream_cmd load_image ../build/firmware/armv7-none-eabihf/release/szl; resume 0; exit"
|
||||
artiq_netboot $load_bitstream_cmd -f $build_dir/$fw_type.bin -b $board_host
|
||||
else
|
||||
if [ $load_bitstream -eq 1 ]; then
|
||||
load_bitstream_cmd="pld load 0 ../result/top.bit;"
|
||||
load_bitstream_cmd="-g $result_dir/top.bit"
|
||||
fi
|
||||
openocd -f zc706.cfg -c "$load_bitstream_cmd load_image ../result/szl.elf; resume 0; exit"
|
||||
fi
|
||||
artiq_netboot $load_bitstream_cmd -f $result_dir/$fw_type.bin -b $board_host
|
||||
fi
|
@ -1,24 +0,0 @@
|
||||
{ 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
|
||||
'';
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#
|
||||
# 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
|
@ -1,40 +0,0 @@
|
||||
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]
|
@ -1,95 +0,0 @@
|
||||
#
|
||||
# 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,15 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Only ZC706 supported for now.
|
||||
|
||||
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"
|
||||
impure=0
|
||||
pure_dir="result"
|
||||
impure_dir="build"
|
||||
sshopts=""
|
||||
load_bitstream=1
|
||||
board_host="192.168.1.52"
|
||||
fw_type="runtime"
|
||||
|
||||
while getopts "h:id:o:l" opt; do
|
||||
while getopts "h:id:o:lt:" opt; do
|
||||
case "$opt" in
|
||||
\?) exit 1
|
||||
;;
|
||||
@ -24,29 +37,33 @@ while getopts "h:id:o:l" opt; do
|
||||
;;
|
||||
l) load_bitstream=0
|
||||
;;
|
||||
b) board_host=$OPTARG
|
||||
;;
|
||||
t) fw_type=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
target_folder="/tmp/zynq-$USER"
|
||||
load_bitstream_cmd=""
|
||||
if [ $load_bitstream -eq 1 ]; then
|
||||
load_bitstream_cmd="pld load 0 top.bit;"
|
||||
fi
|
||||
|
||||
echo "Creating $target_folder..."
|
||||
ssh $sshopts $target_host "mkdir -p $target_folder"
|
||||
echo "Copying files..."
|
||||
rsync -e "ssh $sshopts" openocd/* $target_host:$target_folder
|
||||
rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder
|
||||
rsync -e "ssh $sshopts" -Lc $SZL/szl-zc706.elf $target_host:$target_folder/szl.elf
|
||||
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
|
||||
rsync -e "ssh $sshopts" $impure_dir/gateware/top.bit $target_host:$target_folder
|
||||
load_bitstream_cmd="-g build/gateware/top.bit"
|
||||
fi
|
||||
firmware="build/$fw_type.bin"
|
||||
else
|
||||
rsync -e "ssh $sshopts" -Lc $pure_dir/szl.elf $target_host:$target_folder
|
||||
if [ $load_bitstream -eq 1 ]; then
|
||||
rsync -e "ssh $sshopts" -Lc $pure_dir/top.bit $target_host:$target_folder
|
||||
load_bitstream_cmd="-g $pure_dir/top.bit"
|
||||
fi
|
||||
firmware="$pure_dir/$fw_type.bin"
|
||||
fi
|
||||
echo "Programming board..."
|
||||
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'$load_bitstream_cmd load_image szl.elf; resume 0; exit'"
|
||||
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'"
|
||||
sleep 5
|
||||
artiq_netboot $load_bitstream_cmd -f $firmware -b $board_host
|
||||
|
@ -1,24 +0,0 @@
|
||||
{ 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;
|
||||
})
|
31
shell.nix
31
shell.nix
@ -1,31 +0,0 @@
|
||||
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";
|
||||
}
|
330
src/Cargo.lock
generated
330
src/Cargo.lock
generated
@ -1,10 +1,12 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5444eec77a9ec2bfe4524139e09195862e981400c4358d3b760cae634e4c4ee"
|
||||
checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -13,33 +15,43 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
|
||||
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[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]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.58"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -48,16 +60,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.32"
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bc4ac2c824d2bfc612cba57708198547e9a26943af0632aff033e0693074d5c"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3748f82c7d366a0b4950257d19db685d4958d2fa27c6d164a3f069fec42b748b"
|
||||
|
||||
[[package]]
|
||||
name = "core_io"
|
||||
version = "0.1.20200410"
|
||||
version = "0.1.20210325"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97f8932064288cc79feb4d343a399d353a6f6f001e586ece47fe518a9e8507df"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
|
||||
dependencies = [
|
||||
"build_const",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -66,28 +95,13 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "dwarf"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"compiler_builtins",
|
||||
"cslice",
|
||||
"libc",
|
||||
"unwind",
|
||||
]
|
||||
@ -102,9 +116,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.4"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb"
|
||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
@ -112,9 +126,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fatfs"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e"
|
||||
checksum = "e18f80a87439240dac45d927fd8f8081b6f1e34c03e97271189fa8a8c2e96c8f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
@ -124,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -138,9 +152,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -148,23 +162,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -172,51 +185,79 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.5"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"libsupport_zynq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libasync"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
"libcortex_a9",
|
||||
"nb 0.1.3",
|
||||
"nb 1.0.0",
|
||||
"pin-utils",
|
||||
"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]]
|
||||
name = "libboard_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"embedded-hal",
|
||||
"libasync",
|
||||
"libcortex_a9",
|
||||
"libregister",
|
||||
"log",
|
||||
@ -234,25 +275,37 @@ dependencies = [
|
||||
"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]]
|
||||
name = "libcortex_a9"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"libregister",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.1"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||
|
||||
[[package]]
|
||||
name = "libregister"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"vcell",
|
||||
@ -262,8 +315,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "libsupport_zynq"
|
||||
version = "0.0.0"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#e8ba73a8c74f79f543db6a495ac65aae68e18bab"
|
||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#67dbb5932fa8ff5f143983476f741f945871d286"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"compiler_builtins",
|
||||
"libboard_zynq",
|
||||
"libcortex_a9",
|
||||
@ -274,17 +328,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.8.4"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
|
||||
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -299,12 +353,6 @@ version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
@ -322,9 +370,9 @@ checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0396233fb2d5b0ae3f05ff6aba9a09185f7f6e70f87fb01147d545f85364665"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -333,32 +381,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.12"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.23"
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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",
|
||||
]
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -366,32 +400,20 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -407,17 +429,20 @@ name = "runtime"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"build_zynq",
|
||||
"byteorder",
|
||||
"core_io",
|
||||
"cslice",
|
||||
"dwarf",
|
||||
"dyld",
|
||||
"embedded-hal",
|
||||
"fatfs",
|
||||
"futures",
|
||||
"io",
|
||||
"libasync",
|
||||
"libboard_artiq",
|
||||
"libboard_zynq",
|
||||
"libc",
|
||||
"libconfig",
|
||||
"libcortex_a9",
|
||||
"libm",
|
||||
"libregister",
|
||||
@ -433,10 +458,43 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoltcp"
|
||||
version = "0.6.0"
|
||||
name = "rustc_version"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a"
|
||||
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]]
|
||||
name = "smoltcp"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4a069bef843d170df47e7c0a8bf8d037f217d9f5b325865acc3e466ffe40d3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
@ -445,48 +503,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.38"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "szl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cstr_core",
|
||||
"libboard_zynq",
|
||||
"libcortex_a9",
|
||||
"libsupport_zynq",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unwind"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"compiler_builtins",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
@ -496,9 +542,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"
|
||||
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
|
||||
dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
|
@ -2,19 +2,16 @@
|
||||
members = [
|
||||
"libc",
|
||||
"libdyld",
|
||||
"libcoreio",
|
||||
"libdwarf",
|
||||
"libio",
|
||||
"libunwind",
|
||||
"runtime",
|
||||
"szl"
|
||||
"satman"
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
debug = true
|
||||
codegen-units = 1
|
||||
opt-level = 'z'
|
||||
opt-level = 2
|
||||
lto = true
|
||||
|
||||
[patch.crates-io]
|
||||
core_io = { path = "./libcoreio" }
|
||||
|
38
src/Makefile
38
src/Makefile
@ -1,20 +1,34 @@
|
||||
VARIANT := simple
|
||||
TARGET := zc706
|
||||
GWARGS := -V nist_clock
|
||||
|
||||
all: ../build/firmware/armv7-none-eabihf/release/szl
|
||||
all: runtime
|
||||
|
||||
.PHONY: all
|
||||
runtime: ../build/runtime.bin
|
||||
|
||||
satman: ../build/satman.bin
|
||||
|
||||
../build/pl.rs ../build/rustc-cfg: gateware/*
|
||||
.PHONY: all runtime_target satman_target
|
||||
|
||||
../build/pl.rs ../build/rustc-cfg ../build/mem.rs: gateware/*
|
||||
mkdir -p ../build
|
||||
python gateware/zc706.py -r ../build/pl.rs -c ../build/rustc-cfg -V $(VARIANT)
|
||||
python gateware/$(TARGET).py -r ../build/pl.rs -c ../build/rustc-cfg -m ../build/mem.rs $(GWARGS)
|
||||
|
||||
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg $(shell find . -path ./szl -prune -o -print)
|
||||
XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p runtime --target-dir ../build/firmware
|
||||
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
||||
cd runtime && \
|
||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||
cargo xbuild --release \
|
||||
--target-dir ../../build/firmware \
|
||||
--no-default-features --features=target_$(TARGET)
|
||||
|
||||
../build/szl-payload.bin.lzma: ../build/firmware/armv7-none-eabihf/release/runtime
|
||||
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/szl-payload.bin
|
||||
lzma --keep -f ../build/szl-payload.bin
|
||||
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
||||
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
||||
|
||||
../build/firmware/armv7-none-eabihf/release/szl: .cargo/* armv7-none-eabihf.json Cargo.lock Cargo.toml szl/* szl/src/* ../build/szl-payload.bin.lzma
|
||||
XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p szl --target-dir ../build/firmware
|
||||
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
||||
cd satman && \
|
||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||
cargo xbuild --release \
|
||||
--target-dir ../../build/firmware \
|
||||
--no-default-features --features=target_$(TARGET)
|
||||
|
||||
../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman
|
||||
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin
|
@ -7,8 +7,7 @@ from misoc.interconnect.csr import *
|
||||
|
||||
from artiq.gateware import rtio
|
||||
|
||||
|
||||
OUT_BURST_LEN = 4
|
||||
OUT_BURST_LEN = 10
|
||||
IN_BURST_LEN = 4
|
||||
|
||||
|
||||
@ -98,7 +97,7 @@ class Engine(Module, AutoCSR):
|
||||
### Write
|
||||
self.comb += [
|
||||
w.data.eq(self.din),
|
||||
aw.addr.eq(self.addr_base.storage+32), # Write to next cache line
|
||||
aw.addr.eq(self.addr_base.storage+96),
|
||||
w.strb.eq(0xff),
|
||||
aw.burst.eq(axi.Burst.incr.value),
|
||||
aw.len.eq(IN_BURST_LEN-1), # Number of transfers in burst minus 1
|
||||
@ -191,22 +190,31 @@ class KernelInitiator(Module, AutoCSR):
|
||||
cmd_read.eq(cmd == 1)
|
||||
]
|
||||
|
||||
out_len = Signal(8)
|
||||
dout_cases = {}
|
||||
dout_cases[0] = [
|
||||
cmd.eq(self.engine.dout[:8]),
|
||||
out_len.eq(self.engine.dout[8:16]),
|
||||
cri.chan_sel.eq(self.engine.dout[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] = [
|
||||
cri.o_timestamp.eq(self.engine.dout)
|
||||
cri.o_timestamp.eq(self.engine.dout),
|
||||
cri.i_timeout.eq(self.engine.dout)
|
||||
]
|
||||
dout_cases[2] = [cri.o_data.eq(self.engine.dout)] # only lowest 64 bits
|
||||
for i in range(8):
|
||||
target = cri.o_data[i*64:(i+1)*64]
|
||||
dout_cases[i+2] = [target.eq(self.engine.dout)]
|
||||
|
||||
self.sync += [
|
||||
cri.cmd.eq(rtio.cri.commands["nop"]),
|
||||
If(self.engine.dout_stb,
|
||||
Case(self.engine.dout_index, dout_cases),
|
||||
If(self.engine.dout_index == 2,
|
||||
If(self.engine.dout_index == out_len + 2,
|
||||
If(cmd_write, cri.cmd.eq(rtio.cri.commands["write"])),
|
||||
If(cmd_read, cri.cmd.eq(rtio.cri.commands["read"]))
|
||||
)
|
||||
@ -226,7 +234,11 @@ class KernelInitiator(Module, AutoCSR):
|
||||
)
|
||||
fsm.act("WAIT_OUT_CYCLE",
|
||||
self.engine.din_ready.eq(0),
|
||||
If(self.engine.dout_stb & (self.engine.dout_index == 3),
|
||||
If(self.engine.dout_stb & cmd_write & (self.engine.dout_index == out_len + 2),
|
||||
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")
|
||||
)
|
||||
)
|
||||
|
85
src/gateware/drtio_aux_controller.py
Normal file
85
src/gateware/drtio_aux_controller.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""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
|
544
src/gateware/kasli_soc.py
Executable file
544
src/gateware/kasli_soc.py
Executable file
@ -0,0 +1,544 @@
|
||||
#!/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,13 +11,20 @@ from migen_axi.integration.soc_core import SoCCore
|
||||
from migen_axi.platforms import zc706
|
||||
from misoc.interconnect.csr import *
|
||||
from misoc.integration import cpu_interface
|
||||
from misoc.cores import gpio
|
||||
|
||||
from artiq.gateware import rtio, nist_clock, nist_qc2
|
||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
||||
from artiq.gateware.rtio.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 analyzer
|
||||
import acpki
|
||||
import drtio_aux_controller
|
||||
|
||||
|
||||
class RTIOCRG(Module, AutoCSR):
|
||||
@ -64,25 +71,90 @@ 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):
|
||||
def __init__(self, acpki=False):
|
||||
self.acpki = acpki
|
||||
self.rustc_cfg = dict()
|
||||
|
||||
platform = zc706.Platform()
|
||||
platform.toolchain.bitstream_commands.extend([
|
||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||
])
|
||||
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_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||
|
||||
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
|
||||
self.csr_devices.append("rtio_crg")
|
||||
self.rustc_cfg["has_rtio_crg_clock_sel"] = None
|
||||
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
||||
self.platform.add_false_path_constraints(
|
||||
self.ps7.cd_sys.clk,
|
||||
@ -121,52 +193,303 @@ class ZC706(SoCCore):
|
||||
self.csr_devices.append("rtio_analyzer")
|
||||
|
||||
|
||||
class Simple(ZC706):
|
||||
def __init__(self, **kwargs):
|
||||
ZC706.__init__(self, **kwargs)
|
||||
class _MasterBase(SoCCore):
|
||||
def __init__(self, acpki=False, drtio100mhz=False):
|
||||
self.acpki = acpki
|
||||
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
|
||||
|
||||
rtio_channels = []
|
||||
for i in range(4):
|
||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
self.comb += platform.request("sfp_tx_disable_n").eq(1)
|
||||
data_pads = [
|
||||
platform.request("sfp"),
|
||||
platform.request("user_sma_mgt")
|
||||
]
|
||||
|
||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
||||
rtio_channels.append(rtio.LogChannel())
|
||||
self.submodules += SMAClkinForward(self.platform)
|
||||
|
||||
self.add_rtio(rtio_channels)
|
||||
# 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")
|
||||
|
||||
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")
|
||||
|
||||
|
||||
# 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")),
|
||||
]
|
||||
class _SatelliteBase(SoCCore):
|
||||
def __init__(self, acpki=False, drtio100mhz=False):
|
||||
self.acpki = acpki
|
||||
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
|
||||
|
||||
# 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
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
ZC706.__init__(self, **kwargs)
|
||||
|
||||
def __init__(self):
|
||||
platform = self.platform
|
||||
platform.add_extension(nist_clock.fmc_adapter_io)
|
||||
platform.add_extension(leds_fmc33)
|
||||
platform.add_extension(pmod1_33)
|
||||
platform.add_extension(_ams101_dac)
|
||||
platform.add_extension(_pmod_spi)
|
||||
|
||||
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):
|
||||
if i % 4 == 3:
|
||||
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
||||
@ -182,16 +505,40 @@ class NIST_CLOCK(ZC706):
|
||||
self.submodules += phy
|
||||
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"))
|
||||
self.submodules += 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):
|
||||
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(
|
||||
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)
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
@ -202,37 +549,51 @@ class NIST_CLOCK(ZC706):
|
||||
self.add_rtio(rtio_channels)
|
||||
|
||||
|
||||
class NIST_QC2(ZC706):
|
||||
class _NIST_QC2_RTIO:
|
||||
"""
|
||||
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
|
||||
and 24 DDS channels. Two backplanes are used.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
ZC706.__init__(self, **kwargs)
|
||||
|
||||
def __init__(self):
|
||||
platform = self.platform
|
||||
platform.add_extension(nist_qc2.fmc_adapter_io)
|
||||
platform.add_extension(leds_fmc33)
|
||||
platform.add_extension(_ams101_dac)
|
||||
platform.add_extension(pmod1_33)
|
||||
|
||||
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
|
||||
for i in range(40):
|
||||
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
||||
self.submodules += phy
|
||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||
|
||||
# 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
|
||||
for i in range(2):
|
||||
phy = ttl_simple.ClockGen(
|
||||
platform.request("clkout", i))
|
||||
self.submodules += 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):
|
||||
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
||||
@ -252,7 +613,38 @@ class NIST_QC2(ZC706):
|
||||
self.add_rtio(rtio_channels)
|
||||
|
||||
|
||||
VARIANTS = {cls.__name__.lower(): cls for cls in [Simple, NIST_CLOCK, NIST_QC2]}
|
||||
class NIST_CLOCK(ZC706, _NIST_CLOCK_RTIO):
|
||||
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):
|
||||
@ -260,6 +652,11 @@ def write_csr_file(soc, filename):
|
||||
f.write(cpu_interface.get_csr_rust(
|
||||
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
|
||||
|
||||
def write_mem_file(soc, filename):
|
||||
with open(filename, "w") as f:
|
||||
f.write(cpu_interface.get_mem_rust(
|
||||
soc.get_memory_regions(), soc.get_memory_groups(), None))
|
||||
|
||||
|
||||
def write_rustc_cfg_file(soc, filename):
|
||||
with open(filename, "w") as f:
|
||||
@ -275,13 +672,15 @@ def main():
|
||||
description="ARTIQ port to the ZC706 Zynq development kit")
|
||||
parser.add_argument("-r", default=None,
|
||||
help="build Rust interface into the specified file")
|
||||
parser.add_argument("-m", default=None,
|
||||
help="build Rust memory interface into the specified file")
|
||||
parser.add_argument("-c", default=None,
|
||||
help="build Rust compiler configuration into the specified file")
|
||||
parser.add_argument("-g", default=None,
|
||||
help="build gateware into the specified directory")
|
||||
parser.add_argument("-V", "--variant", default="simple",
|
||||
parser.add_argument("-V", "--variant", default="nist_clock",
|
||||
help="variant: "
|
||||
"[acpki_]simple/nist_clock/nist_qc2 "
|
||||
"[acpki_]nist_clock/nist_qc2[_master/_satellite][_100mhz]"
|
||||
"(default: %(default)s)")
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -289,21 +688,25 @@ def main():
|
||||
acpki = variant.startswith("acpki_")
|
||||
if acpki:
|
||||
variant = variant[6:]
|
||||
drtio100mhz = variant.endswith("_100mhz")
|
||||
if drtio100mhz:
|
||||
variant = variant[:-7]
|
||||
try:
|
||||
cls = VARIANTS[variant]
|
||||
except KeyError:
|
||||
raise SystemExit("Invalid variant (-V/--variant)")
|
||||
|
||||
soc = cls(acpki=acpki)
|
||||
soc = cls(acpki=acpki, drtio100mhz=drtio100mhz)
|
||||
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()
|
||||
|
31
src/libboard_artiq/Cargo.toml
Normal file
31
src/libboard_artiq/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[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" }
|
5
src/libboard_artiq/build.rs
Normal file
5
src/libboard_artiq/build.rs
Normal file
@ -0,0 +1,5 @@
|
||||
extern crate build_zynq;
|
||||
|
||||
fn main() {
|
||||
build_zynq::cfg();
|
||||
}
|
106
src/libboard_artiq/src/drtio_routing.rs
Normal file
106
src/libboard_artiq/src/drtio_routing.rs
Normal file
@ -0,0 +1,106 @@
|
||||
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);
|
||||
}
|
||||
}
|
167
src/libboard_artiq/src/drtioaux.rs
Normal file
167
src/libboard_artiq/src/drtioaux.rs
Normal file
@ -0,0 +1,167 @@
|
||||
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())
|
||||
})
|
||||
}
|
138
src/libboard_artiq/src/drtioaux_async.rs
Normal file
138
src/libboard_artiq/src/drtioaux_async.rs
Normal file
@ -0,0 +1,138 @@
|
||||
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
|
||||
}
|
353
src/libboard_artiq/src/drtioaux_proto.rs
Normal file
353
src/libboard_artiq/src/drtioaux_proto.rs
Normal file
@ -0,0 +1,353 @@
|
||||
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(())
|
||||
}
|
||||
|
||||
}
|
69
src/libboard_artiq/src/lib.rs
Normal file
69
src/libboard_artiq/src/lib.rs
Normal file
@ -0,0 +1,69 @@
|
||||
#![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()
|
||||
);
|
||||
});
|
||||
}
|
353
src/libboard_artiq/src/si5324.rs
Normal file
353
src/libboard_artiq/src/si5324.rs
Normal file
@ -0,0 +1,353 @@
|
||||
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_as_ckin2: 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_as_ckin2: settings.crystal_as_ckin2
|
||||
};
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address")
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register")
|
||||
}
|
||||
if !i2c.write(val).unwrap() {
|
||||
return Err("Si5324 failed to ack value")
|
||||
}
|
||||
i2c.stop().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address")
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register")
|
||||
}
|
||||
i2c.write(val).unwrap();
|
||||
i2c.stop().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
|
||||
i2c.start().unwrap();
|
||||
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||
return Err("Si5324 failed to ack write address")
|
||||
}
|
||||
if !i2c.write(reg).unwrap() {
|
||||
return Err("Si5324 failed to ack register")
|
||||
}
|
||||
i2c.restart().unwrap();
|
||||
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
|
||||
return Err("Si5324 failed to ack read address")
|
||||
}
|
||||
let val = i2c.read(false).unwrap();
|
||||
i2c.stop().unwrap();
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn rmw<F>(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where
|
||||
F: Fn(u8) -> u8 {
|
||||
let value = read(i2c, reg)?;
|
||||
write(i2c, reg, f(value))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ident(i2c: &mut I2c) -> Result<u16> {
|
||||
Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16))
|
||||
}
|
||||
|
||||
#[cfg(si5324_soft_reset)]
|
||||
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||
let val = read(i2c, 136)?;
|
||||
write_no_ack_value(i2c, 136, val | 0x80)?;
|
||||
timer.delay_us(10_000);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_xtal(i2c: &mut I2c) -> Result<bool> {
|
||||
Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0
|
||||
}
|
||||
|
||||
fn has_ckin(i2c: &mut I2c, input: Input) -> Result<bool> {
|
||||
match input {
|
||||
Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0
|
||||
Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0
|
||||
}
|
||||
}
|
||||
|
||||
fn locked(i2c: &mut I2c) -> Result<bool> {
|
||||
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
|
||||
}
|
||||
|
||||
fn monitor_lock(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||
info!("waiting for Si5324 lock...");
|
||||
let timeout = timer.get_time() + Milliseconds(20_000);
|
||||
while !locked(i2c)? {
|
||||
// Yes, lock can be really slow.
|
||||
if timer.get_time() > timeout {
|
||||
return Err("Si5324 lock timeout");
|
||||
}
|
||||
}
|
||||
info!(" ...locked");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||
#[cfg(not(si5324_soft_reset))]
|
||||
hard_reset(timer);
|
||||
|
||||
#[cfg(feature = "target_kasli_soc")]
|
||||
{
|
||||
i2c.pca954x_select(0x70, None)?;
|
||||
i2c.pca954x_select(0x71, Some(3))?;
|
||||
}
|
||||
#[cfg(feature = "target_zc706")]
|
||||
{
|
||||
i2c.pca954x_select(0x74, Some(4))?;
|
||||
}
|
||||
|
||||
if ident(i2c)? != 0x0182 {
|
||||
return Err("Si5324 does not have expected product number");
|
||||
}
|
||||
|
||||
#[cfg(si5324_soft_reset)]
|
||||
soft_reset(i2c, timer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||
let cksel_reg = match input {
|
||||
Input::Ckin1 => 0b00,
|
||||
Input::Ckin2 => 0b01,
|
||||
};
|
||||
init(i2c, timer)?;
|
||||
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
|
||||
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG
|
||||
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||
rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||
let s = map_frequency_settings(settings)?;
|
||||
let cksel_reg = match input {
|
||||
Input::Ckin1 => 0b00,
|
||||
Input::Ckin2 => 0b01,
|
||||
};
|
||||
|
||||
init(i2c, timer)?;
|
||||
if settings.crystal_as_ckin2 {
|
||||
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(())
|
||||
}
|
||||
}
|
8
src/libbuild_zynq/Cargo.toml
Normal file
8
src/libbuild_zynq/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
authors = ["M-Labs"]
|
||||
name = "build_zynq"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
name = "build_zynq"
|
||||
path = "lib.rs"
|
30
src/libbuild_zynq/lib.rs
Normal file
30
src/libbuild_zynq/lib.rs
Normal file
@ -0,0 +1,30 @@
|
||||
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());
|
||||
}
|
||||
}
|
@ -49,10 +49,10 @@ SECTIONS
|
||||
.heap (NOLOAD) : ALIGN(8)
|
||||
{
|
||||
__heap0_start = .;
|
||||
. += 0x800000;
|
||||
. += 0x8000000;
|
||||
__heap0_end = .;
|
||||
__heap1_start = .;
|
||||
. += 0x800000;
|
||||
. += 0x8000000;
|
||||
__heap1_end = .;
|
||||
} > SDRAM
|
||||
|
||||
@ -69,4 +69,18 @@ SECTIONS
|
||||
. += 0x20000;
|
||||
__stack0_start = .;
|
||||
} > 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
|
||||
}
|
@ -5,6 +5,8 @@ fn main() {
|
||||
|
||||
mod libc {
|
||||
use std::path::Path;
|
||||
use std::env;
|
||||
|
||||
pub fn compile() {
|
||||
let cfg = &mut cc::Build::new();
|
||||
cfg.no_default_flags(true);
|
||||
@ -16,6 +18,9 @@ mod libc {
|
||||
cfg.flag("-ffreestanding");
|
||||
cfg.flag("-fno-PIC");
|
||||
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("--target=armv7-none-eabihf");
|
||||
cfg.flag("-O2");
|
||||
|
@ -1,14 +0,0 @@
|
||||
[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
@ -1,896 +0,0 @@
|
||||
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()));
|
||||
}
|
||||
}
|
@ -1,551 +0,0 @@
|
||||
#[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();
|
||||
}
|
||||
}
|
@ -1,378 +0,0 @@
|
||||
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
@ -1,13 +0,0 @@
|
||||
//! 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;
|
@ -1,269 +0,0 @@
|
||||
#![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);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
//! <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,3 +15,4 @@ libc = { path = "../libc" }
|
||||
unwind = { path = "../libunwind" }
|
||||
compiler_builtins = "0.1.0"
|
||||
cfg-if = "0.1.8"
|
||||
cslice = "0.3"
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
use crate::DwarfReader;
|
||||
use core::mem;
|
||||
use cslice::CSlice;
|
||||
|
||||
pub const DW_EH_PE_omit: u8 = 0xFF;
|
||||
pub const DW_EH_PE_absptr: u8 = 0x00;
|
||||
@ -51,10 +52,48 @@ pub enum EHAction {
|
||||
|
||||
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(
|
||||
lsda: *const u8,
|
||||
context: &EHContext<'_>,
|
||||
foreign_exception: bool,
|
||||
id: u32,
|
||||
) -> Result<EHAction, ()> {
|
||||
if lsda.is_null() {
|
||||
return Ok(EHAction::None);
|
||||
@ -72,10 +111,17 @@ pub unsafe fn find_eh_action(
|
||||
};
|
||||
|
||||
let ttype_encoding = reader.read::<u8>();
|
||||
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();
|
||||
}
|
||||
// we do care about the type table
|
||||
let ttype_offset = if ttype_encoding != DW_EH_PE_omit {
|
||||
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_table_length = reader.read_uleb128();
|
||||
@ -94,11 +140,53 @@ pub unsafe fn find_eh_action(
|
||||
break;
|
||||
}
|
||||
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 {
|
||||
// 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);
|
||||
} else {
|
||||
let lpad = lpad_base + cs_lpad;
|
||||
return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
|
||||
let mut saw_cleanup = false;
|
||||
let mut action_record = action_table.offset(cs_action as isize - 1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +194,7 @@ pub unsafe fn find_eh_action(
|
||||
// So rather than returning EHAction::Terminate, we do this.
|
||||
Ok(EHAction::None)
|
||||
} else {
|
||||
// SjLj version:
|
||||
// SjLj version: (not yet modified)
|
||||
// The "IP" is an index into the call-site table, with two exceptions:
|
||||
// -1 means 'no-action', and 0 means 'terminate'.
|
||||
match ip as isize {
|
||||
@ -146,18 +234,41 @@ fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) ->
|
||||
|
||||
#[inline]
|
||||
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
|
||||
if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
|
||||
if align.is_power_of_two() {
|
||||
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(
|
||||
reader: &mut DwarfReader,
|
||||
context: &EHContext<'_>,
|
||||
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, ()> {
|
||||
if encoding == DW_EH_PE_omit {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let original_ptr = reader.ptr;
|
||||
// DW_EH_PE_aligned implies it's an absolute pointer value
|
||||
if encoding == DW_EH_PE_aligned {
|
||||
reader.ptr = round_up(reader.ptr as usize, mem::size_of::<usize>())? as *const u8;
|
||||
@ -177,19 +288,10 @@ unsafe fn read_encoded_pointer(
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
result += match encoding & 0x70 {
|
||||
DW_EH_PE_absptr => 0,
|
||||
// relative to address of the encoded value, despite the name
|
||||
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(()),
|
||||
result += if (encoding & 0x70) == DW_EH_PE_pcrel {
|
||||
original_ptr as usize
|
||||
} else {
|
||||
base
|
||||
};
|
||||
|
||||
if encoding & DW_EH_PE_indirect != 0 {
|
||||
|
@ -26,6 +26,10 @@ impl DwarfReader {
|
||||
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
|
||||
// on a 4-byte boundary. This may cause problems on platforms with strict
|
||||
// alignment requirements. By wrapping data in a "packed" struct, we are
|
||||
|
@ -2726,6 +2726,9 @@ impl Clone for Elf64_Lib {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
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_TYPE(info: Elf32_Word) -> u8 { info as u8 }
|
||||
|
@ -3,7 +3,7 @@ use core::{
|
||||
mem,
|
||||
slice,
|
||||
};
|
||||
use alloc::alloc::{alloc_zeroed, dealloc, Layout, LayoutErr};
|
||||
use alloc::alloc::{alloc_zeroed, dealloc, Layout, LayoutError};
|
||||
use super::{
|
||||
elf::*,
|
||||
Error,
|
||||
@ -27,7 +27,7 @@ pub struct Image {
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub fn new(size: usize, align: usize) -> Result<Self, LayoutErr> {
|
||||
pub fn new(size: usize, align: usize) -> Result<Self, LayoutError> {
|
||||
let layout = Layout::from_size_align(size, align)?;
|
||||
let data = unsafe {
|
||||
let ptr = alloc_zeroed(layout);
|
||||
|
@ -138,7 +138,7 @@ impl Library {
|
||||
reloc::rebind(self.arch, self, name, addr as Elf32_Word)
|
||||
}
|
||||
|
||||
pub fn exidx(&self) -> &[u32] {
|
||||
pub fn exidx(&self) -> &[EXIDX_Entry] {
|
||||
self.image.get_ref_slice_unchecked(&self.exidx)
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,8 @@ impl Relocatable for Elf32_Rela {
|
||||
enum RelType {
|
||||
None,
|
||||
Relative,
|
||||
Lookup,
|
||||
LookupAbs,
|
||||
LookupRel,
|
||||
}
|
||||
|
||||
impl RelType {
|
||||
@ -76,9 +77,11 @@ impl RelType {
|
||||
Some(RelType::Relative),
|
||||
|
||||
R_OR1K_32 | R_OR1K_GLOB_DAT | R_OR1K_JMP_SLOT
|
||||
if arch == Arch::OpenRisc => Some(RelType::Lookup),
|
||||
R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT
|
||||
if arch == Arch::Arm => Some(RelType::Lookup),
|
||||
if arch == Arch::OpenRisc => Some(RelType::LookupAbs),
|
||||
R_ARM_GLOB_DAT | R_ARM_JUMP_SLOT | R_ARM_ABS32
|
||||
if arch == Arch::Arm => Some(RelType::LookupAbs),
|
||||
|
||||
R_ARM_PREL31 if arch == Arch::Arm => Some(RelType::LookupRel),
|
||||
|
||||
_ =>
|
||||
None
|
||||
@ -106,58 +109,86 @@ pub fn relocate<R: Relocatable>(
|
||||
|
||||
let rel_type = RelType::new(arch, rel.type_info())
|
||||
.ok_or("unsupported relocation type")?;
|
||||
let value;
|
||||
match rel_type {
|
||||
let value = match rel_type {
|
||||
RelType::None =>
|
||||
return Ok(()),
|
||||
|
||||
RelType::Relative => {
|
||||
let addend = rel.addend(&lib.image);
|
||||
value = lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word;
|
||||
lib.image.ptr().wrapping_offset(addend as isize) as Elf32_Word
|
||||
}
|
||||
|
||||
RelType::Lookup => {
|
||||
RelType::LookupAbs | RelType::LookupRel => {
|
||||
let sym = sym.ok_or("relocation requires an associated symbol")?;
|
||||
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
|
||||
|
||||
if let Some(addr) = lib.lookup(sym_name) {
|
||||
let sym_addr = if let Some(addr) = lib.lookup(sym_name) {
|
||||
// First, try to resolve against itself.
|
||||
trace!("looked up symbol {} in image", format_sym_name(sym_name));
|
||||
value = lib.image.ptr() as u32 + addr;
|
||||
addr
|
||||
} else if let Some(addr) = resolve(sym_name) {
|
||||
// Second, call the user-provided function.
|
||||
trace!("resolved symbol {:?}", format_sym_name(sym_name));
|
||||
value = addr;
|
||||
addr
|
||||
} else {
|
||||
// We couldn't find it anywhere.
|
||||
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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
lib.image.write(rel.offset(), value)
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rebind(
|
||||
arch: Arch, lib: &Library, name: &[u8], value: Elf32_Word
|
||||
) -> Result<(), Error> {
|
||||
for rela in lib.pltrel() {
|
||||
let rel_type = RelType::new(arch, rela.type_info())
|
||||
.ok_or("unsupported relocation type")?;
|
||||
match rel_type {
|
||||
RelType::Lookup => {
|
||||
let sym = lib.symtab().get(ELF32_R_SYM(rela.r_info) as usize)
|
||||
.ok_or("symbol out of bounds of symbol table")?;
|
||||
let sym_name = lib.name_starting_at(sym.st_name as usize)?;
|
||||
fn rebind_symbol_to_value<R: Relocatable>(
|
||||
arch: Arch, lib: &Library,name: &[u8], value: Elf32_Word, relocs: &[R]
|
||||
) -> Result<(), Error> {
|
||||
for reloc in relocs {
|
||||
let rel_type = RelType::new(arch, reloc.type_info())
|
||||
.ok_or("unsupported relocation type")?;
|
||||
match rel_type {
|
||||
RelType::LookupAbs => {
|
||||
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 {
|
||||
lib.image.write(rela.offset(), value)?
|
||||
if sym_name == name {
|
||||
lib.image.write(reloc.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,
|
||||
// may cause performance degradation.
|
||||
dcci_slice(lib.image.data);
|
||||
|
17
src/libio/Cargo.toml
Normal file
17
src/libio/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[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 = []
|
81
src/libio/cursor.rs
Normal file
81
src/libio/cursor.rs
Normal file
@ -0,0 +1,81 @@
|
||||
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(())
|
||||
}
|
||||
}
|
22
src/libio/lib.rs
Normal file
22
src/libio/lib.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#![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;
|
@ -1,5 +1,5 @@
|
||||
use core::str::Utf8Error;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use alloc::vec;
|
||||
use alloc::string::String;
|
||||
|
||||
@ -28,21 +28,21 @@ pub trait ProtoRead {
|
||||
fn read_u16(&mut self) -> Result<u16, Self::ReadError> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
Ok(NativeEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
Ok(NativeEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
|
||||
let mut bytes = [0; 8];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
Ok(NativeEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -85,42 +85,42 @@ pub trait ProtoWrite {
|
||||
#[inline]
|
||||
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
NativeEndian::write_u16(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_i16(&mut bytes, value);
|
||||
NativeEndian::write_i16(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
NativeEndian::write_u32(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_i32(&mut bytes, value);
|
||||
NativeEndian::write_i32(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_u64(&mut bytes, value);
|
||||
NativeEndian::write_u64(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_i64(&mut bytes, value);
|
||||
NativeEndian::write_i64(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ fn main() {
|
||||
|
||||
mod llvm_libunwind {
|
||||
use std::path::Path;
|
||||
use std::env;
|
||||
|
||||
fn setup_options(cfg: &mut cc::Build) {
|
||||
cfg.no_default_flags(true);
|
||||
@ -16,9 +17,13 @@ mod llvm_libunwind {
|
||||
cfg.flag("-fno-PIC");
|
||||
cfg.flag("-Isrc");
|
||||
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("--target=armv7-none-eabihf");
|
||||
cfg.flag("-O2");
|
||||
cfg.flag("-flto");
|
||||
|
||||
cfg.flag("-std=c99");
|
||||
cfg.flag("-fstrict-aliasing");
|
||||
|
@ -107,9 +107,12 @@ struct _Unwind_Control_Block {
|
||||
} __attribute__((__aligned__(8)));
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||
(_Unwind_State state,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context);
|
||||
(int version,
|
||||
_Unwind_Action actions,
|
||||
uint64_t exceptionClass,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context,
|
||||
void* stop_parameter);
|
||||
|
||||
typedef _Unwind_Reason_Code (*__personality_routine)
|
||||
(_Unwind_State state,
|
||||
|
@ -95,9 +95,11 @@ _Unwind_Reason_Code ProcessDescriptors(
|
||||
case Descriptor::LU32:
|
||||
descriptor = getNextWord(descriptor, &length);
|
||||
descriptor = getNextWord(descriptor, &offset);
|
||||
break;
|
||||
case Descriptor::LU16:
|
||||
descriptor = getNextNibble(descriptor, &length);
|
||||
descriptor = getNextNibble(descriptor, &offset);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
return _URC_FAILURE;
|
||||
@ -183,8 +185,14 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
|
||||
if (result != _URC_CONTINUE_UNWIND)
|
||||
return result;
|
||||
|
||||
if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS)
|
||||
switch (__unw_step(reinterpret_cast<unw_cursor_t *>(context))) {
|
||||
case UNW_STEP_SUCCESS:
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
case UNW_STEP_END:
|
||||
return _URC_END_OF_STACK;
|
||||
default:
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
}
|
||||
|
||||
@ -677,6 +685,128 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor
|
||||
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.
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||
@ -724,15 +854,36 @@ _Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||
unw_cursor_t cursor;
|
||||
__unw_getcontext(&uc);
|
||||
|
||||
// _Unwind_RaiseException on EHABI will always set the reserved1 field to 0,
|
||||
// which is in the same position as private_1 below.
|
||||
// TODO(ajwong): Who wronte the above? Why is it true?
|
||||
unwind_phase2(&uc, &cursor, exception_object, true);
|
||||
if (exception_object->unwinder_cache.reserved1)
|
||||
unwind_phase2_forced(
|
||||
&uc, &cursor, exception_object,
|
||||
(_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1,
|
||||
(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.
|
||||
_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.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||
@ -1002,9 +1153,14 @@ extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
__gnu_unwind_frame(_Unwind_Exception *exception_object,
|
||||
struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
if (__unw_step(cursor) != UNW_STEP_SUCCESS)
|
||||
switch (__unw_step(cursor)) {
|
||||
case UNW_STEP_SUCCESS:
|
||||
return _URC_OK;
|
||||
case UNW_STEP_END:
|
||||
return _URC_END_OF_STACK;
|
||||
default:
|
||||
return _URC_FAILURE;
|
||||
return _URC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||
|
@ -6,9 +6,13 @@ authors = ["M-Labs"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706"]
|
||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
||||
default = ["target_zc706"]
|
||||
|
||||
[build-dependencies]
|
||||
build_zynq = { path = "../libbuild_zynq" }
|
||||
|
||||
[dependencies]
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
num-derive = "0.3"
|
||||
@ -21,18 +25,20 @@ byteorder = { version = "1.3", default-features = false }
|
||||
void = { version = "1", default-features = false }
|
||||
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||
async-recursion = "0.3"
|
||||
fatfs = { version = "0.3", features = ["core_io"], default-features = false }
|
||||
log_buffer = { version = "1.2" }
|
||||
libm = { version = "0.2", features = ["unstable"] }
|
||||
vcell = "0.1"
|
||||
|
||||
libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" }
|
||||
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"] }
|
||||
|
||||
dyld = { path = "../libdyld" }
|
||||
dwarf = { path = "../libdwarf" }
|
||||
unwind = { path = "../libunwind" }
|
||||
libc = { path = "../libc" }
|
||||
io = { path = "../libio" }
|
||||
libboard_artiq = { path = "../libboard_artiq" }
|
@ -1,28 +1,6 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
extern crate build_zynq;
|
||||
|
||||
fn main() {
|
||||
// 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");
|
||||
|
||||
// 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());
|
||||
}
|
||||
build_zynq::add_linker_script();
|
||||
build_zynq::cfg();
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ struct Header {
|
||||
}
|
||||
|
||||
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_i64(stream, header.total_byte_count as i64).await?;
|
||||
write_i8(stream, header.error_occurred as i8).await?;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::fmt;
|
||||
use core::{fmt, slice, str};
|
||||
use core::cell::RefCell;
|
||||
use core::str::Utf8Error;
|
||||
use alloc::{vec, vec::Vec, string::String, collections::BTreeMap, rc::Rc};
|
||||
use log::{info, warn, error};
|
||||
use cslice::CSlice;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
@ -17,19 +17,23 @@ use libboard_zynq::{
|
||||
},
|
||||
timer::GlobalTimer,
|
||||
};
|
||||
use libcortex_a9::{semaphore::Semaphore, mutex::Mutex};
|
||||
use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Receiver}};
|
||||
use futures::{select_biased, future::FutureExt};
|
||||
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::kernel;
|
||||
use crate::rpc;
|
||||
use crate::moninj;
|
||||
use crate::mgmt;
|
||||
use crate::analyzer;
|
||||
|
||||
use crate::rtio_mgt::{self, resolve_channel_name};
|
||||
#[cfg(has_drtio)]
|
||||
use crate::pl;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
@ -37,7 +41,6 @@ pub enum Error {
|
||||
UnexpectedPattern,
|
||||
UnrecognizedPacket,
|
||||
BufferExhausted,
|
||||
Utf8Error(Utf8Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
@ -49,7 +52,6 @@ impl fmt::Display for Error {
|
||||
Error::UnexpectedPattern => write!(f, "unexpected pattern"),
|
||||
Error::UnrecognizedPacket => write!(f, "unrecognized packet"),
|
||||
Error::BufferExhausted => write!(f, "buffer exhausted"),
|
||||
Error::Utf8Error(error) => write!(f, "UTF-8 error: {}", error),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,7 +88,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());
|
||||
|
||||
async fn write_header(stream: &TcpStream, reply: Reply) -> Result<()> {
|
||||
stream.send([0x5a, 0x5a, 0x5a, 0x5a, reply.to_u8().unwrap()].iter().copied()).await?;
|
||||
stream.send_slice(&[0x5a, 0x5a, 0x5a, 0x5a, reply.to_u8().unwrap()]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -95,13 +97,13 @@ async fn read_request(stream: &TcpStream, allow_close: bool) -> Result<Option<Re
|
||||
Ok(true) => {}
|
||||
Ok(false) =>
|
||||
return Err(Error::UnexpectedPattern),
|
||||
Err(smoltcp::Error::Illegal) => {
|
||||
Err(smoltcp::Error::Finished) => {
|
||||
if allow_close {
|
||||
info!("peer closed connection");
|
||||
return Ok(None);
|
||||
} else {
|
||||
error!("peer unexpectedly closed connection");
|
||||
return Err(smoltcp::Error::Illegal)?;
|
||||
return Err(smoltcp::Error::Finished)?;
|
||||
}
|
||||
},
|
||||
Err(e) =>
|
||||
@ -120,12 +122,46 @@ async fn read_bytes(stream: &TcpStream, max_length: usize) -> Result<Vec<u8>> {
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
async fn read_string(stream: &TcpStream, max_length: usize) -> Result<String> {
|
||||
let bytes = read_bytes(stream, max_length).await?;
|
||||
Ok(String::from_utf8(bytes).map_err(|err| Error::Utf8Error(err.utf8_error()))?)
|
||||
const RETRY_LIMIT: usize = 100;
|
||||
|
||||
async fn fast_send(sender: &mut Sender<'_, kernel::Message>, content: kernel::Message) {
|
||||
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 handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kernel::Control>>) -> Result<()> {
|
||||
async fn fast_recv(receiver: &mut Receiver<'_, kernel::Message>) -> kernel::Message {
|
||||
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;
|
||||
loop {
|
||||
let reply = control.borrow_mut().rx.async_recv().await;
|
||||
@ -138,13 +174,13 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||
let stream = stream.unwrap();
|
||||
write_header(stream, Reply::RPCRequest).await?;
|
||||
write_bool(stream, is_async).await?;
|
||||
stream.send(data.iter().copied()).await?;
|
||||
stream.send_slice(&data).await?;
|
||||
if !is_async {
|
||||
let host_request = read_request(stream, false).await?.unwrap();
|
||||
match host_request {
|
||||
Request::RPCReply => {
|
||||
let tag = read_bytes(stream, 512).await?;
|
||||
let slot = match control.borrow_mut().rx.async_recv().await {
|
||||
let slot = match fast_recv(&mut control.borrow_mut().rx).await {
|
||||
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||
other => panic!("expected root value slot from core1, not {:?}", other),
|
||||
};
|
||||
@ -157,8 +193,8 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||
0 as *mut ()
|
||||
} else {
|
||||
let mut control = control.borrow_mut();
|
||||
control.tx.async_send(kernel::Message::RpcRecvReply(Ok(size))).await;
|
||||
match control.rx.async_recv().await {
|
||||
fast_send(&mut control.tx, kernel::Message::RpcRecvReply(Ok(size))).await;
|
||||
match fast_recv(&mut control.rx).await {
|
||||
kernel::Message::RpcRecvRequest(slot) => slot,
|
||||
other => panic!("expected nested value slot from kernel CPU, not {:?}", other),
|
||||
}
|
||||
@ -173,17 +209,17 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||
kernel::Message::RpcRecvRequest(_) => (),
|
||||
other => panic!("expected (ignored) root value slot from kernel CPU, not {:?}", other),
|
||||
}
|
||||
let name = read_string(stream, 16384).await?;
|
||||
let message = read_string(stream, 16384).await?;
|
||||
let id = read_i32(stream).await? as u32;
|
||||
let message = read_i32(stream).await? as u32;
|
||||
let param = [read_i64(stream).await?,
|
||||
read_i64(stream).await?,
|
||||
read_i64(stream).await?];
|
||||
let file = read_string(stream, 16384).await?;
|
||||
let file = read_i32(stream).await? as u32;
|
||||
let line = read_i32(stream).await?;
|
||||
let column = read_i32(stream).await?;
|
||||
let function = read_string(stream, 16384).await?;
|
||||
let function = read_i32(stream).await? as u32;
|
||||
control.tx.async_send(kernel::Message::RpcRecvReply(Err(kernel::RPCException {
|
||||
name, message, param, file, line, column, function
|
||||
id, message, param, file, line, column, function
|
||||
}))).await;
|
||||
},
|
||||
_ => {
|
||||
@ -193,34 +229,55 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||
}
|
||||
}
|
||||
},
|
||||
kernel::Message::KernelFinished => {
|
||||
kernel::Message::KernelFinished(async_errors) => {
|
||||
if let Some(stream) = stream {
|
||||
write_header(stream, Reply::KernelFinished).await?;
|
||||
write_i8(stream, async_errors as i8).await?;
|
||||
}
|
||||
break;
|
||||
},
|
||||
kernel::Message::KernelException(exception, backtrace) => {
|
||||
kernel::Message::KernelException(exceptions, stack_pointers, backtrace, async_errors) => {
|
||||
match stream {
|
||||
Some(stream) => {
|
||||
// only send the exception data to host if there is host,
|
||||
// i.e. not idle/startup kernel.
|
||||
write_header(stream, Reply::KernelException).await?;
|
||||
write_chunk(stream, exception.name.as_ref()).await?;
|
||||
write_chunk(stream, exception.message.as_ref()).await?;
|
||||
write_i64(stream, exception.param[0] as i64).await?;
|
||||
write_i64(stream, exception.param[1] as i64).await?;
|
||||
write_i64(stream, exception.param[2] as i64).await?;
|
||||
write_chunk(stream, exception.file.as_ref()).await?;
|
||||
write_i32(stream, exception.line as i32).await?;
|
||||
write_i32(stream, exception.column as i32).await?;
|
||||
write_chunk(stream, exception.function.as_ref()).await?;
|
||||
write_i32(stream, backtrace.len() as i32).await?;
|
||||
for &addr in backtrace {
|
||||
write_i32(stream, addr as i32).await?;
|
||||
write_i32(stream, exceptions.len() as i32).await?;
|
||||
for exception in exceptions.iter() {
|
||||
let exception = exception.as_ref().unwrap();
|
||||
write_i32(stream, exception.id as i32).await?;
|
||||
|
||||
if exception.message.len() == usize::MAX { // exception with host string
|
||||
write_exception_string(stream, exception.message).await?;
|
||||
} else {
|
||||
let msg = str::from_utf8(unsafe { slice::from_raw_parts(exception.message.as_ptr(), exception.message.len()) })
|
||||
.unwrap()
|
||||
.replace("{rtio_channel_info:0}", &format!("0x{:04x}:{}", exception.param[0], resolve_channel_name(exception.param[0] as u32)));
|
||||
write_exception_string(stream, unsafe { CSlice::new(msg.as_ptr(), msg.len()) }).await?;
|
||||
}
|
||||
|
||||
write_i64(stream, exception.param[0] as i64).await?;
|
||||
write_i64(stream, exception.param[1] as i64).await?;
|
||||
write_i64(stream, exception.param[2] as i64).await?;
|
||||
write_exception_string(stream, exception.file).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?;
|
||||
for &(addr, sp) in backtrace {
|
||||
write_i32(stream, addr as i32).await?;
|
||||
write_i32(stream, sp as i32).await?;
|
||||
}
|
||||
write_i8(stream, async_errors as i8).await?;
|
||||
},
|
||||
None => {
|
||||
error!("Uncaught kernel exception: {:?}", exception);
|
||||
error!("Uncaught kernel exceptions: {:?}", exceptions);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -244,6 +301,11 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||
let result = DMA_RECORD_STORE.lock().get(&name).map(|v| v.clone());
|
||||
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);
|
||||
}
|
||||
@ -285,10 +347,13 @@ async fn load_kernel(buffer: &Vec<u8>, control: &Rc<RefCell<kernel::Control>>, s
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Control>>) -> Result<()> {
|
||||
async fn handle_connection(stream: &mut TcpStream, control: Rc<RefCell<kernel::Control>>, up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>) -> Result<()> {
|
||||
stream.set_ack_delay(None);
|
||||
|
||||
if !expect(stream, b"ARTIQ coredev\n").await? {
|
||||
return Err(Error::UnexpectedPattern);
|
||||
}
|
||||
stream.send_slice("e".as_bytes()).await?;
|
||||
loop {
|
||||
let request = read_request(stream, true).await?;
|
||||
if request.is_none() {
|
||||
@ -298,14 +363,14 @@ async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Contr
|
||||
match request {
|
||||
Request::SystemInfo => {
|
||||
write_header(stream, Reply::SystemInfo).await?;
|
||||
stream.send("ARZQ".bytes()).await?;
|
||||
stream.send_slice("ARZQ".as_bytes()).await?;
|
||||
},
|
||||
Request::LoadKernel => {
|
||||
let buffer = read_bytes(stream, 1024*1024).await?;
|
||||
load_kernel(&buffer, &control, Some(stream)).await?;
|
||||
},
|
||||
Request::RunKernel => {
|
||||
handle_run_kernel(Some(stream), &control).await?;
|
||||
handle_run_kernel(Some(stream), &control, &up_destinations).await?;
|
||||
},
|
||||
_ => {
|
||||
error!("unexpected request from host: {:?}", request);
|
||||
@ -315,15 +380,15 @@ async fn handle_connection(stream: &TcpStream, control: Rc<RefCell<kernel::Contr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main(timer: GlobalTimer, cfg: &config::Config) {
|
||||
let net_addresses = net_settings::get_adresses(cfg);
|
||||
pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||
let net_addresses = net_settings::get_addresses(&cfg);
|
||||
info!("network addresses: {}", net_addresses);
|
||||
|
||||
let eth = zynq::eth::Eth::default(net_addresses.hardware_addr.0.clone());
|
||||
const RX_LEN: usize = 8;
|
||||
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 = 8;
|
||||
const TX_LEN: usize = 64;
|
||||
let eth = eth.start_rx(RX_LEN);
|
||||
let mut eth = eth.start_tx(TX_LEN);
|
||||
|
||||
@ -356,28 +421,42 @@ pub fn main(timer: GlobalTimer, cfg: &config::Config) {
|
||||
|
||||
Sockets::init(32);
|
||||
|
||||
mgmt::start();
|
||||
// before, mutex was on io, but now that io isn't used...?
|
||||
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
|
||||
#[cfg(has_drtio)]
|
||||
let drtio_routing_table = Rc::new(RefCell::new(
|
||||
drtio_routing::config_routing_table(pl::csr::DRTIO.len(), &cfg)));
|
||||
#[cfg(not(has_drtio))]
|
||||
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::RoutingTable::default_empty()));
|
||||
let up_destinations = Rc::new(RefCell::new([false; drtio_routing::DEST_COUNT]));
|
||||
#[cfg(has_drtio_routing)]
|
||||
drtio_routing::interconnect_disable_all();
|
||||
|
||||
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer, &cfg);
|
||||
|
||||
analyzer::start();
|
||||
moninj::start(timer);
|
||||
moninj::start(timer, aux_mutex, drtio_routing_table);
|
||||
|
||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
||||
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
||||
if let Ok(buffer) = cfg.read("startup") {
|
||||
let idle_kernel = Rc::new(cfg.read("idle_kernel").ok());
|
||||
if let Ok(buffer) = cfg.read("startup_kernel") {
|
||||
info!("Loading startup kernel...");
|
||||
if let Ok(()) = task::block_on(load_kernel(&buffer, &control, None)) {
|
||||
info!("Starting startup kernel...");
|
||||
let _ = task::block_on(handle_run_kernel(None, &control));
|
||||
let _ = task::block_on(handle_run_kernel(None, &control, &up_destinations));
|
||||
info!("Startup kernel finished!");
|
||||
} else {
|
||||
error!("Error loading startup kernel!");
|
||||
}
|
||||
}
|
||||
|
||||
mgmt::start(cfg);
|
||||
|
||||
task::spawn(async move {
|
||||
let connection = Rc::new(Semaphore::new(1, 1));
|
||||
let terminate = Rc::new(Semaphore::new(0, 1));
|
||||
loop {
|
||||
let stream = TcpStream::accept(1381, 2048, 2048).await.unwrap();
|
||||
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
||||
|
||||
if connection.try_wait().is_none() {
|
||||
// there is an existing connection
|
||||
@ -389,22 +468,23 @@ pub fn main(timer: GlobalTimer, cfg: &config::Config) {
|
||||
let idle_kernel = idle_kernel.clone();
|
||||
let connection = connection.clone();
|
||||
let terminate = terminate.clone();
|
||||
let up_destinations = up_destinations.clone();
|
||||
|
||||
// we make sure the value of terminate is 0 before we start
|
||||
let _ = terminate.try_wait();
|
||||
task::spawn(async move {
|
||||
select_biased! {
|
||||
_ = (async {
|
||||
let _ = handle_connection(&stream, control.clone())
|
||||
let _ = handle_connection(&mut stream, control.clone(), &up_destinations)
|
||||
.await
|
||||
.map_err(|e| warn!("connection terminated: {}", e));
|
||||
if let Some(buffer) = &*idle_kernel {
|
||||
info!("Loading idle kernel");
|
||||
let _ = load_kernel(&buffer, &control, None)
|
||||
.await.map_err(|e| warn!("error loading idle kernel"));
|
||||
.await.map_err(|_| warn!("error loading idle kernel"));
|
||||
info!("Running idle kernel");
|
||||
let _ = handle_run_kernel(None, &control)
|
||||
.await.map_err(|e| warn!("error running idle kernel"));
|
||||
let _ = handle_run_kernel(None, &control, &up_destinations)
|
||||
.await.map_err(|_| warn!("error running idle kernel"));
|
||||
info!("Idle kernel terminated");
|
||||
}
|
||||
}).fuse() => (),
|
||||
@ -421,3 +501,61 @@ pub fn main(timer: GlobalTimer, cfg: &config::Config) {
|
||||
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)
|
||||
});
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
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,8 +15,9 @@
|
||||
use core::mem;
|
||||
use cslice::CSlice;
|
||||
use unwind as uw;
|
||||
use libc::{c_int, uintptr_t};
|
||||
use log::trace;
|
||||
use libc::{c_int, c_void, uintptr_t};
|
||||
use log::{trace, error};
|
||||
use crate::kernel::KERNEL_IMAGE;
|
||||
|
||||
use dwarf::eh::{self, EHAction, EHContext};
|
||||
|
||||
@ -26,10 +27,12 @@ const EXCEPTION_CLASS: uw::_Unwind_Exception_Class = 0x4d_4c_42_53_41_52_54_51;
|
||||
#[cfg(target_arch = "arm")]
|
||||
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)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Exception<'a> {
|
||||
pub name: CSlice<'a, u8>,
|
||||
pub id: u32,
|
||||
pub file: CSlice<'a, u8>,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
@ -42,31 +45,100 @@ fn str_err(_: core::str::Utf8Error) -> core::fmt::Error {
|
||||
core::fmt::Error
|
||||
}
|
||||
|
||||
impl<'a> core::fmt::Debug for Exception<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
|
||||
core::str::from_utf8(self.name.as_ref()).map_err(str_err)?,
|
||||
core::str::from_utf8(self.function.as_ref()).map_err(str_err)?,
|
||||
core::str::from_utf8(self.file.as_ref()).map_err(str_err)?,
|
||||
self.line, self.column,
|
||||
core::str::from_utf8(self.message.as_ref()).map_err(str_err)?)
|
||||
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> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "Exception {} from {} in {}:{}:{}, message: {}",
|
||||
self.id,
|
||||
exception_str(&self.function).map_err(str_err)?,
|
||||
exception_str(&self.file).map_err(str_err)?,
|
||||
self.line, self.column,
|
||||
exception_str(&self.message).map_err(str_err)?)
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_INFLIGHT_EXCEPTIONS: usize = 10;
|
||||
const MAX_BACKTRACE_SIZE: usize = 128;
|
||||
|
||||
#[repr(C)]
|
||||
struct ExceptionInfo {
|
||||
uw_exception: uw::_Unwind_Exception,
|
||||
exception: Option<Exception<'static>>,
|
||||
handled: bool,
|
||||
backtrace: [usize; MAX_BACKTRACE_SIZE],
|
||||
backtrace_size: usize
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StackPointerBacktrace {
|
||||
pub stack_pointer: usize,
|
||||
pub initial_backtrace_size: usize,
|
||||
pub current_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(
|
||||
context: *mut uw::_Unwind_Context,
|
||||
foreign_exception: bool,
|
||||
id: u32,
|
||||
) -> Result<EHAction, ()> {
|
||||
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
|
||||
let mut ip_before_instr: c_int = 0;
|
||||
@ -79,32 +151,14 @@ unsafe fn find_eh_action(
|
||||
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
|
||||
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
|
||||
};
|
||||
eh::find_eh_action(lsda, &eh_context, foreign_exception)
|
||||
eh::find_eh_action(lsda, &eh_context, foreign_exception, id)
|
||||
}
|
||||
|
||||
pub unsafe fn artiq_personality(state: uw::_Unwind_State,
|
||||
exception_object: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context)
|
||||
-> uw::_Unwind_Reason_Code {
|
||||
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;
|
||||
};
|
||||
|
||||
pub unsafe fn artiq_personality(_state: uw::_Unwind_State,
|
||||
exception_object: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context)
|
||||
-> uw::_Unwind_Reason_Code {
|
||||
// we will only do phase 2 forced unwinding now
|
||||
// The DWARF unwinder assumes that _Unwind_Context holds things like the function
|
||||
// and LSDA pointers, however ARM EHABI places them into the exception object.
|
||||
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
|
||||
@ -120,45 +174,27 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State,
|
||||
|
||||
let exception_class = (*exception_object).exception_class;
|
||||
let foreign_exception = exception_class != EXCEPTION_CLASS;
|
||||
let eh_action = match find_eh_action(context, foreign_exception) {
|
||||
assert!(!foreign_exception, "we do not expect foreign exceptions");
|
||||
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,
|
||||
Err(_) => return uw::_URC_FAILURE,
|
||||
};
|
||||
let exception_info = &mut *(exception_object as *mut ExceptionInfo);
|
||||
let exception = &exception_info.exception.unwrap();
|
||||
if search_phase {
|
||||
match eh_action {
|
||||
EHAction::None => return continue_unwind(exception_object, context),
|
||||
// Actually, cleanup should not return handler found, this is to workaround
|
||||
// the issue of terminating directly when no catch cause is found while
|
||||
// having some cleanup routines defined by finally.
|
||||
// The best way to handle this is to force unwind the stack in the raise
|
||||
// function when end of stack is reached, and call terminate at the end of
|
||||
// the unwind. Unfortunately, there is no forced unwind function defined
|
||||
// for EHABI, and I have no idea how to implement that, so this is a hack.
|
||||
EHAction::Cleanup(_) => return uw::_URC_HANDLER_FOUND,
|
||||
EHAction::Catch(_) => {
|
||||
// EHABI requires the personality routine to update the
|
||||
// SP value in the barrier cache of the exception object.
|
||||
(*exception_object).private[5] =
|
||||
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
|
||||
return uw::_URC_HANDLER_FOUND;
|
||||
}
|
||||
EHAction::Terminate => return uw::_URC_FAILURE,
|
||||
}
|
||||
} else {
|
||||
match eh_action {
|
||||
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,
|
||||
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,
|
||||
}
|
||||
|
||||
// On ARM EHABI the personality routine is responsible for actually
|
||||
@ -166,10 +202,11 @@ pub unsafe fn artiq_personality(state: uw::_Unwind_State,
|
||||
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context)
|
||||
-> uw::_Unwind_Reason_Code {
|
||||
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
|
||||
let reason = __gnu_unwind_frame(exception_object, context);
|
||||
if reason == uw::_URC_NO_REASON {
|
||||
uw::_URC_CONTINUE_UNWIND
|
||||
} else {
|
||||
uw::_URC_FAILURE
|
||||
reason
|
||||
}
|
||||
}
|
||||
// defined in libgcc
|
||||
@ -180,84 +217,238 @@ 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) -> ! {
|
||||
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;
|
||||
|
||||
// Reraise is basically cxa_rethrow, which calls _Unwind_Resume_or_Rethrow,
|
||||
// which for EHABI would always call _Unwind_RaiseException.
|
||||
match INFLIGHT.exception {
|
||||
Some(ref exception) => raise(exception),
|
||||
None => raise(&Exception {
|
||||
name: "0:artiq.coredevice.exceptions.RuntimeError".as_c_slice(),
|
||||
file: file!().as_c_slice(),
|
||||
line: line!(),
|
||||
column: column!(),
|
||||
// https://github.com/rust-lang/rfcs/pull/1719
|
||||
function: "__artiq_reraise".as_c_slice(),
|
||||
message: "No active exception to reraise".as_c_slice(),
|
||||
param: [0, 0, 0]
|
||||
})
|
||||
let count = EXCEPTION_BUFFER.exception_count;
|
||||
let stack = &mut EXCEPTION_BUFFER.exception_stack;
|
||||
let diff = exception as isize - EXCEPTION_BUFFER.exceptions.as_ptr() as isize;
|
||||
if 0 <= diff && diff <= (mem::size_of::<Option<Exception>>() * MAX_INFLIGHT_EXCEPTIONS) as isize {
|
||||
let index = diff / (mem::size_of::<Option<Exception>>() as isize);
|
||||
trace!("reraise at {}", index);
|
||||
|
||||
let mut found = false;
|
||||
for i in 0..=MAX_INFLIGHT_EXCEPTIONS + 1 {
|
||||
if found {
|
||||
if stack[i] == -1 {
|
||||
stack[i - 1] = index;
|
||||
assert!(i == count);
|
||||
break;
|
||||
} else {
|
||||
stack[i - 1] = stack[i];
|
||||
}
|
||||
} else {
|
||||
if stack[i] == index {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(found);
|
||||
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[stack[count - 1] as usize],
|
||||
stop_fn, core::ptr::null_mut());
|
||||
} else {
|
||||
if count < MAX_INFLIGHT_EXCEPTIONS {
|
||||
trace!("raising exception at level {}", count);
|
||||
let exception = &*exception;
|
||||
for (i, slot) in EXCEPTION_BUFFER.exceptions.iter_mut().enumerate() {
|
||||
// we should always be able to find a slot
|
||||
if slot.is_none() {
|
||||
*slot = Some(
|
||||
*mem::transmute::<*const Exception, *const Exception<'static>>
|
||||
(exception));
|
||||
EXCEPTION_BUFFER.exception_stack[count] = i as isize;
|
||||
EXCEPTION_BUFFER.uw_exceptions[i].private =
|
||||
[0; uw::unwinder_private_data_size];
|
||||
EXCEPTION_BUFFER.stack_pointers[i] = StackPointerBacktrace {
|
||||
stack_pointer: 0,
|
||||
initial_backtrace_size: EXCEPTION_BUFFER.backtrace_size,
|
||||
current_backtrace_size: 0,
|
||||
};
|
||||
EXCEPTION_BUFFER.exception_count += 1;
|
||||
let _result = _Unwind_ForcedUnwind(&mut EXCEPTION_BUFFER.uw_exceptions[i],
|
||||
stop_fn, core::ptr::null_mut());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("too many nested exceptions");
|
||||
// TODO: better reporting?
|
||||
let exception = Exception {
|
||||
id: get_exception_id("RuntimeError"),
|
||||
file: file!().as_c_slice(),
|
||||
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_rules! artiq_raise {
|
||||
($name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => ({
|
||||
use cslice::AsCSlice;
|
||||
let name_id = $crate::eh_artiq::get_exception_id($name);
|
||||
let message_cl = $message.clone();
|
||||
let exn = $crate::eh_artiq::Exception {
|
||||
name: concat!("0:artiq.coredevice.exceptions.", $name).as_c_slice(),
|
||||
id: name_id,
|
||||
file: file!().as_c_slice(),
|
||||
line: line!(),
|
||||
column: column!(),
|
||||
// https://github.com/rust-lang/rfcs/pull/1719
|
||||
function: "(Rust function)".as_c_slice(),
|
||||
message: $message.as_c_slice(),
|
||||
message: message_cl.as_c_slice(),
|
||||
param: [$param0, $param1, $param2]
|
||||
};
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $crate::eh_artiq::raise(&exn) }
|
||||
unsafe {
|
||||
$crate::eh_artiq::raise(&exn)
|
||||
}
|
||||
});
|
||||
($name:expr, $message:expr) => ({
|
||||
artiq_raise!($name, $message, 0, 0, 0)
|
||||
|
90
src/runtime/src/i2c.rs
Normal file
90
src/runtime/src/i2c.rs
Normal file
@ -0,0 +1,90 @@
|
||||
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 libcortex_a9::{
|
||||
asm,
|
||||
regs::{MPIDR, SP},
|
||||
asm, interrupt_handler,
|
||||
regs::MPIDR,
|
||||
spin_lock_yield, notify_spin_lock
|
||||
};
|
||||
use libregister::{RegisterR, RegisterW};
|
||||
use libregister::RegisterR;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
extern "C" {
|
||||
@ -14,31 +14,34 @@ extern "C" {
|
||||
|
||||
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[link_section = ".text.boot"]
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
pub unsafe extern "C" fn IRQ() {
|
||||
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
|
||||
if MPIDR.read().cpu_id() == 1 {
|
||||
let mpcore = mpcore::RegisterBlock::new();
|
||||
let mut gic = gic::InterruptController::new(mpcore);
|
||||
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();
|
||||
asm!("b core1_restart");
|
||||
}
|
||||
}
|
||||
stdio::drop_uart();
|
||||
println!("IRQ");
|
||||
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() {
|
||||
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
|
||||
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) {
|
||||
|
@ -9,10 +9,11 @@ use alloc::vec;
|
||||
|
||||
use crate::eh_artiq;
|
||||
use crate::rtio;
|
||||
use crate::i2c;
|
||||
use super::rpc::{rpc_send, rpc_send_async, rpc_recv};
|
||||
use super::dma;
|
||||
use super::cache;
|
||||
|
||||
use super::core1::rtio_get_destination_status;
|
||||
|
||||
extern "C" {
|
||||
fn vsnprintf_(buffer: *mut c_char, count: size_t, format: *const c_char, va: VaList) -> c_int;
|
||||
@ -63,6 +64,15 @@ 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> {
|
||||
let api = &[
|
||||
// timing
|
||||
@ -77,7 +87,7 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
|
||||
// rtio
|
||||
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_output = rtio::output),
|
||||
api!(rtio_output_wide = rtio::output_wide),
|
||||
@ -100,6 +110,14 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
api!(cache_get = cache::get),
|
||||
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
|
||||
// RTABI chapter 4.1.2, Table 2
|
||||
api!(__aeabi_dadd),
|
||||
@ -181,13 +199,25 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
api!(__aeabi_memclr),
|
||||
|
||||
// libc
|
||||
api!(memcmp, extern { fn memcmp(a: *const u8, b: *mut u8, size: usize); }),
|
||||
api!(memcpy, extern { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; }),
|
||||
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
|
||||
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_raise = eh_artiq::raise),
|
||||
api!(__artiq_reraise = eh_artiq::reraise),
|
||||
api!(__artiq_resume = eh_artiq::resume),
|
||||
api!(__artiq_end_catch = eh_artiq::end_catch),
|
||||
|
||||
// Implementations for LLVM math intrinsics
|
||||
api!(__powidf2),
|
||||
|
||||
// libm
|
||||
api_libm_f64f64!(acos),
|
||||
@ -195,14 +225,11 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
api_libm_f64f64!(asin),
|
||||
api_libm_f64f64!(asinh),
|
||||
api_libm_f64f64!(atan),
|
||||
{
|
||||
extern fn atan2(y: f64, x: f64) -> f64 {
|
||||
libm::atan2(y, x)
|
||||
}
|
||||
api!(atan2 = atan2)
|
||||
},
|
||||
api_libm_f64f64f64!(atan2),
|
||||
api_libm_f64f64!(atanh),
|
||||
api_libm_f64f64!(cbrt),
|
||||
api_libm_f64f64!(ceil),
|
||||
api_libm_f64f64f64!(copysign),
|
||||
api_libm_f64f64!(cos),
|
||||
api_libm_f64f64!(cosh),
|
||||
api_libm_f64f64!(erf),
|
||||
@ -219,18 +246,10 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
}
|
||||
api!(fma = fma)
|
||||
},
|
||||
{
|
||||
extern fn fmod(x: f64, y: f64) -> f64 {
|
||||
libm::fmod(x, y)
|
||||
}
|
||||
api!(fmod = fmod)
|
||||
},
|
||||
{
|
||||
extern fn hypot(x: f64, y: f64) -> f64 {
|
||||
libm::hypot(x, y)
|
||||
}
|
||||
api!(hypot = hypot)
|
||||
},
|
||||
api_libm_f64f64f64!(fmax),
|
||||
api_libm_f64f64f64!(fmin),
|
||||
api_libm_f64f64f64!(fmod),
|
||||
api_libm_f64f64f64!(hypot),
|
||||
api_libm_f64f64!(j0),
|
||||
api_libm_f64f64!(j1),
|
||||
{
|
||||
@ -243,12 +262,8 @@ pub fn resolve(required: &[u8]) -> Option<u32> {
|
||||
api_libm_f64f64!(log),
|
||||
api_libm_f64f64!(log2),
|
||||
api_libm_f64f64!(log10),
|
||||
{
|
||||
extern fn pow(x: f64, y: f64) -> f64 {
|
||||
libm::pow(x, y)
|
||||
}
|
||||
api!(pow = pow)
|
||||
},
|
||||
api_libm_f64f64f64!(nextafter),
|
||||
api_libm_f64f64f64!(pow),
|
||||
api_libm_f64f64!(round),
|
||||
api_libm_f64f64!(sin),
|
||||
api_libm_f64f64!(sinh),
|
||||
|
@ -1,26 +1,30 @@
|
||||
use alloc::string::String;
|
||||
use alloc::{string::String, boxed::Box};
|
||||
use cslice::{CSlice, AsCSlice};
|
||||
use core::mem::{transmute, forget};
|
||||
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();
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::CacheGetRequest(key));
|
||||
let msg = KERNEL_CHANNEL_0TO1.lock().as_mut().unwrap().recv();
|
||||
if let Message::CacheGetReply(v) = msg {
|
||||
let slice = unsafe { transmute(v.as_c_slice()) };
|
||||
// we intentionally leak the memory here,
|
||||
// which does not matter as core1 would restart
|
||||
forget(v);
|
||||
slice
|
||||
} else {
|
||||
panic!("Expected CacheGetReply for CacheGetRequest");
|
||||
unsafe {
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CacheGetRequest(key));
|
||||
let msg = KERNEL_CHANNEL_0TO1.as_mut().unwrap().recv();
|
||||
if let Message::CacheGetReply(v) = msg {
|
||||
let leaked = Box::new(v.as_c_slice());
|
||||
let reference = transmute(leaked.as_ref());
|
||||
forget(leaked);
|
||||
forget(v);
|
||||
reference
|
||||
} else {
|
||||
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 value = list.as_ref().to_vec();
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::CachePutRequest(key, value));
|
||||
unsafe {
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::CachePutRequest(key, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use libcortex_a9::sync_channel::{Sender, Receiver};
|
||||
use libsupport_zynq::boot::Core1;
|
||||
|
||||
use super::{CHANNEL_0TO1, CHANNEL_1TO0, INIT_LOCK, Message};
|
||||
use super::{CHANNEL_0TO1, CHANNEL_1TO0, CHANNEL_SEM, INIT_LOCK, Message};
|
||||
use crate::irq::restart_core1;
|
||||
|
||||
use core::mem::{forget, replace};
|
||||
@ -12,6 +12,7 @@ pub struct Control {
|
||||
}
|
||||
|
||||
fn get_channels() -> (Sender<'static, Message>, Receiver<'static, Message>) {
|
||||
CHANNEL_SEM.wait();
|
||||
let mut core0_tx = None;
|
||||
while core0_tx.is_none() {
|
||||
core0_tx = CHANNEL_0TO1.lock().take();
|
||||
@ -40,7 +41,7 @@ impl Control {
|
||||
|
||||
pub fn restart(&mut self) {
|
||||
{
|
||||
INIT_LOCK.lock();
|
||||
let _lock = INIT_LOCK.lock();
|
||||
restart_core1();
|
||||
unsafe {
|
||||
self.tx.drop_elements();
|
||||
|
@ -13,13 +13,14 @@ use libcortex_a9::{
|
||||
};
|
||||
use libboard_zynq::{mpcore, gic};
|
||||
use libsupport_zynq::ram;
|
||||
use dyld::{self, Library};
|
||||
use crate::eh_artiq;
|
||||
use dyld::{self, Library, elf::EXIDX_Entry};
|
||||
use crate::{eh_artiq, get_async_errors, rtio};
|
||||
use super::{
|
||||
api::resolve,
|
||||
rpc::rpc_send_async,
|
||||
INIT_LOCK,
|
||||
CHANNEL_0TO1, CHANNEL_1TO0,
|
||||
CHANNEL_SEM,
|
||||
KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0,
|
||||
KERNEL_IMAGE,
|
||||
Message,
|
||||
@ -28,14 +29,10 @@ use super::{
|
||||
|
||||
// linker symbols
|
||||
extern "C" {
|
||||
#[no_mangle]
|
||||
static __text_start: u32;
|
||||
#[no_mangle]
|
||||
static __text_end: u32;
|
||||
#[no_mangle]
|
||||
static __exidx_start: u32;
|
||||
#[no_mangle]
|
||||
static __exidx_end: u32;
|
||||
static __exidx_start: EXIDX_Entry;
|
||||
static __exidx_end: EXIDX_Entry;
|
||||
}
|
||||
|
||||
unsafe fn attribute_writeback(typeinfo: *const ()) {
|
||||
@ -121,7 +118,7 @@ impl KernelImage {
|
||||
dsb();
|
||||
isb();
|
||||
|
||||
(mem::transmute::<u32, fn()>(self.__modinit__))();
|
||||
(mem::transmute::<u32, extern "C" fn()>(self.__modinit__))();
|
||||
|
||||
if let Some(typeinfo) = self.typeinfo {
|
||||
attribute_writeback(typeinfo as *const ());
|
||||
@ -136,14 +133,12 @@ impl KernelImage {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main_core1() {
|
||||
pub extern "C" fn main_core1() {
|
||||
enable_fpu();
|
||||
debug!("Core1 started");
|
||||
|
||||
enable_fpu();
|
||||
debug!("FPU enabled on Core1");
|
||||
|
||||
ram::init_alloc_core1();
|
||||
gic::InterruptController::new(mpcore::RegisterBlock::new()).enable_interrupts();
|
||||
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
|
||||
|
||||
let (mut core0_tx, mut core1_rx) = sync_channel!(Message, 4);
|
||||
let (mut core1_tx, core0_rx) = sync_channel!(Message, 4);
|
||||
@ -151,10 +146,17 @@ pub fn main_core1() {
|
||||
INIT_LOCK.lock();
|
||||
core0_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();
|
||||
}
|
||||
*CHANNEL_0TO1.lock() = Some(core0_tx);
|
||||
*CHANNEL_1TO0.lock() = Some(core0_rx);
|
||||
CHANNEL_SEM.signal();
|
||||
|
||||
// set on load, cleared on start
|
||||
let mut loaded_kernel = None;
|
||||
@ -179,18 +181,20 @@ pub fn main_core1() {
|
||||
Message::StartRequest => {
|
||||
info!("kernel starting");
|
||||
if let Some(kernel) = loaded_kernel.take() {
|
||||
*KERNEL_CHANNEL_0TO1.lock() = Some(core1_rx);
|
||||
*KERNEL_CHANNEL_1TO0.lock() = Some(core1_tx);
|
||||
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.exec();
|
||||
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");
|
||||
core1_tx.send(Message::KernelFinished);
|
||||
let async_errors = unsafe { get_async_errors() };
|
||||
core1_tx.send(Message::KernelFinished(async_errors));
|
||||
}
|
||||
_ => error!("Core1 received unexpected message: {:?}", message),
|
||||
}
|
||||
@ -198,23 +202,13 @@ pub fn main_core1() {
|
||||
}
|
||||
|
||||
/// Called by eh_artiq
|
||||
pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'static mut [usize]) -> ! {
|
||||
let load_addr = unsafe {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(exceptions: &'static [Option<eh_artiq::Exception<'static>>],
|
||||
stack_pointers: &'static [eh_artiq::StackPointerBacktrace],
|
||||
backtrace: &'static mut [(usize, usize)]) -> ! {
|
||||
{
|
||||
let mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||
core1_tx.as_mut().unwrap().send(Message::KernelException(exception, &backtrace[..cursor]));
|
||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
||||
let errors = unsafe { get_async_errors() };
|
||||
core1_tx.send(Message::KernelException(exceptions, stack_pointers, backtrace, errors));
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
@ -222,23 +216,41 @@ pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'
|
||||
/// Called by llvm_libunwind
|
||||
#[no_mangle]
|
||||
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 start: *const u32;
|
||||
let start: *const EXIDX_Entry;
|
||||
unsafe {
|
||||
if &__text_start as *const u32 <= pc && pc < &__text_end as *const u32 {
|
||||
length = (&__exidx_end as *const u32).offset_from(&__exidx_start) as u32;
|
||||
length = (&__exidx_end as *const EXIDX_Entry).offset_from(&__exidx_start) as u32;
|
||||
start = &__exidx_start;
|
||||
} else {
|
||||
} else if KERNEL_IMAGE != ptr::null() {
|
||||
let exidx = KERNEL_IMAGE.as_ref()
|
||||
.expect("dl_unwind_find_exidx kernel image")
|
||||
.library.get().as_ref().unwrap().exidx();
|
||||
length = exidx.len() as u32;
|
||||
start = exidx.as_ptr();
|
||||
} else {
|
||||
length = 0;
|
||||
start = ptr::null();
|
||||
}
|
||||
*len_ptr = length;
|
||||
}
|
||||
start
|
||||
start as *const u32
|
||||
}
|
||||
|
||||
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,7 +7,6 @@ use alloc::{vec::Vec, string::String, boxed::Box};
|
||||
use cslice::CSlice;
|
||||
use super::{KERNEL_IMAGE, KERNEL_CHANNEL_0TO1, KERNEL_CHANNEL_1TO0, Message};
|
||||
use core::mem;
|
||||
use log::debug;
|
||||
|
||||
use libcortex_a9::cache::dcci_slice;
|
||||
|
||||
@ -36,7 +35,9 @@ pub unsafe fn init_dma_recorder() {
|
||||
|
||||
pub extern fn dma_record_start(name: CSlice<u8>) {
|
||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaEraseRequest(name.clone()));
|
||||
unsafe {
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaEraseRequest(name.clone()));
|
||||
}
|
||||
unsafe {
|
||||
if RECORDER.is_some() {
|
||||
artiq_raise!("DMAError", "DMA is already recording")
|
||||
@ -70,7 +71,7 @@ pub extern fn dma_record_stop(duration: i64) {
|
||||
|
||||
let mut recorder = RECORDER.take().unwrap();
|
||||
recorder.duration = duration;
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(
|
||||
Message::DmaPutRequest(recorder)
|
||||
);
|
||||
}
|
||||
@ -135,13 +136,17 @@ pub extern fn dma_record_output_wide(target: i32, words: CSlice<i32>) {
|
||||
|
||||
pub extern fn dma_erase(name: CSlice<u8>) {
|
||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaEraseRequest(name));
|
||||
unsafe {
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaEraseRequest(name));
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||
let name = String::from_utf8(name.as_ref().to_vec()).unwrap();
|
||||
KERNEL_CHANNEL_1TO0.lock().as_mut().unwrap().send(Message::DmaGetRequest(name));
|
||||
match KERNEL_CHANNEL_0TO1.lock().as_mut().unwrap().recv() {
|
||||
unsafe {
|
||||
KERNEL_CHANNEL_1TO0.as_mut().unwrap().send(Message::DmaGetRequest(name));
|
||||
}
|
||||
match unsafe {KERNEL_CHANNEL_0TO1.as_mut().unwrap()}.recv() {
|
||||
Message::DmaGetReply(None) => (),
|
||||
Message::DmaGetReply(Some((mut v, duration))) => {
|
||||
v.reserve(ALIGNMENT - 1);
|
||||
@ -154,6 +159,7 @@ pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||
// trailing zero to indicate end of buffer
|
||||
v.push(0);
|
||||
v.copy_within(0..original_length, padding);
|
||||
dcci_slice(&v);
|
||||
let v = Box::new(v);
|
||||
let address = Box::into_raw(v) as *mut Vec<u8> as i32;
|
||||
return DmaTrace {
|
||||
@ -168,12 +174,10 @@ pub extern fn dma_retrieve(name: CSlice<u8>) -> DmaTrace {
|
||||
}
|
||||
|
||||
pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||
debug!("DMA playback started");
|
||||
unsafe {
|
||||
let v = Box::from_raw(ptr as *mut Vec<u8>);
|
||||
let padding = ALIGNMENT - v.as_ptr() as usize % ALIGNMENT;
|
||||
let padding = if padding == ALIGNMENT { 0 } else { padding };
|
||||
dcci_slice(&v[padding..]);
|
||||
let ptr = v.as_ptr().add(padding) as i32;
|
||||
|
||||
csr::rtio_dma::base_address_write(ptr as u32);
|
||||
@ -184,8 +188,8 @@ pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||
while csr::rtio_dma::enable_read() != 0 {}
|
||||
csr::cri_con::selected_write(0);
|
||||
|
||||
// leave the handle as we may try to do playback for another time.
|
||||
mem::forget(v);
|
||||
debug!("DMA playback finished");
|
||||
|
||||
let error = csr::rtio_dma::error_read();
|
||||
if error != 0 {
|
||||
@ -194,13 +198,13 @@ pub extern fn dma_playback(timestamp: i64, ptr: i32) {
|
||||
csr::rtio_dma::error_write(1);
|
||||
if error & 1 != 0 {
|
||||
artiq_raise!("RTIOUnderflow",
|
||||
"RTIO underflow at {0} mu, channel {1}",
|
||||
timestamp as i64, channel as i64, 0);
|
||||
"RTIO underflow at {1} mu, channel {rtio_channel_info:0}",
|
||||
channel as i64, timestamp as i64, 0);
|
||||
}
|
||||
if error & 2 != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, output, at {0} mu, channel {1}",
|
||||
timestamp as i64, channel as i64, 0);
|
||||
"RTIO destination unreachable, output, at {1} mu, channel {rtio_channel_info:0}",
|
||||
channel as i64, timestamp as i64, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::ptr;
|
||||
use alloc::{vec::Vec, string::String};
|
||||
|
||||
use libcortex_a9::{mutex::Mutex, sync_channel};
|
||||
use libcortex_a9::{mutex::Mutex, sync_channel, semaphore::Semaphore};
|
||||
use crate::eh_artiq;
|
||||
|
||||
mod control;
|
||||
@ -15,13 +15,13 @@ mod cache;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RPCException {
|
||||
pub name: String,
|
||||
pub message: String,
|
||||
pub id: u32,
|
||||
pub message: u32,
|
||||
pub param: [i64; 3],
|
||||
pub file: String,
|
||||
pub file: u32,
|
||||
pub line: i32,
|
||||
pub column: i32,
|
||||
pub function: String
|
||||
pub function: u32
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -30,8 +30,11 @@ pub enum Message {
|
||||
LoadCompleted,
|
||||
LoadFailed,
|
||||
StartRequest,
|
||||
KernelFinished,
|
||||
KernelException(&'static eh_artiq::Exception<'static>, &'static [usize]),
|
||||
KernelFinished(u8),
|
||||
KernelException(&'static [Option<eh_artiq::Exception<'static>>],
|
||||
&'static [eh_artiq::StackPointerBacktrace],
|
||||
&'static [(usize, usize)],
|
||||
u8),
|
||||
RpcSend { is_async: bool, data: Vec<u8> },
|
||||
RpcRecvRequest(*mut ()),
|
||||
RpcRecvReply(Result<usize, RPCException>),
|
||||
@ -44,15 +47,21 @@ pub enum Message {
|
||||
DmaEraseRequest(String),
|
||||
DmaGetRequest(String),
|
||||
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_1TO0: Mutex<Option<sync_channel::Receiver<'static, Message>>> = Mutex::new(None);
|
||||
static CHANNEL_SEM: Semaphore = Semaphore::new(0, 1);
|
||||
|
||||
static KERNEL_CHANNEL_0TO1: Mutex<Option<sync_channel::Receiver<'static, Message>>> = Mutex::new(None);
|
||||
static KERNEL_CHANNEL_1TO0: Mutex<Option<sync_channel::Sender<'static, Message>>> = Mutex::new(None);
|
||||
static mut KERNEL_CHANNEL_0TO1: Option<sync_channel::Receiver<'static, Message>> = None;
|
||||
static mut KERNEL_CHANNEL_1TO0: Option<sync_channel::Sender<'static, Message>> = None;
|
||||
|
||||
static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
|
||||
pub static mut KERNEL_IMAGE: *const core1::KernelImage = ptr::null();
|
||||
|
||||
static INIT_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Kernel-side RPC API
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use cslice::{CSlice, AsCSlice};
|
||||
use cslice::CSlice;
|
||||
|
||||
use crate::eh_artiq;
|
||||
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 ()) {
|
||||
let mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
send_args(&mut buffer, service, tag.as_ref(), data).expect("RPC encoding failed");
|
||||
core1_tx.as_mut().unwrap().send(Message::RpcSend { is_async, data: buffer });
|
||||
core1_tx.send(Message::RpcSend { is_async, data: buffer });
|
||||
}
|
||||
|
||||
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 {
|
||||
let reply = {
|
||||
let mut core1_rx = KERNEL_CHANNEL_0TO1.lock();
|
||||
let mut core1_tx = KERNEL_CHANNEL_1TO0.lock();
|
||||
core1_tx.as_mut().unwrap().send(Message::RpcRecvRequest(slot));
|
||||
core1_rx.as_mut().unwrap().recv()
|
||||
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::RpcRecvRequest(slot));
|
||||
core1_rx.recv()
|
||||
};
|
||||
match reply {
|
||||
Message::RpcRecvReply(Ok(alloc_size)) => alloc_size,
|
||||
Message::RpcRecvReply(Err(exception)) => unsafe {
|
||||
eh_artiq::raise(&eh_artiq::Exception {
|
||||
name: exception.name.as_bytes().as_c_slice(),
|
||||
file: exception.file.as_bytes().as_c_slice(),
|
||||
id: exception.id,
|
||||
file: CSlice::new(exception.file as *const u8, usize::MAX),
|
||||
line: exception.line as u32,
|
||||
column: exception.column as u32,
|
||||
function: exception.function.as_bytes().as_c_slice(),
|
||||
message: exception.message.as_bytes().as_c_slice(),
|
||||
function: CSlice::new(exception.function as *const u8, usize::MAX),
|
||||
message: CSlice::new(exception.message as *const u8, usize::MAX),
|
||||
param: exception.param
|
||||
})
|
||||
},
|
||||
|
@ -1,171 +0,0 @@
|
||||
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,143 +5,54 @@
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(const_btree_new)]
|
||||
#![feature(ptr_offset_from)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(asm)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use core::{cmp, str};
|
||||
use log::{info, warn, error};
|
||||
|
||||
use libboard_zynq::{timer::GlobalTimer, devc, slcr, mpcore, gic};
|
||||
use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
|
||||
use libasync::{task, block_async};
|
||||
use libsupport_zynq::ram;
|
||||
use libregister::RegisterW;
|
||||
use nb;
|
||||
use void::Void;
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
use libconfig::Config;
|
||||
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 comms;
|
||||
mod rpc;
|
||||
#[path = "../../../build/pl.rs"]
|
||||
mod pl;
|
||||
#[cfg(ki_impl = "csr")]
|
||||
#[path = "rtio_csr.rs"]
|
||||
mod rtio;
|
||||
#[cfg(ki_impl = "acp")]
|
||||
#[path = "rtio_acp.rs"]
|
||||
mod rtio;
|
||||
mod rtio_mgt;
|
||||
mod rtio_clocking;
|
||||
mod kernel;
|
||||
mod moninj;
|
||||
mod load_pl;
|
||||
mod eh_artiq;
|
||||
mod panic;
|
||||
mod logger;
|
||||
mod mgmt;
|
||||
mod analyzer;
|
||||
mod irq;
|
||||
mod i2c;
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
static mut SEEN_ASYNC_ERRORS: u8 = 0;
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
pub unsafe fn get_async_errors() -> u8 {
|
||||
let errors = SEEN_ASYNC_ERRORS;
|
||||
SEEN_ASYNC_ERRORS = 0;
|
||||
errors
|
||||
}
|
||||
|
||||
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
||||
@ -159,54 +70,61 @@ async fn report_async_rtio_errors() {
|
||||
let _ = block_async!(wait_for_async_rtio_error()).await;
|
||||
unsafe {
|
||||
let errors = pl::csr::rtio_core::async_error_read();
|
||||
if errors & 1 != 0 {
|
||||
error!("RTIO collision involving channel {}",
|
||||
pl::csr::rtio_core::collision_channel_read());
|
||||
if errors & ASYNC_ERROR_COLLISION != 0 {
|
||||
let channel = pl::csr::rtio_core::collision_channel_read();
|
||||
error!("RTIO collision involving channel 0x{:04x}:{}", channel, rtio_mgt::resolve_channel_name(channel as u32));
|
||||
}
|
||||
if errors & 2 != 0 {
|
||||
error!("RTIO busy error involving channel {}",
|
||||
pl::csr::rtio_core::busy_channel_read());
|
||||
if errors & ASYNC_ERROR_BUSY != 0 {
|
||||
let channel = pl::csr::rtio_core::busy_channel_read();
|
||||
error!("RTIO busy error involving channel 0x{:04x}:{}", channel, rtio_mgt::resolve_channel_name(channel as u32));
|
||||
}
|
||||
if errors & 4 != 0 {
|
||||
error!("RTIO sequence error involving channel {}",
|
||||
pl::csr::rtio_core::sequence_error_channel_read());
|
||||
if errors & ASYNC_ERROR_SEQUENCE_ERROR != 0 {
|
||||
let channel = pl::csr::rtio_core::sequence_error_channel_read();
|
||||
error!("RTIO sequence error involving channel 0x{:04x}:{}", channel, rtio_mgt::resolve_channel_name(channel as u32));
|
||||
}
|
||||
SEEN_ASYNC_ERRORS = errors;
|
||||
pl::csr::rtio_core::async_error_write(errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main_core0() {
|
||||
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::Debug);
|
||||
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
|
||||
buffer_logger.register();
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
|
||||
info!("NAR3/Zynq7000 starting...");
|
||||
|
||||
ram::init_alloc_core0();
|
||||
gic::InterruptController::new(mpcore::RegisterBlock::new()).enable_interrupts();
|
||||
gic::InterruptController::gic(mpcore::RegisterBlock::mpcore()).enable_interrupts();
|
||||
|
||||
init_gateware();
|
||||
info!("detected gateware: {}", identifier_read(&mut [0; 64]));
|
||||
info!("gateware ident: {}", identifier_read(&mut [0; 64]));
|
||||
|
||||
let cfg = match config::Config::new() {
|
||||
i2c::init();
|
||||
|
||||
let cfg = match Config::new() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
warn!("config initialization failed: {}", err);
|
||||
config::Config::new_dummy()
|
||||
Config::new_dummy()
|
||||
}
|
||||
};
|
||||
|
||||
init_rtio(&mut timer, &cfg);
|
||||
rtio_clocking::init(&mut timer, &cfg);
|
||||
|
||||
task::spawn(report_async_rtio_errors());
|
||||
|
||||
comms::main(timer, &cfg);
|
||||
comms::main(timer, cfg);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use futures::{future::poll_fn, task::Poll};
|
||||
use libasync::{smoltcp::TcpStream, task};
|
||||
use libboard_zynq::smoltcp;
|
||||
use libboard_zynq::{smoltcp, slcr};
|
||||
use libconfig::Config;
|
||||
use core::cell::RefCell;
|
||||
use alloc::rc::Rc;
|
||||
use log::{self, info, warn, LevelFilter};
|
||||
use alloc::{rc::Rc, vec::Vec, string::String};
|
||||
use log::{self, info, debug, warn, error, LevelFilter};
|
||||
|
||||
use crate::logger::{BufferLogger, LogBufferRef};
|
||||
use libboard_artiq::logger::{BufferLogger, LogBufferRef};
|
||||
use crate::proto_async::*;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
@ -43,13 +44,21 @@ pub enum Request {
|
||||
ClearLog = 2,
|
||||
PullLog = 7,
|
||||
SetLogFilter = 3,
|
||||
Reboot = 5,
|
||||
SetUartLogFilter = 6,
|
||||
|
||||
ConfigRead = 12,
|
||||
ConfigWrite = 13,
|
||||
ConfigRemove = 14,
|
||||
}
|
||||
|
||||
#[repr(i8)]
|
||||
pub enum Reply {
|
||||
Success = 1,
|
||||
LogContent = 2,
|
||||
RebootImminent = 3,
|
||||
Error = 6,
|
||||
ConfigData = 7,
|
||||
}
|
||||
|
||||
async fn read_log_level_filter(stream: &mut TcpStream) -> Result<log::LevelFilter> {
|
||||
@ -85,14 +94,36 @@ async fn get_logger_buffer() -> LogBufferRef<'static> {
|
||||
get_logger_buffer_pred(|_| true).await
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>) -> Result<()> {
|
||||
async fn read_key(stream: &mut TcpStream) -> Result<String> {
|
||||
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? {
|
||||
return Err(Error::UnexpectedPattern);
|
||||
}
|
||||
stream.send_slice("e".as_bytes()).await?;
|
||||
|
||||
loop {
|
||||
let msg = read_i8(stream).await;
|
||||
if let Err(smoltcp::Error::Illegal) = msg {
|
||||
if let Err(smoltcp::Error::Finished) = msg {
|
||||
return Ok(());
|
||||
}
|
||||
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
|
||||
@ -150,19 +181,76 @@ async fn handle_connection(stream: &mut TcpStream, pull_id: Rc<RefCell<u32>>) ->
|
||||
}
|
||||
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() {
|
||||
pub fn start(cfg: Config) {
|
||||
task::spawn(async move {
|
||||
let pull_id = Rc::new(RefCell::new(0u32));
|
||||
let cfg = Rc::new(cfg);
|
||||
loop {
|
||||
let mut stream = TcpStream::accept(1380, 2048, 2048).await.unwrap();
|
||||
let pull_id = pull_id.clone();
|
||||
let cfg = cfg.clone();
|
||||
task::spawn(async move {
|
||||
info!("received connection");
|
||||
let _ = handle_connection(&mut stream, pull_id)
|
||||
let _ = handle_connection(&mut stream, pull_id, cfg)
|
||||
.await
|
||||
.map_err(|e| warn!("connection terminated: {:?}", e));
|
||||
let _ = stream.flush().await;
|
||||
|
@ -1,25 +1,26 @@
|
||||
use core::fmt;
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::{fmt, cell::RefCell};
|
||||
use alloc::{collections::BTreeMap, rc::Rc};
|
||||
use log::{debug, info, warn};
|
||||
use void::Void;
|
||||
|
||||
use libboard_artiq::drtio_routing;
|
||||
|
||||
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
|
||||
use libasync::{task, smoltcp::TcpStream, block_async, nb};
|
||||
use libcortex_a9::mutex::Mutex;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use futures::{pin_mut, select_biased, FutureExt};
|
||||
|
||||
use crate::proto_async::*;
|
||||
use crate::pl::csr;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
NetworkError(smoltcp::Error),
|
||||
UnexpectedPattern,
|
||||
UnrecognizedPacket,
|
||||
|
||||
UnrecognizedPacket
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
@ -54,52 +55,131 @@ enum DeviceMessage {
|
||||
InjectionStatus = 1
|
||||
}
|
||||
|
||||
fn read_probe(channel: i32, probe: i8) -> i32 {
|
||||
unsafe {
|
||||
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 i32
|
||||
#[cfg(has_drtio)]
|
||||
mod remote_moninj {
|
||||
use super::*;
|
||||
use libboard_artiq::drtioaux_async;
|
||||
use crate::rtio_mgt::drtio;
|
||||
use log::error;
|
||||
|
||||
pub async fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i64 {
|
||||
let reply = drtio::aux_transact(aux_mutex, linkno, &drtioaux_async::Packet::MonitorRequest {
|
||||
destination: destination,
|
||||
channel: channel as _,
|
||||
probe: probe as _},
|
||||
timer).await;
|
||||
match reply {
|
||||
Ok(drtioaux_async::Packet::MonitorReply { value }) => return value as i64,
|
||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||
Err("link went down") => { debug!("link is down"); },
|
||||
Err(e) => error!("aux packet error ({})", e)
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub async fn inject(aux_mutex: &Rc<Mutex<bool>>, _timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8, value: i8) {
|
||||
let _lock = aux_mutex.lock();
|
||||
drtioaux_async::send(linkno, &drtioaux_async::Packet::InjectionRequest {
|
||||
destination: destination,
|
||||
channel: channel as _,
|
||||
overrd: overrd as _,
|
||||
value: value as _
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
pub async fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
|
||||
let reply = drtio::aux_transact(aux_mutex,
|
||||
linkno,
|
||||
&drtioaux_async::Packet::InjectionStatusRequest {
|
||||
destination: destination,
|
||||
channel: channel as _,
|
||||
overrd: overrd as _},
|
||||
timer).await;
|
||||
match reply {
|
||||
Ok(drtioaux_async::Packet::InjectionStatusReply { value }) => return value as i8,
|
||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||
Err("link went down") => { debug!("link is down"); },
|
||||
Err(e) => error!("aux packet error ({})", e)
|
||||
}
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
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 _);
|
||||
mod local_moninj {
|
||||
use libboard_artiq::pl::csr;
|
||||
|
||||
pub fn read_probe(channel: i32, probe: i8) -> i64 {
|
||||
unsafe {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
macro_rules! dispatch {
|
||||
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let destination = ($channel >> 16) as u8;
|
||||
let channel = $channel;
|
||||
let hop = $routing_table.0[destination as usize][0];
|
||||
if hop == 0 {
|
||||
local_moninj::$func(channel.into(), $($param, )*)
|
||||
} else {
|
||||
let linkno = hop - 1 as u8;
|
||||
remote_moninj::$func($aux_mutex, $timer, linkno, destination, channel, $($param, )*).await
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()> {
|
||||
#[cfg(not(has_drtio))]
|
||||
macro_rules! dispatch {
|
||||
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||
let channel = $channel as u16;
|
||||
local_moninj::$func(channel.into(), $($param, )*)
|
||||
}}
|
||||
}
|
||||
|
||||
async fn handle_connection(stream: &TcpStream, timer: GlobalTimer,
|
||||
_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &drtio_routing::RoutingTable) -> Result<()> {
|
||||
if !expect(&stream, b"ARTIQ moninj\n").await? {
|
||||
return Err(Error::UnexpectedPattern);
|
||||
}
|
||||
|
||||
let mut probe_watch_list: BTreeMap<(i32, i8), Option<i32>> = BTreeMap::new();
|
||||
let mut probe_watch_list: BTreeMap<(i32, i8), Option<i64>> = BTreeMap::new();
|
||||
let mut inject_watch_list: BTreeMap<(i32, i8), Option<i8>> = BTreeMap::new();
|
||||
let mut next_check = Milliseconds(0);
|
||||
let mut next_check = timer.get_time();
|
||||
let timeout = |next_check: Milliseconds| -> nb::Result<(), Void> {
|
||||
if timer.get_time() < next_check {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
loop {
|
||||
// TODO: we don't need fuse() here.
|
||||
// remove after https://github.com/rust-lang/futures-rs/issues/1989 lands
|
||||
let read_message_f = read_i8(&stream).fuse();
|
||||
let next_check_c = next_check.clone();
|
||||
let timeout = || -> nb::Result<(), Void> {
|
||||
if timer.get_time() < next_check_c {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
let timeout_f = block_async!(timeout()).fuse();
|
||||
|
||||
let timeout_f = block_async!(timeout(next_check_c)).fuse();
|
||||
pin_mut!(read_message_f, timeout_f);
|
||||
select_biased! {
|
||||
message = read_message_f => {
|
||||
@ -134,13 +214,13 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||
let channel = read_i32(&stream).await?;
|
||||
let overrd = read_i8(&stream).await?;
|
||||
let value = read_i8(&stream).await?;
|
||||
inject(channel, overrd, value);
|
||||
dispatch!(timer, _aux_mutex, _routing_table, channel, inject, overrd, value);
|
||||
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
|
||||
},
|
||||
HostMessage::GetInjectionStatus => {
|
||||
let channel = read_i32(&stream).await?;
|
||||
let overrd = read_i8(&stream).await?;
|
||||
let value = read_injection_status(channel, overrd);
|
||||
let value = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||
write_i32(&stream, channel).await?;
|
||||
write_i8(&stream, overrd).await?;
|
||||
@ -150,17 +230,17 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||
},
|
||||
_ = timeout_f => {
|
||||
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
||||
let current = read_probe(channel, probe);
|
||||
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_probe, probe);
|
||||
if previous.is_none() || previous.unwrap() != current {
|
||||
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
|
||||
write_i32(&stream, channel).await?;
|
||||
write_i8(&stream, probe).await?;
|
||||
write_i32(&stream, current).await?;
|
||||
write_i64(&stream, current).await?;
|
||||
*previous = Some(current);
|
||||
}
|
||||
}
|
||||
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
|
||||
let current = read_injection_status(channel, overrd);
|
||||
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_injection_status, overrd);
|
||||
if previous.is_none() || previous.unwrap() != current {
|
||||
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||
write_i32(&stream, channel).await?;
|
||||
@ -169,21 +249,24 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||
*previous = Some(current);
|
||||
}
|
||||
}
|
||||
next_check = next_check + Milliseconds(200);
|
||||
next_check = timer.get_time() + Milliseconds(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(timer: GlobalTimer) {
|
||||
pub fn start(timer: GlobalTimer, aux_mutex: Rc<Mutex<bool>>, routing_table: Rc<RefCell<drtio_routing::RoutingTable>>) {
|
||||
task::spawn(async move {
|
||||
loop {
|
||||
let aux_mutex = aux_mutex.clone();
|
||||
let routing_table = routing_table.clone();
|
||||
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
|
||||
task::spawn(async move {
|
||||
info!("received connection");
|
||||
let result = handle_connection(&stream, timer).await;
|
||||
let routing_table = routing_table.borrow();
|
||||
let result = handle_connection(&stream, timer, &aux_mutex, &routing_table).await;
|
||||
match result {
|
||||
Err(Error::NetworkError(smoltcp::Error::Illegal)) => info!("peer closed connection"),
|
||||
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"),
|
||||
Err(error) => warn!("connection terminated: {}", error),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
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,20 +3,21 @@ use libregister::RegisterR;
|
||||
use libcortex_a9::regs::MPIDR;
|
||||
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 SOFT_PANICKED: bool = false;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(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 ");
|
||||
let soft_panicked = unsafe { SOFT_PANICKED };
|
||||
print!("Core {} panic at ", id);
|
||||
if let Some(location) = info.location() {
|
||||
print!("{}:{}:{}", location.file(), location.line(), location.column());
|
||||
} else {
|
||||
@ -27,6 +28,20 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
} else {
|
||||
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: ");
|
||||
let _ = backtrace(|ip| {
|
||||
// Backtrace gives us the return address, i.e. the address after the delay slot,
|
||||
@ -34,6 +49,28 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
print!("{:#08x} ", ip - 2 * 4);
|
||||
});
|
||||
println!("\nEnd backtrace");
|
||||
|
||||
if !soft_panicked && id == 0 {
|
||||
soft_panic(info);
|
||||
}
|
||||
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,55 +52,15 @@ pub async fn read_i8(stream: &TcpStream) -> Result<i8> {
|
||||
}
|
||||
|
||||
pub async fn read_i32(stream: &TcpStream) -> Result<i32> {
|
||||
let mut state = RecvState::NeedsMore(0, 0);
|
||||
loop {
|
||||
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);
|
||||
}
|
||||
}
|
||||
let mut buffer: [u8; 4] = [0; 4];
|
||||
read_chunk(stream, &mut buffer).await?;
|
||||
Ok(i32::from_le_bytes(buffer))
|
||||
}
|
||||
|
||||
pub async fn read_i64(stream: &TcpStream) -> Result<i64> {
|
||||
let mut state = RecvState::NeedsMore(0, 0);
|
||||
loop {
|
||||
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);
|
||||
}
|
||||
}
|
||||
let mut buffer: [u8; 8] = [0; 8];
|
||||
read_chunk(stream, &mut buffer).await?;
|
||||
Ok(i64::from_le_bytes(buffer))
|
||||
}
|
||||
|
||||
pub async fn read_chunk(stream: &TcpStream, destination: &mut [u8]) -> Result<()> {
|
||||
@ -120,39 +80,27 @@ pub async fn read_chunk(stream: &TcpStream, destination: &mut [u8]) -> Result<()
|
||||
}
|
||||
|
||||
pub async fn write_i8(stream: &TcpStream, value: i8) -> Result<()> {
|
||||
stream.send([value as u8].iter().copied()).await?;
|
||||
stream.send_slice(&[value as u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_bool(stream: &TcpStream, value: bool) -> Result<()> {
|
||||
stream.send([value as u8].iter().copied()).await?;
|
||||
stream.send_slice(&[value as u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_i32(stream: &TcpStream, value: i32) -> Result<()> {
|
||||
stream.send([
|
||||
(value >> 24) as u8,
|
||||
(value >> 16) as u8,
|
||||
(value >> 8) as u8,
|
||||
value as u8].iter().copied()).await?;
|
||||
stream.send_slice(&value.to_le_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_i64(stream: &TcpStream, value: i64) -> Result<()> {
|
||||
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?;
|
||||
stream.send_slice(&value.to_le_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_chunk(stream: &TcpStream, value: &[u8]) -> Result<()> {
|
||||
write_i32(stream, value.len() as i32).await?;
|
||||
stream.send(value.iter().copied()).await?;
|
||||
stream.send_slice(value).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use core::str;
|
||||
use core::future::Future;
|
||||
use cslice::{CSlice, CMutSlice};
|
||||
use log::trace;
|
||||
use byteorder::{NativeEndian, ByteOrder};
|
||||
|
||||
use core_io::{Write, Error};
|
||||
use libboard_zynq::smoltcp;
|
||||
@ -9,22 +10,90 @@ use libasync::smoltcp::TcpStream;
|
||||
use alloc::boxed::Box;
|
||||
use async_recursion::async_recursion;
|
||||
|
||||
use crate::proto_core_io::ProtoWrite;
|
||||
use io::proto::ProtoWrite;
|
||||
use crate::proto_async;
|
||||
use self::tag::{Tag, TagIterator, split_tag};
|
||||
|
||||
#[inline]
|
||||
fn round_up(val: usize, power_of_two: usize) -> usize {
|
||||
assert!(power_of_two.is_power_of_two());
|
||||
let max_rem = power_of_two - 1;
|
||||
(val + max_rem) & (!max_rem)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn round_up_mut<T>(ptr: *mut T, power_of_two: usize) -> *mut T {
|
||||
round_up(ptr as usize, power_of_two) as *mut T
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn round_up_const<T>(ptr: *const T, power_of_two: usize) -> *const T {
|
||||
round_up(ptr as usize, power_of_two) as *const T
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn align_ptr<T>(ptr: *const ()) -> *const T {
|
||||
let alignment = core::mem::align_of::<T>() as isize;
|
||||
let fix = (alignment - (ptr as isize) % alignment) % alignment;
|
||||
((ptr as isize) + fix) as *const T
|
||||
round_up_const(ptr, core::mem::align_of::<T>()) as *const T
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn align_ptr_mut<T>(ptr: *mut ()) -> *mut T {
|
||||
let alignment = core::mem::align_of::<T>() as isize;
|
||||
let fix = (alignment - (ptr as isize) % alignment) % alignment;
|
||||
((ptr as isize) + fix) as *mut T
|
||||
round_up_mut(ptr, core::mem::align_of::<T>()) as *mut T
|
||||
}
|
||||
|
||||
/// Reads (deserializes) `length` array or list elements of type `tag` from `stream`,
|
||||
/// writing them into the buffer given by `storage`.
|
||||
///
|
||||
/// `alloc` is used for nested allocations (if elements themselves contain
|
||||
/// lists/arrays), see [recv_value].
|
||||
#[async_recursion(?Send)]
|
||||
async unsafe fn recv_elements<F>(
|
||||
stream: &TcpStream,
|
||||
elt_tag: Tag<'async_recursion>,
|
||||
length: usize,
|
||||
storage: *mut (),
|
||||
alloc: &(impl Fn(usize) -> F + 'async_recursion)
|
||||
) -> Result<(), smoltcp::Error>
|
||||
where
|
||||
F: Future<Output=*mut ()>,
|
||||
{
|
||||
// List of simple types are special-cased in the protocol for performance.
|
||||
match elt_tag {
|
||||
Tag::Bool => {
|
||||
let dest = core::slice::from_raw_parts_mut(storage as *mut u8, length);
|
||||
proto_async::read_chunk(stream, dest).await?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let ptr = storage as *mut u32;
|
||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 4);
|
||||
proto_async::read_chunk(stream, dest).await?;
|
||||
drop(dest);
|
||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||
NativeEndian::from_slice_u32(dest);
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let ptr = storage as *mut u64;
|
||||
let dest = core::slice::from_raw_parts_mut(ptr as *mut u8, length * 8);
|
||||
proto_async::read_chunk(stream, dest).await?;
|
||||
drop(dest);
|
||||
let dest = core::slice::from_raw_parts_mut(ptr, length);
|
||||
NativeEndian::from_slice_u64(dest);
|
||||
},
|
||||
_ => {
|
||||
let mut data = storage;
|
||||
for _ in 0..length {
|
||||
recv_value(stream, elt_tag, &mut data, alloc).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads (deserializes) a value of type `tag` from `stream`, writing the results to
|
||||
/// the kernel-side buffer `data` (the passed pointer to which is incremented to point
|
||||
/// past the just-received data). For nested allocations (lists/arrays), `alloc` is
|
||||
/// invoked any number of times with the size of the required allocation as a parameter
|
||||
/// (which is assumed to be correctly aligned for all payload types).
|
||||
#[async_recursion(?Send)]
|
||||
async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, data: &mut *mut (),
|
||||
alloc: &(impl Fn(usize) -> F + 'async_recursion))
|
||||
@ -65,30 +134,63 @@ async unsafe fn recv_value<F>(stream: &TcpStream, tag: Tag<'async_recursion>, da
|
||||
})
|
||||
}
|
||||
Tag::Tuple(it, arity) => {
|
||||
let alignment = tag.alignment();
|
||||
*data = round_up_mut(*data, alignment);
|
||||
let mut it = it.clone();
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
recv_value(stream, tag, data, alloc).await?;
|
||||
recv_value(stream, tag, data, alloc).await?
|
||||
}
|
||||
// Take into account any tail padding (if element(s) with largest alignment
|
||||
// are not at the end).
|
||||
*data = round_up_mut(*data, alignment);
|
||||
Ok(())
|
||||
}
|
||||
Tag::List(it) | Tag::Array(it) => {
|
||||
Tag::List(it) => {
|
||||
#[repr(C)]
|
||||
struct List { elements: *mut (), length: u32 };
|
||||
consume_value!(List, |ptr| {
|
||||
(*ptr).length = proto_async::read_i32(stream).await? as u32;
|
||||
|
||||
struct List { elements: *mut (), length: usize }
|
||||
consume_value!(*mut List, |ptr_to_list| {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
(*ptr).elements = alloc(tag.size() * (*ptr).length as usize).await;
|
||||
let length = proto_async::read_i32(stream).await? as usize;
|
||||
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
recv_value(stream, tag, &mut data, alloc).await?
|
||||
// To avoid multiple kernel CPU roundtrips, use a single allocation for
|
||||
// both the pointer/length List (slice) and the backing storage for the
|
||||
// elements. We can assume that alloc() is aligned suitably, so just
|
||||
// need to take into account any extra padding required.
|
||||
// (Note: At the time of writing, there will never actually be any types
|
||||
// with alignment larger than 8 bytes, so storage_offset == 0 always.)
|
||||
let list_size = 4 + 4;
|
||||
let storage_offset = round_up(list_size, tag.alignment());
|
||||
let storage_size = tag.size() * length;
|
||||
|
||||
let allocation = alloc(storage_offset + storage_size).await as *mut u8;
|
||||
*ptr_to_list = allocation as *mut List;
|
||||
let storage = allocation.offset(storage_offset as isize) as *mut ();
|
||||
|
||||
(**ptr_to_list).length = length;
|
||||
(**ptr_to_list).elements = storage;
|
||||
recv_elements(stream, tag, length, storage, alloc).await
|
||||
})
|
||||
}
|
||||
Tag::Array(it, num_dims) => {
|
||||
consume_value!(*mut (), |buffer| {
|
||||
// Deserialize length along each dimension and compute total number of
|
||||
// elements.
|
||||
let mut total_len: usize = 1;
|
||||
for _ in 0..num_dims {
|
||||
let len = proto_async::read_i32(stream).await? as usize;
|
||||
total_len *= len;
|
||||
consume_value!(usize, |ptr| *ptr = len )
|
||||
}
|
||||
Ok(())
|
||||
|
||||
// Allocate backing storage for elements; deserialize them.
|
||||
let elt_tag = it.clone().next().expect("truncated tag");
|
||||
*buffer = alloc(elt_tag.size() * total_len).await;
|
||||
recv_elements(stream, elt_tag, total_len, *buffer, alloc).await
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
*data = round_up_mut(*data, tag.alignment());
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
recv_value(stream, tag, data, alloc).await?;
|
||||
recv_value(stream, tag, data, alloc).await?;
|
||||
@ -115,6 +217,36 @@ pub async fn recv_return<F>(stream: &TcpStream, tag_bytes: &[u8], data: *mut (),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_elements<W>(writer: &mut W, elt_tag: Tag, length: usize, data: *const ())
|
||||
-> Result<(), Error>
|
||||
where W: Write + ?Sized
|
||||
{
|
||||
writer.write_u8(elt_tag.as_u8())?;
|
||||
match elt_tag {
|
||||
// we cannot use NativeEndian::from_slice_i32 as the data is not mutable,
|
||||
// and that is not needed as the data is already in native endian
|
||||
Tag::Bool => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int32 => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length * 4);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
Tag::Int64 | Tag::Float64 => {
|
||||
let slice = core::slice::from_raw_parts(data as *const u8, length * 8);
|
||||
writer.write_all(slice)?;
|
||||
},
|
||||
_ => {
|
||||
let mut data = data;
|
||||
for _ in 0..length {
|
||||
send_value(writer, elt_tag, &mut data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
-> Result<(), Error>
|
||||
where W: Write + ?Sized
|
||||
@ -148,23 +280,39 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut it = it.clone();
|
||||
writer.write_u8(arity)?;
|
||||
let mut max_alignment = 0;
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
max_alignment = core::cmp::max(max_alignment, tag.alignment());
|
||||
send_value(writer, tag, data)?
|
||||
}
|
||||
*data = round_up_const(*data, max_alignment);
|
||||
Ok(())
|
||||
}
|
||||
Tag::List(it) | Tag::Array(it) => {
|
||||
Tag::List(it) => {
|
||||
#[repr(C)]
|
||||
struct List { elements: *const (), length: u32 };
|
||||
consume_value!(List, |ptr| {
|
||||
struct List { elements: *const (), length: u32 }
|
||||
consume_value!(&List, |ptr| {
|
||||
let length = (**ptr).length as usize;
|
||||
writer.write_u32((*ptr).length)?;
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
let mut data = (*ptr).elements;
|
||||
for _ in 0..(*ptr).length as usize {
|
||||
send_value(writer, tag, &mut data)?;
|
||||
send_elements(writer, tag, length, (**ptr).elements)
|
||||
})
|
||||
}
|
||||
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;
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
let length = total_len as usize;
|
||||
send_elements(writer, elt_tag, length, *buffer)
|
||||
})
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
@ -176,7 +324,7 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
}
|
||||
Tag::Keyword(it) => {
|
||||
#[repr(C)]
|
||||
struct Keyword<'a> { name: CSlice<'a, u8> };
|
||||
struct Keyword<'a> { name: CSlice<'a, u8> }
|
||||
consume_value!(Keyword, |ptr| {
|
||||
writer.write_string(str::from_utf8((*ptr).name.as_ref()).unwrap())?;
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
@ -188,7 +336,7 @@ unsafe fn send_value<W>(writer: &mut W, tag: Tag, data: &mut *const ())
|
||||
}
|
||||
Tag::Object => {
|
||||
#[repr(C)]
|
||||
struct Object { id: u32 };
|
||||
struct Object { id: u32 }
|
||||
consume_value!(*const Object, |ptr|
|
||||
writer.write_u32((**ptr).id))
|
||||
}
|
||||
@ -245,7 +393,7 @@ mod tag {
|
||||
ByteArray,
|
||||
Tuple(TagIterator<'a>, u8),
|
||||
List(TagIterator<'a>),
|
||||
Array(TagIterator<'a>),
|
||||
Array(TagIterator<'a>, u8),
|
||||
Range(TagIterator<'a>),
|
||||
Keyword(TagIterator<'a>),
|
||||
Object
|
||||
@ -264,13 +412,38 @@ mod tag {
|
||||
Tag::ByteArray => b'A',
|
||||
Tag::Tuple(_, _) => b't',
|
||||
Tag::List(_) => b'l',
|
||||
Tag::Array(_) => b'a',
|
||||
Tag::Array(_, _) => b'a',
|
||||
Tag::Range(_) => b'r',
|
||||
Tag::Keyword(_) => b'k',
|
||||
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()
|
||||
}
|
||||
// the ptr/length(s) pair is basically CSlice
|
||||
Tag::Bytes | Tag::String | Tag::ByteArray | Tag::List(_) | Tag::Array(_, _) =>
|
||||
core::mem::align_of::<CSlice<()>>(),
|
||||
Tag::Keyword(_) => unreachable!("Tag::Keyword should not appear in composite types"),
|
||||
Tag::Object => core::mem::align_of::<u32>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self) -> usize {
|
||||
match self {
|
||||
Tag::None => 0,
|
||||
@ -283,15 +456,22 @@ mod tag {
|
||||
Tag::ByteArray => 8,
|
||||
Tag::Tuple(it, arity) => {
|
||||
let mut size = 0;
|
||||
let mut max_alignment = 0;
|
||||
let mut it = it.clone();
|
||||
for _ in 0..arity {
|
||||
let tag = it.next().expect("truncated tag");
|
||||
let alignment = tag.alignment();
|
||||
max_alignment = core::cmp::max(max_alignment, alignment);
|
||||
size = super::round_up(size, alignment);
|
||||
size += tag.size();
|
||||
}
|
||||
// Take into account any tail padding (if element(s) with largest
|
||||
// alignment are not at the end).
|
||||
size = super::round_up(size, max_alignment);
|
||||
size
|
||||
}
|
||||
Tag::List(_) => 8,
|
||||
Tag::Array(_) => 8,
|
||||
Tag::List(_) => 4,
|
||||
Tag::Array(_, num_dims) => 4 * (1 + num_dims as usize),
|
||||
Tag::Range(it) => {
|
||||
let tag = it.clone().next().expect("truncated tag");
|
||||
tag.size() * 3
|
||||
@ -309,10 +489,23 @@ mod tag {
|
||||
|
||||
impl<'a> TagIterator<'a> {
|
||||
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
|
||||
TagIterator { data: data }
|
||||
TagIterator { 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 {
|
||||
return None
|
||||
}
|
||||
@ -334,21 +527,17 @@ mod tag {
|
||||
Tag::Tuple(self.sub(count), count)
|
||||
}
|
||||
b'l' => Tag::List(self.sub(1)),
|
||||
b'a' => Tag::Array(self.sub(1)),
|
||||
b'a' => {
|
||||
let count = self.data[0];
|
||||
self.data = &self.data[1..];
|
||||
Tag::Array(self.sub(1), count)
|
||||
}
|
||||
b'r' => Tag::Range(self.sub(1)),
|
||||
b'k' => Tag::Keyword(self.sub(1)),
|
||||
b'O' => Tag::Object,
|
||||
_ => 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> {
|
||||
@ -389,10 +578,10 @@ mod tag {
|
||||
it.fmt(f)?;
|
||||
write!(f, ")")?;
|
||||
}
|
||||
Tag::Array(it) => {
|
||||
Tag::Array(it, num_dims) => {
|
||||
write!(f, "Array(")?;
|
||||
it.fmt(f)?;
|
||||
write!(f, ")")?;
|
||||
write!(f, ", {})", num_dims)?;
|
||||
}
|
||||
Tag::Range(it) => {
|
||||
write!(f, "Range(")?;
|
||||
|
@ -1,10 +1,11 @@
|
||||
use cslice::CSlice;
|
||||
use vcell::VolatileCell;
|
||||
use libcortex_a9::asm;
|
||||
|
||||
use crate::artiq_raise;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use crate::pl::csr;
|
||||
use crate::rtio_mgt::resolve_channel_name;
|
||||
|
||||
pub const RTIO_O_STATUS_WAIT: i32 = 1;
|
||||
pub const RTIO_O_STATUS_UNDERFLOW: i32 = 2;
|
||||
@ -20,33 +21,33 @@ pub struct TimestampedData {
|
||||
data: i32,
|
||||
}
|
||||
|
||||
#[repr(C, align(32))]
|
||||
#[repr(C, align(64))]
|
||||
struct Transaction {
|
||||
request_cmd: i8,
|
||||
padding0: i8,
|
||||
padding1: i8,
|
||||
padding2: i8,
|
||||
data_width: i8,
|
||||
padding0: [i8; 2],
|
||||
request_target: i32,
|
||||
request_timestamp: i64,
|
||||
request_data: i64,
|
||||
padding: i64,
|
||||
request_data: [i32; 16],
|
||||
padding1: [i64; 2],
|
||||
reply_status: VolatileCell<i32>,
|
||||
reply_data: VolatileCell<i32>,
|
||||
reply_timestamp: VolatileCell<i64>
|
||||
reply_timestamp: VolatileCell<i64>,
|
||||
padding2: [i64; 2],
|
||||
}
|
||||
|
||||
static mut TRANSACTION_BUFFER: Transaction = Transaction {
|
||||
request_cmd: 0,
|
||||
padding0: 0,
|
||||
padding1: 0,
|
||||
padding2: 0,
|
||||
data_width: 0,
|
||||
request_target: 0,
|
||||
request_timestamp: 0,
|
||||
request_data: 0,
|
||||
padding: 0,
|
||||
request_data: [0; 16],
|
||||
reply_status: 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() {
|
||||
@ -57,11 +58,6 @@ pub extern fn init() {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn get_destination_status(destination: i32) -> bool {
|
||||
// TODO
|
||||
destination == 0
|
||||
}
|
||||
|
||||
pub extern fn get_counter() -> i64 {
|
||||
unsafe {
|
||||
csr::rtio::counter_update_write(1);
|
||||
@ -92,12 +88,12 @@ unsafe fn process_exceptional_status(channel: i32, status: i32) {
|
||||
}
|
||||
if status & RTIO_O_STATUS_UNDERFLOW != 0 {
|
||||
artiq_raise!("RTIOUnderflow",
|
||||
"RTIO underflow at {0} mu, channel {1}, slack {2} mu",
|
||||
timestamp, channel as i64, timestamp - get_counter());
|
||||
format!("RTIO underflow at {{1}} mu, channel 0x{:04x}:{}, slack {{2}} mu", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, timestamp, timestamp - get_counter());
|
||||
}
|
||||
if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, output, at {0} mu, channel {1}",
|
||||
format!("RTIO destination unreachable, output, at {{0}} mu, channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
timestamp, channel as i64, 0);
|
||||
}
|
||||
}
|
||||
@ -108,13 +104,41 @@ pub extern fn output(target: i32, data: i32) {
|
||||
TRANSACTION_BUFFER.reply_status.set(0);
|
||||
|
||||
TRANSACTION_BUFFER.request_cmd = 0;
|
||||
TRANSACTION_BUFFER.data_width = 1;
|
||||
TRANSACTION_BUFFER.request_target = target;
|
||||
TRANSACTION_BUFFER.request_timestamp = NOW;
|
||||
TRANSACTION_BUFFER.request_data = data as i64;
|
||||
TRANSACTION_BUFFER.request_data[0] = data;
|
||||
|
||||
asm::dmb();
|
||||
fence(Ordering::SeqCst);
|
||||
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;
|
||||
loop {
|
||||
status = TRANSACTION_BUFFER.reply_status.get();
|
||||
@ -130,21 +154,17 @@ pub extern fn output(target: i32, data: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn output_wide(target: i32, data: CSlice<i32>) {
|
||||
// TODO
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||
unsafe {
|
||||
// Clear status so we can observe response
|
||||
TRANSACTION_BUFFER.reply_status.set(0);
|
||||
|
||||
TRANSACTION_BUFFER.request_cmd = 1;
|
||||
TRANSACTION_BUFFER.request_timestamp = NOW;
|
||||
TRANSACTION_BUFFER.request_timestamp = timeout;
|
||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||
TRANSACTION_BUFFER.data_width = 0;
|
||||
|
||||
asm::dmb();
|
||||
fence(Ordering::SeqCst);
|
||||
asm::sev();
|
||||
|
||||
let mut status;
|
||||
@ -157,7 +177,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
|
||||
@ -165,7 +185,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
@ -180,8 +200,9 @@ pub extern fn input_data(channel: i32) -> i32 {
|
||||
TRANSACTION_BUFFER.request_cmd = 1;
|
||||
TRANSACTION_BUFFER.request_timestamp = -1;
|
||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||
TRANSACTION_BUFFER.data_width = 0;
|
||||
|
||||
asm::dmb();
|
||||
fence(Ordering::SeqCst);
|
||||
asm::sev();
|
||||
|
||||
let mut status;
|
||||
@ -194,12 +215,12 @@ pub extern fn input_data(channel: i32) -> i32 {
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
@ -214,8 +235,9 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||
TRANSACTION_BUFFER.request_cmd = 1;
|
||||
TRANSACTION_BUFFER.request_timestamp = timeout;
|
||||
TRANSACTION_BUFFER.request_target = channel << 8;
|
||||
TRANSACTION_BUFFER.data_width = 0;
|
||||
|
||||
asm::dmb();
|
||||
fence(Ordering::SeqCst);
|
||||
asm::sev();
|
||||
|
||||
let mut status;
|
||||
@ -228,12 +250,12 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
@ -245,6 +267,17 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||
}
|
||||
|
||||
pub fn write_log(data: &[i8]) {
|
||||
// TODO
|
||||
unimplemented!();
|
||||
let mut word: u32 = 0;
|
||||
for i in 0..data.len() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
257
src/runtime/src/rtio_clocking.rs
Normal file
257
src/runtime/src/rtio_clocking.rs
Normal file
@ -0,0 +1,257 @@
|
||||
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, si5324_ref_input) = match clk {
|
||||
RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW
|
||||
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
|
||||
(
|
||||
si5324::FrequencySettings {
|
||||
n1_hs : 10,
|
||||
nc1_ls : 4,
|
||||
n2_hs : 10,
|
||||
n2_ls : 300,
|
||||
n31 : 6,
|
||||
n32 : 6,
|
||||
bwsel : 4,
|
||||
crystal_as_ckin2: false
|
||||
},
|
||||
SI5324_EXT_INPUT
|
||||
)
|
||||
},
|
||||
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_as_ckin2: false
|
||||
},
|
||||
SI5324_EXT_INPUT
|
||||
)
|
||||
},
|
||||
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_as_ckin2: false
|
||||
},
|
||||
SI5324_EXT_INPUT
|
||||
)
|
||||
},
|
||||
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_as_ckin2: true
|
||||
},
|
||||
si5324::Input::Ckin2
|
||||
)
|
||||
},
|
||||
RtioClock::Int_100 => { // 100MHz output, from crystal
|
||||
info!("using internal 100MHz RTIO clock");
|
||||
(
|
||||
si5324::FrequencySettings {
|
||||
n1_hs : 9,
|
||||
nc1_ls : 6,
|
||||
n2_hs : 10,
|
||||
n2_ls : 33732,
|
||||
n31 : 7139,
|
||||
n32 : 7139,
|
||||
bwsel : 3,
|
||||
crystal_as_ckin2: true
|
||||
},
|
||||
si5324::Input::Ckin2
|
||||
)
|
||||
},
|
||||
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
|
||||
info!("using internal 125MHz RTIO clock");
|
||||
(
|
||||
si5324::FrequencySettings {
|
||||
n1_hs : 10,
|
||||
nc1_ls : 4,
|
||||
n2_hs : 10,
|
||||
n2_ls : 19972,
|
||||
n31 : 4565,
|
||||
n32 : 4565,
|
||||
bwsel : 4,
|
||||
crystal_as_ckin2: true
|
||||
},
|
||||
si5324::Input::Ckin2
|
||||
)
|
||||
},
|
||||
_ => { // same setting as Int_125, but fallback to default
|
||||
warn!("rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.", clk);
|
||||
(
|
||||
si5324::FrequencySettings {
|
||||
n1_hs : 10,
|
||||
nc1_ls : 4,
|
||||
n2_hs : 10,
|
||||
n2_ls : 19972,
|
||||
n31 : 4565,
|
||||
n32 : 4565,
|
||||
bwsel : 4,
|
||||
crystal_as_ckin2: true
|
||||
},
|
||||
si5324::Input::Ckin2
|
||||
)
|
||||
}
|
||||
};
|
||||
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
|
||||
}
|
||||
|
||||
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||
|
||||
let clk = get_rtio_clock_cfg(cfg);
|
||||
#[cfg(has_si5324)]
|
||||
{
|
||||
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
|
||||
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);
|
||||
|
||||
}
|
@ -4,6 +4,7 @@ use cslice::CSlice;
|
||||
use crate::artiq_raise;
|
||||
|
||||
use crate::pl::csr;
|
||||
use crate::rtio_mgt::resolve_channel_name;
|
||||
|
||||
pub const RTIO_O_STATUS_WAIT: u8 = 1;
|
||||
pub const RTIO_O_STATUS_UNDERFLOW: u8 = 2;
|
||||
@ -25,11 +26,6 @@ pub extern fn init() {
|
||||
}
|
||||
}
|
||||
|
||||
pub extern fn get_destination_status(destination: i32) -> bool {
|
||||
// TODO
|
||||
destination == 0
|
||||
}
|
||||
|
||||
pub extern fn get_counter() -> i64 {
|
||||
unsafe {
|
||||
csr::rtio::counter_update_write(1);
|
||||
@ -77,12 +73,12 @@ unsafe fn process_exceptional_status(channel: i32, status: u8) {
|
||||
}
|
||||
if status & RTIO_O_STATUS_UNDERFLOW != 0 {
|
||||
artiq_raise!("RTIOUnderflow",
|
||||
"RTIO underflow at {0} mu, channel {1}, slack {2} mu",
|
||||
timestamp, channel as i64, timestamp - get_counter());
|
||||
format!("RTIO underflow at {{1}} mu, channel 0x{:04x}:{}, slack {{2}} mu", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, timestamp, timestamp - get_counter());
|
||||
}
|
||||
if status & RTIO_O_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, output, at {0} mu, channel {1}",
|
||||
format!("RTIO destination unreachable, output, at {{0}} mu, channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
timestamp, channel as i64, 0);
|
||||
}
|
||||
}
|
||||
@ -125,7 +121,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
|
||||
@ -133,7 +129,7 @@ pub extern fn input_timestamp(timeout: i64, channel: i32) -> i64 {
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
@ -153,12 +149,12 @@ pub extern fn input_data(channel: i32) -> i32 {
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
@ -178,7 +174,7 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||
|
||||
if status & RTIO_I_STATUS_OVERFLOW != 0 {
|
||||
artiq_raise!("RTIOOverflow",
|
||||
"RTIO input overflow on channel {0}",
|
||||
format!("RTIO input overflow on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
if status & RTIO_I_STATUS_WAIT_EVENT != 0 {
|
||||
@ -186,7 +182,7 @@ pub extern fn input_timestamped_data(timeout: i64, channel: i32) -> TimestampedD
|
||||
}
|
||||
if status & RTIO_I_STATUS_DESTINATION_UNREACHABLE != 0 {
|
||||
artiq_raise!("RTIODestinationUnreachable",
|
||||
"RTIO destination unreachable, input, on channel {0}",
|
||||
format!("RTIO destination unreachable, input, on channel 0x{:04x}:{}", channel, resolve_channel_name(channel as u32)),
|
||||
channel as i64, 0, 0);
|
||||
}
|
||||
|
||||
|
403
src/runtime/src/rtio_mgt.rs
Normal file
403
src/runtime/src/rtio_mgt.rs
Normal file
@ -0,0 +1,403 @@
|
||||
use core::cell::RefCell;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use libboard_zynq::timer::GlobalTimer;
|
||||
use libboard_artiq::{pl::csr, drtio_routing};
|
||||
use libcortex_a9::mutex::Mutex;
|
||||
use libconfig::Config;
|
||||
use io::{Cursor, ProtoRead};
|
||||
use log::error;
|
||||
|
||||
static mut RTIO_DEVICE_MAP: BTreeMap<u32, String> = BTreeMap::new();
|
||||
|
||||
|
||||
#[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, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_SEQUENCE_ERROR };
|
||||
}
|
||||
Ok(Packet::DestinationCollisionReply { channel }) =>{
|
||||
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_COLLISION };
|
||||
}
|
||||
Ok(Packet::DestinationBusyReply { channel }) =>{
|
||||
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}:{}", destination, channel, resolve_channel_name(channel as u32));
|
||||
unsafe { SEEN_ASYNC_ERRORS |= ASYNC_ERROR_BUSY };
|
||||
}
|
||||
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_device_map(cfg: &Config) -> BTreeMap<u32, String> {
|
||||
let mut device_map: BTreeMap<u32, String> = BTreeMap::new();
|
||||
let _ = cfg.read("device_map").and_then(|raw_bytes| {
|
||||
let mut bytes_cr = Cursor::new(raw_bytes);
|
||||
let size = bytes_cr.read_u32().unwrap();
|
||||
for _ in 0..size {
|
||||
let channel = bytes_cr.read_u32().unwrap();
|
||||
let device_name = bytes_cr.read_string().unwrap();
|
||||
if let Some(old_entry) = device_map.insert(channel, device_name.clone()) {
|
||||
error!("read_device_map: conflicting entries for channel {}: `{}` and `{}`",
|
||||
channel, old_entry, device_name);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} ).or_else(|err| {
|
||||
error!("read_device_map: error reading `device_map` from config: {}", err);
|
||||
Err(err)
|
||||
});
|
||||
device_map
|
||||
}
|
||||
|
||||
fn _resolve_channel_name(channel: u32, device_map: &BTreeMap<u32, String>) -> String {
|
||||
match device_map.get(&channel) {
|
||||
Some(val) => val.clone(),
|
||||
None => String::from("unknown")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_channel_name(channel: u32) -> String {
|
||||
_resolve_channel_name(channel, unsafe{&RTIO_DEVICE_MAP})
|
||||
}
|
||||
|
||||
#[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,
|
||||
cfg: &Config) {
|
||||
unsafe { RTIO_DEVICE_MAP = read_device_map(cfg); }
|
||||
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)
|
||||
}
|
@ -1,303 +0,0 @@
|
||||
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(());
|
||||
}
|
||||
}
|
28
src/satman/Cargo.toml
Normal file
28
src/satman/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[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" }
|
6
src/satman/build.rs
Normal file
6
src/satman/build.rs
Normal file
@ -0,0 +1,6 @@
|
||||
extern crate build_zynq;
|
||||
|
||||
fn main() {
|
||||
build_zynq::add_linker_script();
|
||||
build_zynq::cfg();
|
||||
}
|
659
src/satman/src/main.rs
Normal file
659
src/satman/src/main.rs
Normal file
@ -0,0 +1,659 @@
|
||||
#![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_as_ckin2: 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_as_ckin2: 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
|
||||
}
|
290
src/satman/src/repeater.rs
Normal file
290
src/satman/src/repeater.rs
Normal file
@ -0,0 +1,290 @@
|
||||
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(()) }
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
[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" }
|
@ -1,49 +0,0 @@
|
||||
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");
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
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.*);
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
#![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");
|
||||
}
|
@ -1,670 +0,0 @@
|
||||
/*
|
||||
*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
Block a user