forked from M-Labs/artiq-zynq
Compare commits
69 Commits
Author | SHA1 | Date |
---|---|---|
stevefan1999 | fdb658839b | |
Steve Fan | 24747f09cd | |
stevefan1999 | a4fbb96296 | |
Steve Fan | 851ed095df | |
Steve Fan | dbc3f581e5 | |
mwojcik | 64fecf09b7 | |
mwojcik | 31fb2b388a | |
mwojcik | e045837b67 | |
mwojcik | ada3f2e704 | |
mwojcik | 8be5048cd3 | |
mwojcik | e8db2a4b49 | |
mwojcik | 4218354e65 | |
mwojcik | 2376f9ab5e | |
mwojcik | 0b27349ec4 | |
mwojcik | 21eb1cab1a | |
mwojcik | 3096daaaee | |
mwojcik | 4fbfccf575 | |
mwojcik | 5c40115945 | |
Sebastien Bourdeauducq | a5e3580d18 | |
Sebastien Bourdeauducq | 3582af564d | |
mwojcik | 742ce9fdde | |
mwojcik | c4de1c261a | |
mwojcik | 219c075931 | |
mwojcik | d04a7decfe | |
mwojcik | 0efa83e956 | |
Sebastien Bourdeauducq | 4fa824f42b | |
mwojcik | ab0c205dd2 | |
mwojcik | 8d2bb09149 | |
Sebastien Bourdeauducq | 41295b0e01 | |
mwojcik | aaec0abdf6 | |
mwojcik | e241957419 | |
mwojcik | 50262b3f0c | |
mwojcik | 827c6c1306 | |
mwojcik | e6863263b4 | |
mwojcik | d7f45d473e | |
pca006132 | 35250b3f56 | |
Sebastien Bourdeauducq | 2ed2ffe417 | |
Sebastien Bourdeauducq | 18e05c91e1 | |
mwojcik | e3d3cb2311 | |
Sebastien Bourdeauducq | f543501012 | |
Sebastien Bourdeauducq | 111ac0c716 | |
Sebastien Bourdeauducq | 8128dc0b56 | |
Sebastien Bourdeauducq | cbcda286dc | |
Sebastien Bourdeauducq | dcb6129b0e | |
Sebastien Bourdeauducq | f5933092c9 | |
Sebastien Bourdeauducq | f25e261bdd | |
Sebastien Bourdeauducq | 44c2c0fe4d | |
Sebastien Bourdeauducq | 4c2c23fcdd | |
Sebastien Bourdeauducq | 480a80cab7 | |
Sebastien Bourdeauducq | 2ba4d8935d | |
Sebastien Bourdeauducq | 8c8a5d53b9 | |
Sebastien Bourdeauducq | 852123b42a | |
Sebastien Bourdeauducq | f1fd55dee5 | |
Sebastien Bourdeauducq | 21d98711c1 | |
Sebastien Bourdeauducq | 0ae2138034 | |
Sebastien Bourdeauducq | ce9d38827b | |
Sebastien Bourdeauducq | 1b474d2dd4 | |
Sebastien Bourdeauducq | 4f1689f254 | |
Sebastien Bourdeauducq | 506c741238 | |
Sebastien Bourdeauducq | 8815f76114 | |
Sebastien Bourdeauducq | ef18fa4c6d | |
Sebastien Bourdeauducq | faf9714e10 | |
Sebastien Bourdeauducq | c90cb7adad | |
Sebastien Bourdeauducq | 8d4e42be32 | |
Sebastien Bourdeauducq | dcd3cbc488 | |
Astro | fcb38fae6c | |
Astro | bfd8343876 | |
Sebastien Bourdeauducq | 4039431533 | |
Sebastien Bourdeauducq | 3f9bd06468 |
14
README.md
14
README.md
|
@ -24,7 +24,7 @@ The following configuration keys are available:
|
||||||
- ``ip``: IPv4 address.
|
- ``ip``: IPv4 address.
|
||||||
- ``ip6``: IPv6 address.
|
- ``ip6``: IPv6 address.
|
||||||
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
- ``startup``: startup kernel in ELF format (as produced by ``artiq_compile``).
|
||||||
- ``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.
|
- ``boot``: SD card "boot.bin" file, for replacing the boot firmware/gateware. Write only.
|
||||||
|
|
||||||
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
Configurations can be read/written/removed via ``artiq_coremgmt``. Config erase is
|
||||||
|
@ -45,7 +45,7 @@ Note: if you are using Nix channels the first time, you need to be aware of this
|
||||||
Pure build with Nix and execution on a remote JTAG server:
|
Pure build with Nix and execution on a remote JTAG server:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix-build -A zc706-simple-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock-jtag
|
nix-build -A zc706-nist_clock-jtag # or zc706-nist_qc2-jtag or zc706-nist_clock_satellite-jtag etc.
|
||||||
./remote_run.sh
|
./remote_run.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -54,19 +54,23 @@ Impure incremental build and execution on a remote JTAG server:
|
||||||
```shell
|
```shell
|
||||||
nix-shell
|
nix-shell
|
||||||
cd src
|
cd src
|
||||||
gateware/zc706.py -g ../build/gateware # build gateware
|
gateware/zc706.py -g ../build/gateware -v <variant> # build gateware
|
||||||
make # build firmware
|
make GWARGS="-V <variant>" <runtime/satman> # build firmware
|
||||||
cd ..
|
cd ..
|
||||||
./remote_run.sh -i
|
./remote_run.sh -i
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- This is developed with Nixpkgs 21.05, and the ``nixbld.m-labs.hk`` binary substituter can also be used here (see the ARTIQ manual for the public key and instructions).
|
- This is developed with Nixpkgs 21.05[^1], and the ``nixbld.m-labs.hk`` binary substituter can also be used here (see the ARTIQ manual for the public key and instructions).
|
||||||
- The impure build process is also compatible with non-Nix systems.
|
- The impure build process is also compatible with non-Nix systems.
|
||||||
|
- When calling make, you need to specify both the variant and firmware type.
|
||||||
|
- Firmware type must be either ``runtime`` for DRTIO-less or DRTIO master variants, or ``satman`` for DRTIO satellite.
|
||||||
- If the board is connected to the local machine, use the ``local_run.sh`` script.
|
- If the board is connected to the local machine, use the ``local_run.sh`` script.
|
||||||
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``zynq-rs.nix`` in sync.
|
- To update ``zynq-rs``, update the cargo files as per usual for Rust projects, but also keep ``zynq-rs.nix`` in sync.
|
||||||
|
|
||||||
|
[^1]: Thus, on newer version of NixOS, you should run `nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/21.05.tar.gz` instead
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
61
default.nix
61
default.nix
|
@ -8,23 +8,31 @@ let
|
||||||
vivado = import <artiq-fast/vivado.nix> { inherit pkgs; };
|
vivado = import <artiq-fast/vivado.nix> { inherit pkgs; };
|
||||||
# FSBL configuration supplied by Vivado 2020.1 for these boards:
|
# FSBL configuration supplied by Vivado 2020.1 for these boards:
|
||||||
fsblTargets = ["zc702" "zc706" "zed"];
|
fsblTargets = ["zc702" "zc706" "zed"];
|
||||||
build = { target, variant }: let
|
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 = (import zynq-rs)."${target}-szl";
|
szl = (import zynq-rs)."${target}-szl";
|
||||||
fsbl = import "${zynq-rs}/nix/fsbl.nix" {
|
fsbl = import "${zynq-rs}/nix/fsbl.nix" {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
board = target;
|
board = target;
|
||||||
};
|
};
|
||||||
|
fwtype = if builtins.elem variant sat_variants then "satman" else "runtime";
|
||||||
|
|
||||||
firmware = rustPlatform.buildRustPackage rec {
|
firmware = rustPlatform.buildRustPackage rec {
|
||||||
# note: due to fetchCargoTarball, cargoSha256 depends on package name
|
# note: due to fetchCargoTarball, cargoSha256 depends on package name
|
||||||
name = "firmware";
|
name = "firmware";
|
||||||
|
|
||||||
src = ./src;
|
src = ./src;
|
||||||
cargoSha256 = "1g1nb8jz1na1v60hq3zrpgrwnw9qz7xrnnr2lkiyhsp72bg52ssl";
|
cargoSha256 = "sha256-uiwESZNwPdVnDkA1n0v1DQHp3rTazDkgIYscVTpgNq0=";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.gnumake
|
pkgs.gnumake
|
||||||
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
|
||||||
cargo-xbuild
|
cargo-xbuild
|
||||||
pkgs.llvmPackages_9.llvm
|
pkgs.llvmPackages_9.llvm
|
||||||
pkgs.llvmPackages_9.clang-unwrapped
|
pkgs.llvmPackages_9.clang-unwrapped
|
||||||
|
@ -33,15 +41,15 @@ let
|
||||||
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
|
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 CLANG_EXTRA_INCLUDE_DIR="${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include"
|
||||||
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
|
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
|
||||||
make TARGET=${target} VARIANT=${variant}
|
make TARGET=${target} GWARGS="${if json == null then "-V ${variant}" else json}" ${fwtype}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out $out/nix-support
|
mkdir -p $out $out/nix-support
|
||||||
cp ../build/runtime.bin $out/runtime.bin
|
cp ../build/${fwtype}.bin $out/${fwtype}.bin
|
||||||
cp ../build/firmware/armv7-none-eabihf/release/runtime $out/runtime.elf
|
cp ../build/firmware/armv7-none-eabihf/release/${fwtype} $out/${fwtype}.elf
|
||||||
echo file binary-dist $out/runtime.bin >> $out/nix-support/hydra-build-products
|
echo file binary-dist $out/${fwtype}.bin >> $out/nix-support/hydra-build-products
|
||||||
echo file binary-dist $out/runtime.elf >> $out/nix-support/hydra-build-products
|
echo file binary-dist $out/${fwtype}.elf >> $out/nix-support/hydra-build-products
|
||||||
'';
|
'';
|
||||||
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
@ -50,12 +58,12 @@ let
|
||||||
gateware = pkgs.runCommand "${target}-${variant}-gateware"
|
gateware = pkgs.runCommand "${target}-${variant}-gateware"
|
||||||
{
|
{
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ])))
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ ps.jsonschema migen migen-axi misoc artiq ])))
|
||||||
vivado
|
vivado
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
python ${./src/gateware}/${target}.py -g build -V ${variant}
|
python ${./src/gateware}/${target}.py -g build ${if json == null then "-V ${variant}" else json}
|
||||||
mkdir -p $out $out/nix-support
|
mkdir -p $out $out/nix-support
|
||||||
cp build/top.bit $out
|
cp build/top.bit $out
|
||||||
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
|
echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products
|
||||||
|
@ -66,7 +74,7 @@ let
|
||||||
''
|
''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
ln -s ${szl}/szl.elf $out
|
ln -s ${szl}/szl.elf $out
|
||||||
ln -s ${firmware}/runtime.bin $out
|
ln -s ${firmware}/${fwtype}.bin $out
|
||||||
ln -s ${gateware}/top.bit $out
|
ln -s ${gateware}/top.bit $out
|
||||||
'';
|
'';
|
||||||
sd = pkgs.runCommand "${target}-${variant}-sd"
|
sd = pkgs.runCommand "${target}-${variant}-sd"
|
||||||
|
@ -79,14 +87,14 @@ let
|
||||||
bifdir=`mktemp -d`
|
bifdir=`mktemp -d`
|
||||||
cd $bifdir
|
cd $bifdir
|
||||||
ln -s ${szl}/szl.elf szl.elf
|
ln -s ${szl}/szl.elf szl.elf
|
||||||
ln -s ${firmware}/runtime.elf runtime.elf
|
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
|
||||||
ln -s ${gateware}/top.bit top.bit
|
ln -s ${gateware}/top.bit top.bit
|
||||||
cat > boot.bif << EOF
|
cat > boot.bif << EOF
|
||||||
the_ROM_image:
|
the_ROM_image:
|
||||||
{
|
{
|
||||||
[bootloader]szl.elf
|
[bootloader]szl.elf
|
||||||
top.bit
|
top.bit
|
||||||
runtime.elf
|
${fwtype}.elf
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
mkdir $out $out/nix-support
|
mkdir $out $out/nix-support
|
||||||
|
@ -104,13 +112,13 @@ let
|
||||||
cd $bifdir
|
cd $bifdir
|
||||||
ln -s ${fsbl}/fsbl.elf fsbl.elf
|
ln -s ${fsbl}/fsbl.elf fsbl.elf
|
||||||
ln -s ${gateware}/top.bit top.bit
|
ln -s ${gateware}/top.bit top.bit
|
||||||
ln -s ${firmware}/runtime.elf runtime.elf
|
ln -s ${firmware}/${fwtype}.elf ${fwtype}.elf
|
||||||
cat > boot.bif << EOF
|
cat > boot.bif << EOF
|
||||||
the_ROM_image:
|
the_ROM_image:
|
||||||
{
|
{
|
||||||
[bootloader]fsbl.elf
|
[bootloader]fsbl.elf
|
||||||
top.bit
|
top.bit
|
||||||
runtime.elf
|
${fwtype}.elf
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
mkdir $out $out/nix-support
|
mkdir $out $out/nix-support
|
||||||
|
@ -131,17 +139,24 @@ let
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
(
|
(
|
||||||
(build { target = "zc706"; variant = "simple"; }) //
|
|
||||||
(build { target = "zc706"; variant = "nist_clock"; }) //
|
(build { target = "zc706"; variant = "nist_clock"; }) //
|
||||||
|
(build { target = "zc706"; variant = "nist_clock_master"; }) //
|
||||||
|
(build { target = "zc706"; variant = "nist_clock_satellite"; }) //
|
||||||
|
(build { target = "zc706"; variant = "nist_clock_satellite_100mhz"; }) //
|
||||||
(build { target = "zc706"; variant = "nist_qc2"; }) //
|
(build { target = "zc706"; variant = "nist_qc2"; }) //
|
||||||
(build { target = "zc706"; variant = "acpki_simple"; }) //
|
(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"; }) //
|
||||||
|
(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"; }) //
|
||||||
(build { target = "coraz7"; variant = "10"; }) //
|
(build { target = "zc706"; variant = "acpki_nist_qc2_master"; }) //
|
||||||
(build { target = "coraz7"; variant = "07s"; }) //
|
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite"; }) //
|
||||||
(build { target = "coraz7"; variant = "acpki_10"; }) //
|
(build { target = "zc706"; variant = "acpki_nist_qc2_satellite_100mhz"; }) //
|
||||||
(build { target = "coraz7"; variant = "acpki_07s"; }) //
|
(build { target = "kasli_soc"; variant = "demo"; json = ./demo.json; }) //
|
||||||
(build { target = "redpitaya"; variant = "simple"; }) //
|
(build { target = "kasli_soc"; variant = "master"; json = ./kasli-soc-master.json; }) //
|
||||||
(build { target = "redpitaya"; variant = "acpki_simple"; }) //
|
(build { target = "kasli_soc"; variant = "satellite"; json = ./kasli-soc-satellite.json; }) //
|
||||||
{ inherit zynq-rs; }
|
{ inherit zynq-rs; }
|
||||||
)
|
)
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -29,30 +29,11 @@ device_db = {
|
||||||
"class": "PCA9548"
|
"class": "PCA9548"
|
||||||
},
|
},
|
||||||
|
|
||||||
# led? are common to all variants
|
|
||||||
"led0": {
|
"led0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLOut",
|
"class": "TTLOut",
|
||||||
"arguments": {"channel": 0},
|
"arguments": {"channel": 41},
|
||||||
},
|
|
||||||
"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}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +43,7 @@ for i in range(40):
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"module": "artiq.coredevice.ttl",
|
"module": "artiq.coredevice.ttl",
|
||||||
"class": "TTLInOut",
|
"class": "TTLInOut",
|
||||||
"arguments": {"channel": 4+i}
|
"arguments": {"channel": i}
|
||||||
}
|
}
|
||||||
|
|
||||||
device_db["ad9914dds0"] = {
|
device_db["ad9914dds0"] = {
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
25
local_run.sh
25
local_run.sh
|
@ -13,9 +13,10 @@ fi
|
||||||
|
|
||||||
impure=0
|
impure=0
|
||||||
load_bitstream=1
|
load_bitstream=1
|
||||||
board_host="192.168.1.52"
|
board_type="kasli_soc"
|
||||||
|
fw_type="runtime"
|
||||||
|
|
||||||
while getopts "ilb:" opt; do
|
while getopts "ilb:t:f:" opt; do
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
\?) exit 1
|
\?) exit 1
|
||||||
;;
|
;;
|
||||||
|
@ -25,24 +26,36 @@ while getopts "ilb:" opt; do
|
||||||
;;
|
;;
|
||||||
b) board_host=$OPTARG
|
b) board_host=$OPTARG
|
||||||
;;
|
;;
|
||||||
|
t) board_type=$OPTARG
|
||||||
|
;;
|
||||||
|
f) fw_type=$OPTARG
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ -z "$board_host" ]; then
|
||||||
|
case $board_type in
|
||||||
|
kasli_soc) board_host="192.168.1.56";;
|
||||||
|
zc706) board_host="192.168.1.52";;
|
||||||
|
*) echo "Unknown board type"; exit 1;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
load_bitstream_cmd=""
|
load_bitstream_cmd=""
|
||||||
|
|
||||||
build_dir=`pwd`/build
|
build_dir=`pwd`/build
|
||||||
result_dir=`pwd`/result
|
result_dir=`pwd`/result
|
||||||
cd $OPENOCD_ZYNQ
|
cd $OPENOCD_ZYNQ
|
||||||
openocd -f zc706.cfg -c "load_image $SZL; resume 0; exit"
|
openocd -f $board_type.cfg -c "load_image $SZL/szl-$board_type.elf; resume 0; exit"
|
||||||
sleep 5
|
sleep 5
|
||||||
if [ $impure -eq 1 ]; then
|
if [ $impure -eq 1 ]; then
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g $build_dir/gateware/top.bit"
|
load_bitstream_cmd="-g $build_dir/gateware/top.bit"
|
||||||
fi
|
fi
|
||||||
artiq_netboot $load_bitstream_cmd -f $build_dir/runtime.bin -b $board_host
|
artiq_netboot $load_bitstream_cmd -f $build_dir/$fwtype.bin -b $board_host
|
||||||
else
|
else
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g $result_dir/top.bit"
|
load_bitstream_cmd="-g $result_dir/top.bit"
|
||||||
fi
|
fi
|
||||||
artiq_netboot $load_bitstream_cmd -f $result_dir/runtime.bin -b $board_host
|
artiq_netboot $load_bitstream_cmd -f $result_dir/$fwtype.bin -b $board_host
|
||||||
fi
|
fi
|
|
@ -1,5 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Only ZC706 supported for now.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -z "$OPENOCD_ZYNQ" ]; then
|
if [ -z "$OPENOCD_ZYNQ" ]; then
|
||||||
|
@ -18,8 +20,9 @@ impure_dir="build"
|
||||||
sshopts=""
|
sshopts=""
|
||||||
load_bitstream=1
|
load_bitstream=1
|
||||||
board_host="192.168.1.52"
|
board_host="192.168.1.52"
|
||||||
|
fw_type="runtime"
|
||||||
|
|
||||||
while getopts "h:id:o:l" opt; do
|
while getopts "h:id:o:lt:" opt; do
|
||||||
case "$opt" in
|
case "$opt" in
|
||||||
\?) exit 1
|
\?) exit 1
|
||||||
;;
|
;;
|
||||||
|
@ -36,6 +39,8 @@ while getopts "h:id:o:l" opt; do
|
||||||
;;
|
;;
|
||||||
b) board_host=$OPTARG
|
b) board_host=$OPTARG
|
||||||
;;
|
;;
|
||||||
|
t) fw_type=$OPTARG
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -46,17 +51,17 @@ echo "Creating $target_folder..."
|
||||||
ssh $sshopts $target_host "mkdir -p $target_folder"
|
ssh $sshopts $target_host "mkdir -p $target_folder"
|
||||||
echo "Copying files..."
|
echo "Copying files..."
|
||||||
rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder
|
rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder
|
||||||
rsync -e "ssh $sshopts" -Lc $SZL $target_host:$target_folder
|
rsync -e "ssh $sshopts" -Lc $SZL/szl-zc706.elf $target_host:$target_folder/szl.elf
|
||||||
if [ $impure -eq 1 ]; then
|
if [ $impure -eq 1 ]; then
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g build/gateware/top.bit"
|
load_bitstream_cmd="-g build/gateware/top.bit"
|
||||||
fi
|
fi
|
||||||
firmware="build/runtime.bin"
|
firmware="build/$fw_type.bin"
|
||||||
else
|
else
|
||||||
if [ $load_bitstream -eq 1 ]; then
|
if [ $load_bitstream -eq 1 ]; then
|
||||||
load_bitstream_cmd="-g $pure_dir/top.bit"
|
load_bitstream_cmd="-g $pure_dir/top.bit"
|
||||||
fi
|
fi
|
||||||
firmware="$pure_dir/runtime.bin"
|
firmware="$pure_dir/$fw_type.bin"
|
||||||
fi
|
fi
|
||||||
echo "Programming board..."
|
echo "Programming board..."
|
||||||
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'"
|
ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'"
|
||||||
|
|
|
@ -6,7 +6,6 @@ let
|
||||||
artiq-fast = <artiq-fast>;
|
artiq-fast = <artiq-fast>;
|
||||||
artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; };
|
artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; };
|
||||||
vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; };
|
vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; };
|
||||||
zc706-szl = (import zynq-rs).zc706-szl;
|
|
||||||
in
|
in
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "artiq-zynq-env";
|
name = "artiq-zynq-env";
|
||||||
|
@ -22,7 +21,7 @@ in
|
||||||
pkgs.openocd
|
pkgs.openocd
|
||||||
pkgs.openssh pkgs.rsync
|
pkgs.openssh pkgs.rsync
|
||||||
|
|
||||||
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ])))
|
(pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq artiq-netboot ps.jsonschema ps.pyftdi ])))
|
||||||
vivado
|
vivado
|
||||||
artiqpkgs.binutils-arm
|
artiqpkgs.binutils-arm
|
||||||
|
|
||||||
|
@ -32,5 +31,5 @@ in
|
||||||
XARGO_RUST_SRC = "${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library";
|
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";
|
CLANG_EXTRA_INCLUDE_DIR = "${pkgs.llvmPackages_9.clang-unwrapped.lib}/lib/clang/9.0.1/include";
|
||||||
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
|
OPENOCD_ZYNQ = "${zynq-rs}/openocd";
|
||||||
SZL = "${zc706-szl}/szl.elf";
|
SZL = "${(import zynq-rs).szl}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,9 +94,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db184d3fa27bc7a2344250394c0264144dfe0bc81a4401801dcb964b8dd172ad"
|
checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nb 0.1.3",
|
"nb 0.1.3",
|
||||||
"void",
|
"void",
|
||||||
|
@ -195,7 +195,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libasync"
|
name = "libasync"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"libcortex_a9",
|
"libcortex_a9",
|
||||||
|
@ -207,7 +207,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libboard_zynq"
|
name = "libboard_zynq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
@ -232,7 +232,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libconfig"
|
name = "libconfig"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core_io",
|
"core_io",
|
||||||
"fatfs",
|
"fatfs",
|
||||||
|
@ -243,7 +243,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcortex_a9"
|
name = "libcortex_a9"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"libregister",
|
"libregister",
|
||||||
|
@ -259,7 +259,7 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libregister"
|
name = "libregister"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"vcell",
|
"vcell",
|
||||||
|
@ -269,7 +269,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsupport_zynq"
|
name = "libsupport_zynq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#2c161720fa12f8b7abecaf60f77b062b08ac9bc1"
|
source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#57d8d8fbc7087863305721bcb8fdbdd13d65bd65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"compiler_builtins",
|
"compiler_builtins",
|
||||||
|
|
|
@ -3,8 +3,10 @@ members = [
|
||||||
"libc",
|
"libc",
|
||||||
"libdyld",
|
"libdyld",
|
||||||
"libdwarf",
|
"libdwarf",
|
||||||
|
"libio",
|
||||||
"libunwind",
|
"libunwind",
|
||||||
"runtime",
|
"runtime",
|
||||||
|
"satman"
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
25
src/Makefile
25
src/Makefile
|
@ -1,16 +1,19 @@
|
||||||
TARGET := zc706
|
TARGET := zc706
|
||||||
VARIANT := simple
|
GWARGS := -V nist_clock
|
||||||
|
|
||||||
all: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
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
|
mkdir -p ../build
|
||||||
python gateware/$(TARGET).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 . -print)
|
../build/firmware/armv7-none-eabihf/release/runtime: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
||||||
cd runtime && \
|
cd runtime && \
|
||||||
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||||
cargo xbuild --release \
|
cargo xbuild --release \
|
||||||
|
@ -19,3 +22,13 @@ all: ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
||||||
|
|
||||||
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
../build/runtime.bin: ../build/firmware/armv7-none-eabihf/release/runtime
|
||||||
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/runtime ../build/runtime.bin
|
||||||
|
|
||||||
|
../build/firmware/armv7-none-eabihf/release/satman: ../build/pl.rs ../build/rustc-cfg ../build/mem.rs $(shell find . -type f -print)
|
||||||
|
cd satman && \
|
||||||
|
XBUILD_SYSROOT_PATH=`pwd`/../../build/sysroot \
|
||||||
|
cargo xbuild --release \
|
||||||
|
--target-dir ../../build/firmware \
|
||||||
|
--no-default-features --features=target_$(TARGET)
|
||||||
|
|
||||||
|
../build/satman.bin: ../build/firmware/armv7-none-eabihf/release/satman
|
||||||
|
llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/satman ../build/satman.bin
|
|
@ -1,191 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.build.generic_platform import *
|
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
||||||
from migen.genlib.cdc import MultiReg
|
|
||||||
from migen_axi.integration.soc_core import SoCCore
|
|
||||||
from migen_axi.platforms import coraz7
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
from misoc.integration import cpu_interface
|
|
||||||
|
|
||||||
from artiq.gateware import rtio
|
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
|
||||||
|
|
||||||
import dma
|
|
||||||
import analyzer
|
|
||||||
import acpki
|
|
||||||
|
|
||||||
|
|
||||||
class RTIOCRG(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, rtio_internal_clk):
|
|
||||||
self.clock_sel = CSRStorage()
|
|
||||||
self.pll_reset = CSRStorage(reset=1)
|
|
||||||
self.pll_locked = CSRStatus()
|
|
||||||
self.clock_domains.cd_rtio = ClockDomain()
|
|
||||||
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
|
|
||||||
|
|
||||||
rtio_external_clk = Signal()
|
|
||||||
# user_sma_clock = platform.request("user_sma_clock")
|
|
||||||
# platform.add_period_constraint(user_sma_clock.p, 8.0)
|
|
||||||
# self.specials += Instance("IBUFDS",
|
|
||||||
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
|
|
||||||
# o_O=rtio_external_clk)
|
|
||||||
|
|
||||||
pll_locked = Signal()
|
|
||||||
rtio_clk = Signal()
|
|
||||||
rtiox4_clk = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("PLLE2_ADV",
|
|
||||||
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
|
|
||||||
|
|
||||||
p_REF_JITTER1=0.01,
|
|
||||||
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
|
|
||||||
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
|
|
||||||
# Warning: CLKINSEL=0 means CLKIN2 is selected
|
|
||||||
i_CLKINSEL=~self.clock_sel.storage,
|
|
||||||
|
|
||||||
# VCO @ 1GHz when using 125MHz input
|
|
||||||
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
|
|
||||||
i_CLKFBIN=self.cd_rtio.clk,
|
|
||||||
i_RST=self.pll_reset.storage,
|
|
||||||
|
|
||||||
o_CLKFBOUT=rtio_clk,
|
|
||||||
|
|
||||||
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
|
|
||||||
o_CLKOUT0=rtiox4_clk),
|
|
||||||
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
|
|
||||||
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
|
|
||||||
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
|
|
||||||
MultiReg(pll_locked, self.pll_locked.status)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CoraZ7(SoCCore):
|
|
||||||
def __init__(self, device_variant="10", acpki=False):
|
|
||||||
self.acpki = acpki
|
|
||||||
self.rustc_cfg = dict()
|
|
||||||
|
|
||||||
platform = coraz7.Platform(device_variant=device_variant)
|
|
||||||
platform.toolchain.bitstream_commands.extend([
|
|
||||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
|
||||||
])
|
|
||||||
ident = self.__class__.__name__
|
|
||||||
if self.acpki:
|
|
||||||
ident = "acpki_" + ident
|
|
||||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
|
||||||
|
|
||||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
|
||||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
|
||||||
|
|
||||||
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
|
|
||||||
self.csr_devices.append("rtio_crg")
|
|
||||||
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
|
||||||
self.platform.add_false_path_constraints(
|
|
||||||
self.ps7.cd_sys.clk,
|
|
||||||
self.rtio_crg.cd_rtio.clk)
|
|
||||||
|
|
||||||
def add_rtio(self, rtio_channels):
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
|
||||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_core")
|
|
||||||
|
|
||||||
if self.acpki:
|
|
||||||
self.rustc_cfg["ki_impl"] = "acp"
|
|
||||||
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
|
|
||||||
bus=self.ps7.s_axi_acp,
|
|
||||||
user=self.ps7.s_axi_acp_user,
|
|
||||||
evento=self.ps7.event.o)
|
|
||||||
self.csr_devices.append("rtio")
|
|
||||||
else:
|
|
||||||
self.rustc_cfg["ki_impl"] = "csr"
|
|
||||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
|
||||||
self.csr_devices.append("rtio")
|
|
||||||
|
|
||||||
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
|
|
||||||
self.csr_devices.append("rtio_dma")
|
|
||||||
|
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
|
||||||
[self.rtio.cri, self.rtio_dma.cri],
|
|
||||||
[self.rtio_core.cri])
|
|
||||||
self.csr_devices.append("cri_con")
|
|
||||||
|
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_moninj")
|
|
||||||
|
|
||||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
|
||||||
self.ps7.s_axi_hp1)
|
|
||||||
self.csr_devices.append("rtio_analyzer")
|
|
||||||
|
|
||||||
|
|
||||||
class Simple(CoraZ7):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
CoraZ7.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
rtio_channels = []
|
|
||||||
for i in range(2):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
|
||||||
rtio_channels.append(rtio.LogChannel())
|
|
||||||
|
|
||||||
self.add_rtio(rtio_channels)
|
|
||||||
|
|
||||||
|
|
||||||
def write_csr_file(soc, filename):
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
f.write(cpu_interface.get_csr_rust(
|
|
||||||
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
|
|
||||||
|
|
||||||
|
|
||||||
def write_rustc_cfg_file(soc, filename):
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
|
|
||||||
if v is None:
|
|
||||||
f.write("{}\n".format(k))
|
|
||||||
else:
|
|
||||||
f.write("{}=\"{}\"\n".format(k, v))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="ARTIQ port to the Cora Z7 Zynq development kit")
|
|
||||||
parser.add_argument("-r", default=None,
|
|
||||||
help="build Rust interface into the specified file")
|
|
||||||
parser.add_argument("-c", default=None,
|
|
||||||
help="build Rust compiler configuration into the specified file")
|
|
||||||
parser.add_argument("-g", default=None,
|
|
||||||
help="build gateware into the specified directory")
|
|
||||||
parser.add_argument("-V", "--variant", default="10",
|
|
||||||
help="variant: "
|
|
||||||
"[acpki_]10/07s "
|
|
||||||
"(default: %(default)s)")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
variant = args.variant.lower()
|
|
||||||
acpki = variant.startswith("acpki_")
|
|
||||||
if acpki:
|
|
||||||
variant = variant[6:]
|
|
||||||
try:
|
|
||||||
soc = Simple(device_variant=variant, acpki=acpki)
|
|
||||||
except KeyError:
|
|
||||||
raise SystemExit("Invalid variant (-V/--variant)")
|
|
||||||
soc.finalize()
|
|
||||||
|
|
||||||
if args.r is not None:
|
|
||||||
write_csr_file(soc, args.r)
|
|
||||||
if args.c is not None:
|
|
||||||
write_rustc_cfg_file(soc, args.c)
|
|
||||||
if args.g is not None:
|
|
||||||
soc.build(build_dir=args.g)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -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
|
|
@ -0,0 +1,526 @@
|
||||||
|
#!/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 GenericStandalone(SoCCore):
|
||||||
|
def __init__(self, description, acpki=False):
|
||||||
|
self.acpki = acpki
|
||||||
|
self.rustc_cfg = dict()
|
||||||
|
|
||||||
|
platform = kasli_soc.Platform()
|
||||||
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||||
|
])
|
||||||
|
ident = self.__class__.__name__
|
||||||
|
if self.acpki:
|
||||||
|
ident = "acpki_" + ident
|
||||||
|
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||||
|
|
||||||
|
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||||
|
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||||
|
|
||||||
|
self.rustc_cfg["has_si5324"] = None
|
||||||
|
self.rustc_cfg["si5324_soft_reset"] = None
|
||||||
|
|
||||||
|
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||||
|
self.submodules.rtio_crg = RTIOCRG(self.platform)
|
||||||
|
self.csr_devices.append("rtio_crg")
|
||||||
|
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
||||||
|
self.platform.add_false_path_constraints(
|
||||||
|
self.ps7.cd_sys.clk,
|
||||||
|
self.rtio_crg.cd_rtio.clk)
|
||||||
|
|
||||||
|
self.rtio_channels = []
|
||||||
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
|
if has_grabber:
|
||||||
|
self.grabber_csr_group = []
|
||||||
|
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
|
||||||
|
for i in (0, 1):
|
||||||
|
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||||
|
user_led = self.platform.request("user_led", i)
|
||||||
|
phy = ttl_simple.Output(user_led)
|
||||||
|
self.submodules += phy
|
||||||
|
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||||
|
self.rtio_channels.append(rtio.LogChannel())
|
||||||
|
|
||||||
|
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
||||||
|
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
|
||||||
|
self.csr_devices.append("rtio_core")
|
||||||
|
|
||||||
|
if self.acpki:
|
||||||
|
self.rustc_cfg["ki_impl"] = "acp"
|
||||||
|
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
|
||||||
|
bus=self.ps7.s_axi_acp,
|
||||||
|
user=self.ps7.s_axi_acp_user,
|
||||||
|
evento=self.ps7.event.o)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
else:
|
||||||
|
self.rustc_cfg["ki_impl"] = "csr"
|
||||||
|
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
|
||||||
|
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
|
||||||
|
self.csr_devices.append("rtio_dma")
|
||||||
|
|
||||||
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
|
[self.rtio.cri, self.rtio_dma.cri],
|
||||||
|
[self.rtio_core.cri])
|
||||||
|
self.csr_devices.append("cri_con")
|
||||||
|
|
||||||
|
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
|
||||||
|
self.csr_devices.append("rtio_moninj")
|
||||||
|
|
||||||
|
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
||||||
|
self.ps7.s_axi_hp1)
|
||||||
|
self.csr_devices.append("rtio_analyzer")
|
||||||
|
|
||||||
|
if has_grabber:
|
||||||
|
self.rustc_cfg["has_grabber"] = None
|
||||||
|
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||||
|
for grabber in self.grabber_csr_group:
|
||||||
|
self.platform.add_false_path_constraints(
|
||||||
|
self.rtio_crg.cd_rtio.clk, getattr(self, grabber).deserializer.cd_cl.clk)
|
||||||
|
|
||||||
|
|
||||||
|
class GenericMaster(SoCCore):
|
||||||
|
def __init__(self, description, acpki=False):
|
||||||
|
sys_clk_freq = 125e6
|
||||||
|
rtio_clk_freq = description["rtio_frequency"]
|
||||||
|
|
||||||
|
self.acpki = acpki
|
||||||
|
self.rustc_cfg = dict()
|
||||||
|
|
||||||
|
platform = kasli_soc.Platform()
|
||||||
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||||
|
])
|
||||||
|
ident = self.__class__.__name__
|
||||||
|
if self.acpki:
|
||||||
|
ident = "acpki_" + ident
|
||||||
|
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||||
|
|
||||||
|
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||||
|
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||||
|
|
||||||
|
data_pads = [platform.request("sfp", i) for i in range(4)]
|
||||||
|
|
||||||
|
self.submodules.drtio_transceiver = gtx_7series.GTX(
|
||||||
|
clock_pads=platform.request("clk125_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.rtio_channels = []
|
||||||
|
has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"])
|
||||||
|
if has_grabber:
|
||||||
|
self.grabber_csr_group = []
|
||||||
|
eem_7series.add_peripherals(self, description["peripherals"], iostandard=eem_iostandard)
|
||||||
|
for i in (0, 1):
|
||||||
|
print("USER LED at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||||
|
user_led = self.platform.request("user_led", i)
|
||||||
|
phy = ttl_simple.Output(user_led)
|
||||||
|
self.submodules += phy
|
||||||
|
self.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||||
|
self.rtio_channels.append(rtio.LogChannel())
|
||||||
|
|
||||||
|
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
||||||
|
|
||||||
|
drtio_csr_group = []
|
||||||
|
drtioaux_csr_group = []
|
||||||
|
drtioaux_memory_group = []
|
||||||
|
self.drtio_cri = []
|
||||||
|
for i in range(len(self.drtio_transceiver.channels)):
|
||||||
|
core_name = "drtio" + str(i)
|
||||||
|
coreaux_name = "drtioaux" + str(i)
|
||||||
|
memory_name = "drtioaux" + str(i) + "_mem"
|
||||||
|
drtio_csr_group.append(core_name)
|
||||||
|
drtioaux_csr_group.append(coreaux_name)
|
||||||
|
drtioaux_memory_group.append(memory_name)
|
||||||
|
|
||||||
|
cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)})
|
||||||
|
|
||||||
|
core = cdr(DRTIOMaster(self.rtio_tsc, self.drtio_transceiver.channels[i]))
|
||||||
|
setattr(self.submodules, core_name, core)
|
||||||
|
self.drtio_cri.append(core.cri)
|
||||||
|
self.csr_devices.append(core_name)
|
||||||
|
|
||||||
|
coreaux = cdr(drtio_aux_controller.DRTIOAuxControllerBare(core.link_layer))
|
||||||
|
setattr(self.submodules, coreaux_name, coreaux)
|
||||||
|
self.csr_devices.append(coreaux_name)
|
||||||
|
|
||||||
|
size = coreaux.get_mem_size()
|
||||||
|
memory_address = self.axi2csr.register_port(coreaux.get_tx_port(), size)
|
||||||
|
self.axi2csr.register_port(coreaux.get_rx_port(), size)
|
||||||
|
self.add_memory_region(memory_name, self.mem_map["csr"] + memory_address, size * 2)
|
||||||
|
self.rustc_cfg["has_drtio"] = None
|
||||||
|
self.rustc_cfg["has_drtio_routing"] = None
|
||||||
|
self.add_csr_group("drtio", drtio_csr_group)
|
||||||
|
self.add_csr_group("drtioaux", drtioaux_csr_group)
|
||||||
|
self.add_memory_group("drtioaux_mem", drtioaux_memory_group)
|
||||||
|
|
||||||
|
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, self.rtio_channels)
|
||||||
|
self.csr_devices.append("rtio_core")
|
||||||
|
|
||||||
|
if self.acpki:
|
||||||
|
self.rustc_cfg["ki_impl"] = "acp"
|
||||||
|
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
|
||||||
|
bus=self.ps7.s_axi_acp,
|
||||||
|
user=self.ps7.s_axi_acp_user,
|
||||||
|
evento=self.ps7.event.o)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
else:
|
||||||
|
self.rustc_cfg["ki_impl"] = "csr"
|
||||||
|
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
|
||||||
|
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
|
||||||
|
self.csr_devices.append("rtio_dma")
|
||||||
|
|
||||||
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
|
[self.rtio.cri, self.rtio_dma.cri],
|
||||||
|
[self.rtio_core.cri] + self.drtio_cri,
|
||||||
|
enable_routing=True)
|
||||||
|
self.csr_devices.append("cri_con")
|
||||||
|
|
||||||
|
self.submodules.rtio_moninj = rtio.MonInj(self.rtio_channels)
|
||||||
|
self.csr_devices.append("rtio_moninj")
|
||||||
|
|
||||||
|
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
||||||
|
self.csr_devices.append("routing_table")
|
||||||
|
|
||||||
|
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
||||||
|
self.ps7.s_axi_hp1)
|
||||||
|
self.csr_devices.append("rtio_analyzer")
|
||||||
|
|
||||||
|
if has_grabber:
|
||||||
|
self.rustc_cfg["has_grabber"] = None
|
||||||
|
self.add_csr_group("grabber", self.grabber_csr_group)
|
||||||
|
|
||||||
|
|
||||||
|
class GenericSatellite(SoCCore):
|
||||||
|
def __init__(self, description, acpki=False):
|
||||||
|
sys_clk_freq = 125e6
|
||||||
|
rtio_clk_freq = description["rtio_frequency"]
|
||||||
|
|
||||||
|
self.acpki = acpki
|
||||||
|
self.rustc_cfg = dict()
|
||||||
|
|
||||||
|
platform = kasli_soc.Platform()
|
||||||
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||||
|
])
|
||||||
|
ident = self.__class__.__name__
|
||||||
|
if self.acpki:
|
||||||
|
ident = "acpki_" + ident
|
||||||
|
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||||
|
|
||||||
|
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||||
|
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||||
|
|
||||||
|
self.crg = self.ps7 # HACK for eem_7series to find the clock
|
||||||
|
self.submodules.rtio_crg = RTIOClockMultiplier(rtio_clk_freq)
|
||||||
|
self.csr_devices.append("rtio_crg")
|
||||||
|
|
||||||
|
data_pads = [platform.request("sfp", i) for i in range(4)]
|
||||||
|
|
||||||
|
self.submodules.drtio_transceiver = gtx_7series.GTX(
|
||||||
|
clock_pads=platform.request("clk125_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)
|
||||||
|
|
||||||
|
self.rustc_cfg["has_si5324"] = None
|
||||||
|
self.rustc_cfg["si5324_soft_reset"] = None
|
||||||
|
|
||||||
|
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()
|
|
@ -1,191 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from operator import itemgetter
|
|
||||||
|
|
||||||
from migen import *
|
|
||||||
from migen.build.generic_platform import *
|
|
||||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
|
||||||
from migen.genlib.cdc import MultiReg
|
|
||||||
from migen_axi.integration.soc_core import SoCCore
|
|
||||||
from migen_axi.platforms import redpitaya
|
|
||||||
from misoc.interconnect.csr import *
|
|
||||||
from misoc.integration import cpu_interface
|
|
||||||
|
|
||||||
from artiq.gateware import rtio
|
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
|
||||||
|
|
||||||
import dma
|
|
||||||
import analyzer
|
|
||||||
import acpki
|
|
||||||
|
|
||||||
|
|
||||||
class RTIOCRG(Module, AutoCSR):
|
|
||||||
def __init__(self, platform, rtio_internal_clk):
|
|
||||||
self.clock_sel = CSRStorage()
|
|
||||||
self.pll_reset = CSRStorage(reset=1)
|
|
||||||
self.pll_locked = CSRStatus()
|
|
||||||
self.clock_domains.cd_rtio = ClockDomain()
|
|
||||||
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
|
|
||||||
|
|
||||||
rtio_external_clk = Signal()
|
|
||||||
# user_sma_clock = platform.request("user_sma_clock")
|
|
||||||
# platform.add_period_constraint(user_sma_clock.p, 8.0)
|
|
||||||
# self.specials += Instance("IBUFDS",
|
|
||||||
# i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
|
|
||||||
# o_O=rtio_external_clk)
|
|
||||||
|
|
||||||
pll_locked = Signal()
|
|
||||||
rtio_clk = Signal()
|
|
||||||
rtiox4_clk = Signal()
|
|
||||||
self.specials += [
|
|
||||||
Instance("PLLE2_ADV",
|
|
||||||
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
|
|
||||||
|
|
||||||
p_REF_JITTER1=0.01,
|
|
||||||
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
|
|
||||||
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
|
|
||||||
# Warning: CLKINSEL=0 means CLKIN2 is selected
|
|
||||||
i_CLKINSEL=~self.clock_sel.storage,
|
|
||||||
|
|
||||||
# VCO @ 1GHz when using 125MHz input
|
|
||||||
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
|
|
||||||
i_CLKFBIN=self.cd_rtio.clk,
|
|
||||||
i_RST=self.pll_reset.storage,
|
|
||||||
|
|
||||||
o_CLKFBOUT=rtio_clk,
|
|
||||||
|
|
||||||
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
|
|
||||||
o_CLKOUT0=rtiox4_clk),
|
|
||||||
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
|
|
||||||
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
|
|
||||||
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
|
|
||||||
MultiReg(pll_locked, self.pll_locked.status)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Redpitaya(SoCCore):
|
|
||||||
def __init__(self, acpki=False):
|
|
||||||
self.acpki = acpki
|
|
||||||
self.rustc_cfg = dict()
|
|
||||||
|
|
||||||
platform = redpitaya.Platform()
|
|
||||||
platform.toolchain.bitstream_commands.extend([
|
|
||||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
|
||||||
])
|
|
||||||
ident = self.__class__.__name__
|
|
||||||
if self.acpki:
|
|
||||||
ident = "acpki_" + ident
|
|
||||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
|
||||||
|
|
||||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
|
||||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
|
||||||
|
|
||||||
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
|
|
||||||
self.csr_devices.append("rtio_crg")
|
|
||||||
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
|
||||||
self.platform.add_false_path_constraints(
|
|
||||||
self.ps7.cd_sys.clk,
|
|
||||||
self.rtio_crg.cd_rtio.clk)
|
|
||||||
|
|
||||||
def add_rtio(self, rtio_channels):
|
|
||||||
self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3)
|
|
||||||
self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_core")
|
|
||||||
|
|
||||||
if self.acpki:
|
|
||||||
self.rustc_cfg["ki_impl"] = "acp"
|
|
||||||
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
|
|
||||||
bus=self.ps7.s_axi_acp,
|
|
||||||
user=self.ps7.s_axi_acp_user,
|
|
||||||
evento=self.ps7.event.o)
|
|
||||||
self.csr_devices.append("rtio")
|
|
||||||
else:
|
|
||||||
self.rustc_cfg["ki_impl"] = "csr"
|
|
||||||
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
|
||||||
self.csr_devices.append("rtio")
|
|
||||||
|
|
||||||
self.submodules.rtio_dma = dma.DMA(self.ps7.s_axi_hp0)
|
|
||||||
self.csr_devices.append("rtio_dma")
|
|
||||||
|
|
||||||
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
|
||||||
[self.rtio.cri, self.rtio_dma.cri],
|
|
||||||
[self.rtio_core.cri])
|
|
||||||
self.csr_devices.append("cri_con")
|
|
||||||
|
|
||||||
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
|
||||||
self.csr_devices.append("rtio_moninj")
|
|
||||||
|
|
||||||
self.submodules.rtio_analyzer = analyzer.Analyzer(self.rtio_tsc, self.rtio_core.cri,
|
|
||||||
self.ps7.s_axi_hp1)
|
|
||||||
self.csr_devices.append("rtio_analyzer")
|
|
||||||
|
|
||||||
|
|
||||||
class Simple(Redpitaya):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
Redpitaya.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
|
||||||
|
|
||||||
rtio_channels = []
|
|
||||||
for i in range(2):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
|
||||||
rtio_channels.append(rtio.LogChannel())
|
|
||||||
|
|
||||||
self.add_rtio(rtio_channels)
|
|
||||||
|
|
||||||
|
|
||||||
VARIANTS = {cls.__name__.lower(): cls for cls in [Simple]}
|
|
||||||
|
|
||||||
|
|
||||||
def write_csr_file(soc, filename):
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
f.write(cpu_interface.get_csr_rust(
|
|
||||||
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
|
|
||||||
|
|
||||||
|
|
||||||
def write_rustc_cfg_file(soc, filename):
|
|
||||||
with open(filename, "w") as f:
|
|
||||||
for k, v in sorted(soc.rustc_cfg.items(), key=itemgetter(0)):
|
|
||||||
if v is None:
|
|
||||||
f.write("{}\n".format(k))
|
|
||||||
else:
|
|
||||||
f.write("{}=\"{}\"\n".format(k, v))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description="ARTIQ port to the Redpitaya Zynq development kit")
|
|
||||||
parser.add_argument("-r", default=None,
|
|
||||||
help="build Rust interface into the specified file")
|
|
||||||
parser.add_argument("-c", default=None,
|
|
||||||
help="build Rust compiler configuration into the specified file")
|
|
||||||
parser.add_argument("-g", default=None,
|
|
||||||
help="build gateware into the specified directory")
|
|
||||||
parser.add_argument("-V", "--variant", default="10",
|
|
||||||
help="variant: "
|
|
||||||
"[acpki_]simple "
|
|
||||||
"(default: %(default)s)")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
variant = args.variant.lower()
|
|
||||||
acpki = variant.startswith("acpki_")
|
|
||||||
if acpki:
|
|
||||||
variant = variant[6:]
|
|
||||||
soc = Simple(acpki=acpki)
|
|
||||||
soc.finalize()
|
|
||||||
|
|
||||||
if args.r is not None:
|
|
||||||
write_csr_file(soc, args.r)
|
|
||||||
if args.c is not None:
|
|
||||||
write_rustc_cfg_file(soc, args.c)
|
|
||||||
if args.g is not None:
|
|
||||||
soc.build(build_dir=args.g)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -11,13 +11,20 @@ from migen_axi.integration.soc_core import SoCCore
|
||||||
from migen_axi.platforms import zc706
|
from migen_axi.platforms import zc706
|
||||||
from misoc.interconnect.csr import *
|
from misoc.interconnect.csr import *
|
||||||
from misoc.integration import cpu_interface
|
from misoc.integration import cpu_interface
|
||||||
|
from misoc.cores import gpio
|
||||||
|
|
||||||
from artiq.gateware import rtio, nist_clock, nist_qc2
|
from artiq.gateware import rtio, nist_clock, nist_qc2
|
||||||
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds, spi2
|
||||||
|
from artiq.gateware.rtio.xilinx_clocking import RTIOClockMultiplier, fix_serdes_timing_path
|
||||||
|
from artiq.gateware.drtio.transceiver import gtx_7series
|
||||||
|
from artiq.gateware.drtio.siphaser import SiPhaser7Series
|
||||||
|
from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer
|
||||||
|
from artiq.gateware.drtio import *
|
||||||
|
|
||||||
import dma
|
import dma
|
||||||
import analyzer
|
import analyzer
|
||||||
import acpki
|
import acpki
|
||||||
|
import drtio_aux_controller
|
||||||
|
|
||||||
|
|
||||||
class RTIOCRG(Module, AutoCSR):
|
class RTIOCRG(Module, AutoCSR):
|
||||||
|
@ -64,25 +71,77 @@ class RTIOCRG(Module, AutoCSR):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# The NIST backplanes require setting VADJ to 3.3V by reprogramming the power supply.
|
||||||
|
# This also changes the I/O standard for some on-board LEDs.
|
||||||
|
leds_fmc33 = [
|
||||||
|
("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
|
||||||
|
("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
|
||||||
|
]
|
||||||
|
|
||||||
|
# same deal as with LEDs - changed I/O standard.
|
||||||
|
si5324_fmc33 = [
|
||||||
|
("si5324_33", 0,
|
||||||
|
Subsignal("rst_n", Pins("W23"), IOStandard("LVCMOS33")),
|
||||||
|
Subsignal("int", Pins("AJ25"), IOStandard("LVCMOS33"))
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
pmod1_33 = [
|
||||||
|
("pmod1_33", 0, Pins("AJ21"), IOStandard("LVCMOS33")),
|
||||||
|
("pmod1_33", 1, Pins("AK21"), IOStandard("LVCMOS33")),
|
||||||
|
("pmod1_33", 2, Pins("AB21"), IOStandard("LVCMOS33")),
|
||||||
|
("pmod1_33", 3, Pins("AB16"), IOStandard("LVCMOS33")),
|
||||||
|
# rest removed for use with dummy spi
|
||||||
|
]
|
||||||
|
|
||||||
|
_ams101_dac = [
|
||||||
|
("ams101_dac", 0,
|
||||||
|
Subsignal("ldac", Pins("XADC:GPIO0")),
|
||||||
|
Subsignal("clk", Pins("XADC:GPIO1")),
|
||||||
|
Subsignal("mosi", Pins("XADC:GPIO2")),
|
||||||
|
Subsignal("cs_n", Pins("XADC:GPIO3")),
|
||||||
|
IOStandard("LVCMOS15")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
_pmod_spi = [
|
||||||
|
("pmod_spi", 0,
|
||||||
|
# PMOD_1 4-7 pins, same bank as sfp_tx_disable or user_sma_clock
|
||||||
|
Subsignal("miso", Pins("Y20"), IOStandard("LVCMOS25")),
|
||||||
|
Subsignal("clk", Pins("AA20"), IOStandard("LVCMOS25")),
|
||||||
|
Subsignal("mosi", Pins("AC18"), IOStandard("LVCMOS25")),
|
||||||
|
Subsignal("cs_n", Pins("AC19"), IOStandard("LVCMOS25")),
|
||||||
|
IOStandard("LVCMOS25")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_zc706_platform(platform):
|
||||||
|
platform.toolchain.bitstream_commands.extend([
|
||||||
|
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
||||||
|
])
|
||||||
|
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
||||||
|
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
||||||
|
|
||||||
|
|
||||||
class ZC706(SoCCore):
|
class ZC706(SoCCore):
|
||||||
def __init__(self, acpki=False):
|
def __init__(self, acpki=False):
|
||||||
self.acpki = acpki
|
self.acpki = acpki
|
||||||
self.rustc_cfg = dict()
|
self.rustc_cfg = dict()
|
||||||
|
|
||||||
platform = zc706.Platform()
|
platform = zc706.Platform()
|
||||||
platform.toolchain.bitstream_commands.extend([
|
prepare_zc706_platform(platform)
|
||||||
"set_property BITSTREAM.GENERAL.COMPRESS True [current_design]",
|
|
||||||
])
|
|
||||||
ident = self.__class__.__name__
|
ident = self.__class__.__name__
|
||||||
if self.acpki:
|
if self.acpki:
|
||||||
ident = "acpki_" + ident
|
ident = "acpki_" + ident
|
||||||
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
SoCCore.__init__(self, platform=platform, csr_data_width=32, ident=ident)
|
||||||
|
|
||||||
platform.add_platform_command("create_clock -name clk_fpga_0 -period 8 [get_pins \"PS7/FCLKCLK[0]\"]")
|
|
||||||
platform.add_platform_command("set_input_jitter clk_fpga_0 0.24")
|
|
||||||
|
|
||||||
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
|
self.submodules.rtio_crg = RTIOCRG(self.platform, self.ps7.cd_sys.clk)
|
||||||
self.csr_devices.append("rtio_crg")
|
self.csr_devices.append("rtio_crg")
|
||||||
|
self.rustc_cfg["has_rtio_crg_clock_sel"] = None
|
||||||
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
self.platform.add_period_constraint(self.rtio_crg.cd_rtio.clk, 8.)
|
||||||
self.platform.add_false_path_constraints(
|
self.platform.add_false_path_constraints(
|
||||||
self.ps7.cd_sys.clk,
|
self.ps7.cd_sys.clk,
|
||||||
|
@ -121,52 +180,301 @@ class ZC706(SoCCore):
|
||||||
self.csr_devices.append("rtio_analyzer")
|
self.csr_devices.append("rtio_analyzer")
|
||||||
|
|
||||||
|
|
||||||
class Simple(ZC706):
|
class _MasterBase(SoCCore):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, acpki=False, drtio100mhz=False):
|
||||||
ZC706.__init__(self, **kwargs)
|
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
|
platform = self.platform
|
||||||
|
|
||||||
rtio_channels = []
|
self.comb += platform.request("sfp_tx_disable_n").eq(1)
|
||||||
for i in range(4):
|
data_pads = [
|
||||||
phy = ttl_simple.Output(platform.request("user_led", i))
|
platform.request("sfp"),
|
||||||
self.submodules += phy
|
platform.request("user_sma_mgt")
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
]
|
||||||
|
|
||||||
self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
|
# 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock
|
||||||
rtio_channels.append(rtio.LogChannel())
|
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.add_rtio(rtio_channels)
|
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.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
||||||
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
|
[self.rtio.cri, self.rtio_dma.cri],
|
||||||
|
[self.local_io.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.
|
class _SatelliteBase(SoCCore):
|
||||||
# This also changes the I/O standard for some on-board LEDs.
|
def __init__(self, acpki=False, drtio100mhz=False):
|
||||||
leds_fmc33 = [
|
self.acpki = acpki
|
||||||
("user_led_33", 0, Pins("Y21"), IOStandard("LVCMOS33")),
|
self.rustc_cfg = dict()
|
||||||
("user_led_33", 1, Pins("G2"), IOStandard("LVCMOS15")),
|
|
||||||
("user_led_33", 2, Pins("W21"), IOStandard("LVCMOS33")),
|
platform = zc706.Platform()
|
||||||
("user_led_33", 3, Pins("A17"), IOStandard("LVCMOS15")),
|
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")
|
||||||
|
fix_serdes_timing_path(self.platform)
|
||||||
|
|
||||||
|
def add_rtio(self, rtio_channels):
|
||||||
|
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
|
||||||
|
self.csr_devices.append("rtio_moninj")
|
||||||
|
|
||||||
|
if self.acpki:
|
||||||
|
self.rustc_cfg["ki_impl"] = "acp"
|
||||||
|
self.submodules.rtio = acpki.KernelInitiator(self.rtio_tsc,
|
||||||
|
bus=self.ps7.s_axi_acp,
|
||||||
|
user=self.ps7.s_axi_acp_user,
|
||||||
|
evento=self.ps7.event.o)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
else:
|
||||||
|
self.rustc_cfg["ki_impl"] = "csr"
|
||||||
|
self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc, now64=True)
|
||||||
|
self.csr_devices.append("rtio")
|
||||||
|
|
||||||
|
self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels)
|
||||||
|
self.submodules.cri_con = rtio.CRIInterconnectShared(
|
||||||
|
[self.drtiosat.cri],
|
||||||
|
[self.local_io.cri] + self.drtio_cri,
|
||||||
|
mode="sync", enable_routing=True)
|
||||||
|
self.csr_devices.append("cri_con")
|
||||||
|
|
||||||
|
self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con)
|
||||||
|
self.csr_devices.append("routing_table")
|
||||||
|
|
||||||
|
|
||||||
class NIST_CLOCK(ZC706):
|
|
||||||
|
class _NIST_CLOCK_RTIO:
|
||||||
"""
|
"""
|
||||||
NIST clock hardware, with old backplane and 11 DDS channels
|
NIST clock hardware, with old backplane and 11 DDS channels
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self):
|
||||||
ZC706.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
platform = self.platform
|
||||||
platform.add_extension(nist_clock.fmc_adapter_io)
|
platform.add_extension(nist_clock.fmc_adapter_io)
|
||||||
platform.add_extension(leds_fmc33)
|
platform.add_extension(leds_fmc33)
|
||||||
|
platform.add_extension(pmod1_33)
|
||||||
|
platform.add_extension(_ams101_dac)
|
||||||
|
platform.add_extension(_pmod_spi)
|
||||||
|
|
||||||
rtio_channels = []
|
rtio_channels = []
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led_33", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
if i % 4 == 3:
|
if i % 4 == 3:
|
||||||
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
||||||
|
@ -182,16 +490,40 @@ class NIST_CLOCK(ZC706):
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||||
|
|
||||||
|
# no SMA GPIO, replaced with PMOD1_0
|
||||||
|
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||||
|
|
||||||
|
phy = ttl_simple.Output(platform.request("user_led_33", 0))
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
|
ams101_dac = self.platform.request("ams101_dac", 0)
|
||||||
|
phy = ttl_simple.Output(ams101_dac.ldac)
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
phy = ttl_simple.ClockGen(platform.request("la32_p"))
|
phy = ttl_simple.ClockGen(platform.request("la32_p"))
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
|
phy = spi2.SPIMaster(ams101_dac)
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(
|
||||||
|
phy, ififo_depth=4))
|
||||||
|
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(
|
rtio_channels.append(rtio.Channel.from_phy(
|
||||||
phy, ififo_depth=128))
|
phy, ififo_depth=128))
|
||||||
|
|
||||||
|
# no SDIO on PL side, dummy SPI placeholder instead
|
||||||
|
phy = spi2.SPIMaster(platform.request("pmod_spi"))
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
|
||||||
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
|
phy = dds.AD9914(platform.request("dds"), 11, onehot=True)
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||||
|
@ -202,37 +534,51 @@ class NIST_CLOCK(ZC706):
|
||||||
self.add_rtio(rtio_channels)
|
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
|
NIST QC2 hardware, as used in Quantum I and Quantum II, with new backplane
|
||||||
and 24 DDS channels. Two backplanes are used.
|
and 24 DDS channels. Two backplanes are used.
|
||||||
"""
|
"""
|
||||||
def __init__(self, **kwargs):
|
def __init__(self):
|
||||||
ZC706.__init__(self, **kwargs)
|
|
||||||
|
|
||||||
platform = self.platform
|
platform = self.platform
|
||||||
platform.add_extension(nist_qc2.fmc_adapter_io)
|
platform.add_extension(nist_qc2.fmc_adapter_io)
|
||||||
platform.add_extension(leds_fmc33)
|
platform.add_extension(leds_fmc33)
|
||||||
|
platform.add_extension(_ams101_dac)
|
||||||
|
platform.add_extension(pmod1_33)
|
||||||
|
|
||||||
rtio_channels = []
|
rtio_channels = []
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
phy = ttl_simple.Output(platform.request("user_led_33", i))
|
|
||||||
self.submodules += phy
|
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
|
||||||
|
|
||||||
# All TTL channels are In+Out capable
|
# All TTL channels are In+Out capable
|
||||||
for i in range(40):
|
for i in range(40):
|
||||||
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
phy = ttl_serdes_7series.InOut_8X(platform.request("ttl", i))
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||||
|
|
||||||
|
# no SMA GPIO, replaced with PMOD1_0
|
||||||
|
phy = ttl_serdes_7series.InOut_8X(platform.request("pmod1_33", 0))
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
|
||||||
|
|
||||||
|
phy = ttl_simple.Output(platform.request("user_led_33", 0))
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
|
ams101_dac = self.platform.request("ams101_dac", 0)
|
||||||
|
phy = ttl_simple.Output(ams101_dac.ldac)
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
# CLK0, CLK1 are for clock generators, on backplane SMP connectors
|
# CLK0, CLK1 are for clock generators, on backplane SMP connectors
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
phy = ttl_simple.ClockGen(
|
phy = ttl_simple.ClockGen(
|
||||||
platform.request("clkout", i))
|
platform.request("clkout", i))
|
||||||
self.submodules += phy
|
self.submodules += phy
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
|
phy = spi2.SPIMaster(ams101_dac)
|
||||||
|
self.submodules += phy
|
||||||
|
rtio_channels.append(rtio.Channel.from_phy(
|
||||||
|
phy, ififo_depth=4))
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
phy = spi2.SPIMaster(self.platform.request("spi", i))
|
||||||
|
@ -252,7 +598,38 @@ class NIST_QC2(ZC706):
|
||||||
self.add_rtio(rtio_channels)
|
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):
|
def write_csr_file(soc, filename):
|
||||||
|
@ -260,6 +637,11 @@ def write_csr_file(soc, filename):
|
||||||
f.write(cpu_interface.get_csr_rust(
|
f.write(cpu_interface.get_csr_rust(
|
||||||
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
|
soc.get_csr_regions(), soc.get_csr_groups(), soc.get_constants()))
|
||||||
|
|
||||||
|
def write_mem_file(soc, filename):
|
||||||
|
with open(filename, "w") as f:
|
||||||
|
f.write(cpu_interface.get_mem_rust(
|
||||||
|
soc.get_memory_regions(), soc.get_memory_groups(), None))
|
||||||
|
|
||||||
|
|
||||||
def write_rustc_cfg_file(soc, filename):
|
def write_rustc_cfg_file(soc, filename):
|
||||||
with open(filename, "w") as f:
|
with open(filename, "w") as f:
|
||||||
|
@ -275,13 +657,15 @@ def main():
|
||||||
description="ARTIQ port to the ZC706 Zynq development kit")
|
description="ARTIQ port to the ZC706 Zynq development kit")
|
||||||
parser.add_argument("-r", default=None,
|
parser.add_argument("-r", default=None,
|
||||||
help="build Rust interface into the specified file")
|
help="build Rust interface into the specified file")
|
||||||
|
parser.add_argument("-m", default=None,
|
||||||
|
help="build Rust memory interface into the specified file")
|
||||||
parser.add_argument("-c", default=None,
|
parser.add_argument("-c", default=None,
|
||||||
help="build Rust compiler configuration into the specified file")
|
help="build Rust compiler configuration into the specified file")
|
||||||
parser.add_argument("-g", default=None,
|
parser.add_argument("-g", default=None,
|
||||||
help="build gateware into the specified directory")
|
help="build gateware into the specified directory")
|
||||||
parser.add_argument("-V", "--variant", default="simple",
|
parser.add_argument("-V", "--variant", default="nist_clock",
|
||||||
help="variant: "
|
help="variant: "
|
||||||
"[acpki_]simple/nist_clock/nist_qc2 "
|
"[acpki_]nist_clock/nist_qc2[_master/_satellite][_100mhz]"
|
||||||
"(default: %(default)s)")
|
"(default: %(default)s)")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -289,21 +673,25 @@ def main():
|
||||||
acpki = variant.startswith("acpki_")
|
acpki = variant.startswith("acpki_")
|
||||||
if acpki:
|
if acpki:
|
||||||
variant = variant[6:]
|
variant = variant[6:]
|
||||||
|
drtio100mhz = variant.endswith("_100mhz")
|
||||||
|
if drtio100mhz:
|
||||||
|
variant = variant[:-7]
|
||||||
try:
|
try:
|
||||||
cls = VARIANTS[variant]
|
cls = VARIANTS[variant]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise SystemExit("Invalid variant (-V/--variant)")
|
raise SystemExit("Invalid variant (-V/--variant)")
|
||||||
|
|
||||||
soc = cls(acpki=acpki)
|
soc = cls(acpki=acpki, drtio100mhz=drtio100mhz)
|
||||||
soc.finalize()
|
soc.finalize()
|
||||||
|
|
||||||
if args.r is not None:
|
if args.r is not None:
|
||||||
write_csr_file(soc, args.r)
|
write_csr_file(soc, args.r)
|
||||||
|
if args.m is not None:
|
||||||
|
write_mem_file(soc, args.m)
|
||||||
if args.c is not None:
|
if args.c is not None:
|
||||||
write_rustc_cfg_file(soc, args.c)
|
write_rustc_cfg_file(soc, args.c)
|
||||||
if args.g is not None:
|
if args.g is not None:
|
||||||
soc.build(build_dir=args.g)
|
soc.build(build_dir=args.g)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "libboard_artiq"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "libboard_artiq"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
target_zc706 = []
|
||||||
|
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"}
|
||||||
|
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" }
|
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate build_zynq;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_zynq::cfg();
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
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 routing table is incorrect, using default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
warn!("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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
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 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 u16, dst: *mut u16, 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/2)).step_by(2) {
|
||||||
|
*dst.offset(i) = *src.offset(i);
|
||||||
|
*dst.offset(i+1) = *src.offset(i+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 u16;
|
||||||
|
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 u16, 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 u16;
|
||||||
|
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 u16, 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)?;
|
||||||
|
|
||||||
|
let padding = 4 - (writer.position() % 4);
|
||||||
|
if padding != 4 {
|
||||||
|
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())
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
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 u16;
|
||||||
|
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 u16, 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 u16;
|
||||||
|
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 u16, 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)?;
|
||||||
|
|
||||||
|
let padding = 4 - (writer.position() % 4);
|
||||||
|
if padding != 4 {
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,339 @@
|
||||||
|
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: u32 },
|
||||||
|
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 },
|
||||||
|
|
||||||
|
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_u32()?
|
||||||
|
},
|
||||||
|
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()?
|
||||||
|
},
|
||||||
|
|
||||||
|
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_u32(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::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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -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_ref: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Input {
|
||||||
|
Ckin1,
|
||||||
|
Ckin2,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_frequency_settings(settings: &FrequencySettings) -> Result<FrequencySettings> {
|
||||||
|
if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 {
|
||||||
|
return Err("NC1_LS must be 0 or even")
|
||||||
|
}
|
||||||
|
if settings.nc1_ls > (1 << 20) {
|
||||||
|
return Err("NC1_LS is too high")
|
||||||
|
}
|
||||||
|
if (settings.n2_ls % 2) == 1 {
|
||||||
|
return Err("N2_LS must be even")
|
||||||
|
}
|
||||||
|
if settings.n2_ls > (1 << 20) {
|
||||||
|
return Err("N2_LS is too high")
|
||||||
|
}
|
||||||
|
if settings.n31 > (1 << 19) {
|
||||||
|
return Err("N31 is too high")
|
||||||
|
}
|
||||||
|
if settings.n32 > (1 << 19) {
|
||||||
|
return Err("N32 is too high")
|
||||||
|
}
|
||||||
|
let r = FrequencySettings {
|
||||||
|
n1_hs: match settings.n1_hs {
|
||||||
|
4 => 0b000,
|
||||||
|
5 => 0b001,
|
||||||
|
6 => 0b010,
|
||||||
|
7 => 0b011,
|
||||||
|
8 => 0b100,
|
||||||
|
9 => 0b101,
|
||||||
|
10 => 0b110,
|
||||||
|
11 => 0b111,
|
||||||
|
_ => return Err("N1_HS has an invalid value")
|
||||||
|
},
|
||||||
|
nc1_ls: settings.nc1_ls - 1,
|
||||||
|
n2_hs: match settings.n2_hs {
|
||||||
|
4 => 0b000,
|
||||||
|
5 => 0b001,
|
||||||
|
6 => 0b010,
|
||||||
|
7 => 0b011,
|
||||||
|
8 => 0b100,
|
||||||
|
9 => 0b101,
|
||||||
|
10 => 0b110,
|
||||||
|
11 => 0b111,
|
||||||
|
_ => return Err("N2_HS has an invalid value")
|
||||||
|
},
|
||||||
|
n2_ls: settings.n2_ls - 1,
|
||||||
|
n31: settings.n31 - 1,
|
||||||
|
n32: settings.n32 - 1,
|
||||||
|
bwsel: settings.bwsel,
|
||||||
|
crystal_ref: settings.crystal_ref
|
||||||
|
};
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||||
|
i2c.start().unwrap();
|
||||||
|
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack write address")
|
||||||
|
}
|
||||||
|
if !i2c.write(reg).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack register")
|
||||||
|
}
|
||||||
|
if !i2c.write(val).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack value")
|
||||||
|
}
|
||||||
|
i2c.stop().unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> {
|
||||||
|
i2c.start().unwrap();
|
||||||
|
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack write address")
|
||||||
|
}
|
||||||
|
if !i2c.write(reg).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack register")
|
||||||
|
}
|
||||||
|
i2c.write(val).unwrap();
|
||||||
|
i2c.stop().unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(i2c: &mut I2c, reg: u8) -> Result<u8> {
|
||||||
|
i2c.start().unwrap();
|
||||||
|
if !i2c.write(ADDRESS << 1).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack write address")
|
||||||
|
}
|
||||||
|
if !i2c.write(reg).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack register")
|
||||||
|
}
|
||||||
|
i2c.restart().unwrap();
|
||||||
|
if !i2c.write((ADDRESS << 1) | 1).unwrap() {
|
||||||
|
return Err("Si5324 failed to ack read address")
|
||||||
|
}
|
||||||
|
let val = i2c.read(false).unwrap();
|
||||||
|
i2c.stop().unwrap();
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rmw<F>(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where
|
||||||
|
F: Fn(u8) -> u8 {
|
||||||
|
let value = read(i2c, reg)?;
|
||||||
|
write(i2c, reg, f(value))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(i2c: &mut I2c) -> Result<u16> {
|
||||||
|
Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(si5324_soft_reset)]
|
||||||
|
fn soft_reset(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let val = read(i2c, 136)?;
|
||||||
|
write_no_ack_value(i2c, 136, val | 0x80)?;
|
||||||
|
timer.delay_us(10_000);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_xtal(i2c: &mut I2c) -> Result<bool> {
|
||||||
|
Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ckin(i2c: &mut I2c, input: Input) -> Result<bool> {
|
||||||
|
match input {
|
||||||
|
Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0
|
||||||
|
Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn locked(i2c: &mut I2c) -> Result<bool> {
|
||||||
|
Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn monitor_lock(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
info!("waiting for Si5324 lock...");
|
||||||
|
let timeout = timer.get_time() + Milliseconds(20_000);
|
||||||
|
while !locked(i2c)? {
|
||||||
|
// Yes, lock can be really slow.
|
||||||
|
if timer.get_time() > timeout {
|
||||||
|
return Err("Si5324 lock timeout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!(" ...locked");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(i2c: &mut I2c, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
#[cfg(not(si5324_soft_reset))]
|
||||||
|
hard_reset(timer);
|
||||||
|
|
||||||
|
#[cfg(feature = "target_kasli_soc")]
|
||||||
|
{
|
||||||
|
i2c.pca9548_select(0x70, 0)?;
|
||||||
|
i2c.pca9548_select(0x71, 1 << 3)?;
|
||||||
|
}
|
||||||
|
#[cfg(feature = "target_zc706")]
|
||||||
|
{
|
||||||
|
i2c.pca9548_select(0x74, 1 << 4)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ident(i2c)? != 0x0182 {
|
||||||
|
return Err("Si5324 does not have expected product number");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(si5324_soft_reset)]
|
||||||
|
soft_reset(i2c, timer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bypass(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
init(i2c, timer)?;
|
||||||
|
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
|
||||||
|
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG
|
||||||
|
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||||
|
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||||
|
rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let s = map_frequency_settings(settings)?;
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
|
||||||
|
init(i2c, timer)?;
|
||||||
|
if settings.crystal_ref {
|
||||||
|
rmw(i2c, 0, |v| v | 0x40)?; // FREE_RUN=1
|
||||||
|
}
|
||||||
|
rmw(i2c, 2, |v| (v & 0x0f) | (s.bwsel << 4))?;
|
||||||
|
rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0
|
||||||
|
rmw(i2c, 3, |v| (v & 0x2f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1
|
||||||
|
rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00
|
||||||
|
rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111
|
||||||
|
write(i2c, 25, (s.n1_hs << 5 ) as u8)?;
|
||||||
|
write(i2c, 31, (s.nc1_ls >> 16) as u8)?;
|
||||||
|
write(i2c, 32, (s.nc1_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 33, (s.nc1_ls) as u8)?;
|
||||||
|
write(i2c, 34, (s.nc1_ls >> 16) as u8)?; // write to NC2_LS as well
|
||||||
|
write(i2c, 35, (s.nc1_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 36, (s.nc1_ls) as u8)?;
|
||||||
|
write(i2c, 40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?;
|
||||||
|
write(i2c, 41, (s.n2_ls >> 8 ) as u8)?;
|
||||||
|
write(i2c, 42, (s.n2_ls) as u8)?;
|
||||||
|
write(i2c, 43, (s.n31 >> 16) as u8)?;
|
||||||
|
write(i2c, 44, (s.n31 >> 8) as u8)?;
|
||||||
|
write(i2c, 45, (s.n31) as u8)?;
|
||||||
|
write(i2c, 46, (s.n32 >> 16) as u8)?;
|
||||||
|
write(i2c, 47, (s.n32 >> 8) as u8)?;
|
||||||
|
write(i2c, 48, (s.n32) as u8)?;
|
||||||
|
rmw(i2c, 137, |v| v | 0x01)?; // FASTLOCK=1
|
||||||
|
rmw(i2c, 136, |v| v | 0x40)?; // ICAL=1
|
||||||
|
|
||||||
|
if !has_xtal(i2c)? {
|
||||||
|
return Err("Si5324 misses XA/XB signal");
|
||||||
|
}
|
||||||
|
if !has_ckin(i2c, input)? {
|
||||||
|
return Err("Si5324 misses clock input signal");
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_lock(i2c, timer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_input(i2c: &mut I2c, input: Input, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let cksel_reg = match input {
|
||||||
|
Input::Ckin1 => 0b00,
|
||||||
|
Input::Ckin2 => 0b01,
|
||||||
|
};
|
||||||
|
rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?;
|
||||||
|
if !has_ckin(i2c, input)? {
|
||||||
|
return Err("Si5324 misses clock input signal");
|
||||||
|
}
|
||||||
|
monitor_lock(i2c, timer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_siphaser)]
|
||||||
|
pub mod siphaser {
|
||||||
|
use super::*;
|
||||||
|
use crate::pl::csr;
|
||||||
|
|
||||||
|
pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let val = read(i2c, 3)?;
|
||||||
|
write(i2c, 3, (val & 0xdf) | (1 << 5))?; // DHOLD=1
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 });
|
||||||
|
}
|
||||||
|
let val = read(i2c, 3)?;
|
||||||
|
write(i2c, 3, (val & 0xdf) | (0 << 5))?; // DHOLD=0
|
||||||
|
monitor_lock(i2c, timer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn phase_shift(direction: u8, timer: &mut GlobalTimer) {
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::phase_shift_write(direction);
|
||||||
|
while csr::siphaser::phase_shift_done_read() == 0 {}
|
||||||
|
}
|
||||||
|
// wait for the Si5324 loop to stabilize
|
||||||
|
timer.delay_us(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_error(timer: &mut GlobalTimer) -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::error_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_us(5_000);
|
||||||
|
unsafe {
|
||||||
|
csr::siphaser::error_read() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result<u32> {
|
||||||
|
let mut nshifts = 0;
|
||||||
|
|
||||||
|
let mut previous = has_error(timer);
|
||||||
|
loop {
|
||||||
|
phase_shift(1, timer);
|
||||||
|
nshifts += 1;
|
||||||
|
let current = has_error(timer);
|
||||||
|
if previous != target && current == target {
|
||||||
|
return Ok(nshifts);
|
||||||
|
}
|
||||||
|
if nshifts > 5000 {
|
||||||
|
return Err("failed to find timing error edge");
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calibrate_skew(timer: &mut GlobalTimer) -> Result<()> {
|
||||||
|
let jitter_margin = 32;
|
||||||
|
let lead = find_edge(false, timer)?;
|
||||||
|
for _ in 0..jitter_margin {
|
||||||
|
phase_shift(1, timer);
|
||||||
|
}
|
||||||
|
let width = find_edge(true, timer)? + jitter_margin;
|
||||||
|
// width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter
|
||||||
|
info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8));
|
||||||
|
|
||||||
|
// Apply reverse phase shift for half the width to get into the
|
||||||
|
// middle of the working region.
|
||||||
|
for _ in 0..width/2 {
|
||||||
|
phase_shift(0, timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
authors = ["M-Labs"]
|
||||||
|
name = "build_zynq"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "build_zynq"
|
||||||
|
path = "lib.rs"
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 = []
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -6,12 +6,13 @@ authors = ["M-Labs"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706"]
|
target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"]
|
||||||
target_coraz7 = ["libboard_zynq/target_coraz7", "libsupport_zynq/target_coraz7", "libconfig/target_coraz7"]
|
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"]
|
||||||
target_redpitaya = ["libboard_zynq/target_redpitaya", "libsupport_zynq/target_redpitaya", "libconfig/target_redpitaya"]
|
|
||||||
target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc"]
|
|
||||||
default = ["target_zc706"]
|
default = ["target_zc706"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
build_zynq = { path = "../libbuild_zynq" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
num-derive = "0.3"
|
num-derive = "0.3"
|
||||||
|
@ -39,3 +40,5 @@ dyld = { path = "../libdyld" }
|
||||||
dwarf = { path = "../libdwarf" }
|
dwarf = { path = "../libdwarf" }
|
||||||
unwind = { path = "../libunwind" }
|
unwind = { path = "../libunwind" }
|
||||||
libc = { path = "../libc" }
|
libc = { path = "../libc" }
|
||||||
|
io = { path = "../libio" }
|
||||||
|
libboard_artiq = { path = "../libboard_artiq" }
|
|
@ -1,28 +1,6 @@
|
||||||
use std::env;
|
extern crate build_zynq;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Put the linker script somewhere the linker can find it
|
build_zynq::add_linker_script();
|
||||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
build_zynq::cfg();
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use libcortex_a9::{semaphore::Semaphore, mutex::Mutex, sync_channel::{Sender, Re
|
||||||
use futures::{select_biased, future::FutureExt};
|
use futures::{select_biased, future::FutureExt};
|
||||||
use libasync::{smoltcp::{Sockets, TcpStream}, task};
|
use libasync::{smoltcp::{Sockets, TcpStream}, task};
|
||||||
use libconfig::{Config, net_settings};
|
use libconfig::{Config, net_settings};
|
||||||
|
use libboard_artiq::drtio_routing;
|
||||||
|
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use crate::kernel;
|
use crate::kernel;
|
||||||
|
@ -28,7 +29,9 @@ use crate::rpc;
|
||||||
use crate::moninj;
|
use crate::moninj;
|
||||||
use crate::mgmt;
|
use crate::mgmt;
|
||||||
use crate::analyzer;
|
use crate::analyzer;
|
||||||
|
use crate::rtio_mgt;
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
use crate::pl;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -94,13 +97,13 @@ async fn read_request(stream: &TcpStream, allow_close: bool) -> Result<Option<Re
|
||||||
Ok(true) => {}
|
Ok(true) => {}
|
||||||
Ok(false) =>
|
Ok(false) =>
|
||||||
return Err(Error::UnexpectedPattern),
|
return Err(Error::UnexpectedPattern),
|
||||||
Err(smoltcp::Error::Illegal) => {
|
Err(smoltcp::Error::Finished) => {
|
||||||
if allow_close {
|
if allow_close {
|
||||||
info!("peer closed connection");
|
info!("peer closed connection");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
} else {
|
} else {
|
||||||
error!("peer unexpectedly closed connection");
|
error!("peer unexpectedly closed connection");
|
||||||
return Err(smoltcp::Error::Illegal)?;
|
return Err(smoltcp::Error::Finished)?;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
|
@ -221,13 +224,14 @@ 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 {
|
if let Some(stream) = stream {
|
||||||
write_header(stream, Reply::KernelFinished).await?;
|
write_header(stream, Reply::KernelFinished).await?;
|
||||||
|
write_i8(stream, async_errors as i8).await?;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
kernel::Message::KernelException(exception, backtrace) => {
|
kernel::Message::KernelException(exception, backtrace, async_errors) => {
|
||||||
match stream {
|
match stream {
|
||||||
Some(stream) => {
|
Some(stream) => {
|
||||||
// only send the exception data to host if there is host,
|
// only send the exception data to host if there is host,
|
||||||
|
@ -246,6 +250,7 @@ async fn handle_run_kernel(stream: Option<&TcpStream>, control: &Rc<RefCell<kern
|
||||||
for &addr in backtrace {
|
for &addr in backtrace {
|
||||||
write_i32(stream, addr as i32).await?;
|
write_i32(stream, addr as i32).await?;
|
||||||
}
|
}
|
||||||
|
write_i8(stream, async_errors as i8).await?;
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
error!("Uncaught kernel exception: {:?}", exception);
|
error!("Uncaught kernel exception: {:?}", exception);
|
||||||
|
@ -313,7 +318,9 @@ 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>>) -> Result<()> {
|
||||||
|
stream.set_ack_delay(None);
|
||||||
|
|
||||||
if !expect(stream, b"ARTIQ coredev\n").await? {
|
if !expect(stream, b"ARTIQ coredev\n").await? {
|
||||||
return Err(Error::UnexpectedPattern);
|
return Err(Error::UnexpectedPattern);
|
||||||
}
|
}
|
||||||
|
@ -385,8 +392,21 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
|
|
||||||
Sockets::init(32);
|
Sockets::init(32);
|
||||||
|
|
||||||
|
// before, mutex was on io, but now that io isn't used...?
|
||||||
|
let aux_mutex: Rc<Mutex<bool>> = Rc::new(Mutex::new(false));
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
let drtio_routing_table = Rc::new(RefCell::new(
|
||||||
|
drtio_routing::config_routing_table(pl::csr::DRTIO.len(), &cfg)));
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
let drtio_routing_table = Rc::new(RefCell::new(drtio_routing::RoutingTable::default_empty()));
|
||||||
|
let up_destinations = Rc::new(RefCell::new([false; drtio_routing::DEST_COUNT]));
|
||||||
|
#[cfg(has_drtio_routing)]
|
||||||
|
drtio_routing::interconnect_disable_all();
|
||||||
|
|
||||||
|
rtio_mgt::startup(&aux_mutex, &drtio_routing_table, &up_destinations, timer);
|
||||||
|
|
||||||
analyzer::start();
|
analyzer::start();
|
||||||
moninj::start(timer);
|
moninj::start(timer, aux_mutex, drtio_routing_table);
|
||||||
|
|
||||||
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
let control: Rc<RefCell<kernel::Control>> = Rc::new(RefCell::new(kernel::Control::start()));
|
||||||
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
let idle_kernel = Rc::new(cfg.read("idle").ok());
|
||||||
|
@ -407,7 +427,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
let connection = Rc::new(Semaphore::new(1, 1));
|
let connection = Rc::new(Semaphore::new(1, 1));
|
||||||
let terminate = Rc::new(Semaphore::new(0, 1));
|
let terminate = Rc::new(Semaphore::new(0, 1));
|
||||||
loop {
|
loop {
|
||||||
let stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
let mut stream = TcpStream::accept(1381, 0x10_000, 0x10_000).await.unwrap();
|
||||||
|
|
||||||
if connection.try_wait().is_none() {
|
if connection.try_wait().is_none() {
|
||||||
// there is an existing connection
|
// there is an existing connection
|
||||||
|
@ -425,7 +445,7 @@ pub fn main(timer: GlobalTimer, cfg: Config) {
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
select_biased! {
|
select_biased! {
|
||||||
_ = (async {
|
_ = (async {
|
||||||
let _ = handle_connection(&stream, control.clone())
|
let _ = handle_connection(&mut stream, control.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| warn!("connection terminated: {}", e));
|
.map_err(|e| warn!("connection terminated: {}", e));
|
||||||
if let Some(buffer) = &*idle_kernel {
|
if let Some(buffer) = &*idle_kernel {
|
||||||
|
|
|
@ -1,100 +1,67 @@
|
||||||
#[cfg(feature = "target_zc706")]
|
use libboard_zynq;
|
||||||
mod i2c {
|
use crate::artiq_raise;
|
||||||
use libboard_zynq;
|
|
||||||
use crate::artiq_raise;
|
|
||||||
|
|
||||||
static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
|
pub static mut I2C_BUS: Option<libboard_zynq::i2c::I2c> = None;
|
||||||
|
|
||||||
pub extern fn start(busno: i32) {
|
pub extern fn start(busno: i32) {
|
||||||
if busno > 0 {
|
if busno > 0 {
|
||||||
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
|
|
||||||
artiq_raise!("I2CError", "I2C start failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
unsafe {
|
||||||
pub extern fn restart(busno: i32) {
|
if (&mut I2C_BUS).as_mut().unwrap().start().is_err() {
|
||||||
if busno > 0 {
|
artiq_raise!("I2CError", "I2C start failed");
|
||||||
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 fn init() {
|
|
||||||
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
|
|
||||||
i2c.init().expect("I2C bus initialization failed");
|
|
||||||
unsafe { I2C_BUS = Some(i2c) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "target_zc706"))]
|
pub extern fn restart(busno: i32) {
|
||||||
mod i2c {
|
if busno > 0 {
|
||||||
use crate::artiq_raise;
|
artiq_raise!("I2CError", "I2C bus could not be accessed");
|
||||||
|
|
||||||
pub extern fn start(_busno: i32) {
|
|
||||||
artiq_raise!("I2CError", "No I2C bus");
|
|
||||||
}
|
}
|
||||||
|
unsafe {
|
||||||
pub extern fn restart(_busno: i32) {
|
if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() {
|
||||||
artiq_raise!("I2CError", "No I2C bus");
|
artiq_raise!("I2CError", "I2C restart failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub extern fn stop(_busno: i32) {
|
|
||||||
artiq_raise!("I2CError", "No I2C bus");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn write(_busno: i32, _data: i32) -> bool {
|
|
||||||
artiq_raise!("I2CError", "No I2C bus");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn read(_busno: i32, _ack: bool) -> i32 {
|
|
||||||
artiq_raise!("I2CError", "No I2C bus");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use i2c::*;
|
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 fn init() {
|
||||||
|
let mut i2c = libboard_zynq::i2c::I2c::i2c0();
|
||||||
|
i2c.init().expect("I2C bus initialization failed");
|
||||||
|
unsafe { I2C_BUS = Some(i2c) };
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use libcortex_a9::{
|
||||||
use libboard_zynq::{mpcore, gic};
|
use libboard_zynq::{mpcore, gic};
|
||||||
use libsupport_zynq::ram;
|
use libsupport_zynq::ram;
|
||||||
use dyld::{self, Library};
|
use dyld::{self, Library};
|
||||||
use crate::{eh_artiq, rtio};
|
use crate::{eh_artiq, get_async_errors, rtio};
|
||||||
use super::{
|
use super::{
|
||||||
api::resolve,
|
api::resolve,
|
||||||
rpc::rpc_send_async,
|
rpc::rpc_send_async,
|
||||||
|
@ -192,7 +192,8 @@ pub extern "C" fn main_core1() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("kernel finished");
|
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),
|
_ => error!("Core1 received unexpected message: {:?}", message),
|
||||||
}
|
}
|
||||||
|
@ -216,7 +217,8 @@ pub fn terminate(exception: &'static eh_artiq::Exception<'static>, backtrace: &'
|
||||||
|
|
||||||
{
|
{
|
||||||
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
let core1_tx = unsafe { KERNEL_CHANNEL_1TO0.as_mut().unwrap() };
|
||||||
core1_tx.send(Message::KernelException(exception, &backtrace[..cursor]));
|
let errors = unsafe { get_async_errors() };
|
||||||
|
core1_tx.send(Message::KernelException(exception, &backtrace[..cursor], errors));
|
||||||
}
|
}
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ pub enum Message {
|
||||||
LoadCompleted,
|
LoadCompleted,
|
||||||
LoadFailed,
|
LoadFailed,
|
||||||
StartRequest,
|
StartRequest,
|
||||||
KernelFinished,
|
KernelFinished(u8),
|
||||||
KernelException(&'static eh_artiq::Exception<'static>, &'static [usize]),
|
KernelException(&'static eh_artiq::Exception<'static>, &'static [usize], u8),
|
||||||
RpcSend { is_async: bool, data: Vec<u8> },
|
RpcSend { is_async: bool, data: Vec<u8> },
|
||||||
RpcRecvRequest(*mut ()),
|
RpcRecvRequest(*mut ()),
|
||||||
RpcRecvReply(Result<usize, RPCException>),
|
RpcRecvReply(Result<usize, RPCException>),
|
||||||
|
|
|
@ -11,120 +11,43 @@
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use core::{cmp, str};
|
|
||||||
use log::{info, warn, error};
|
use log::{info, warn, error};
|
||||||
|
|
||||||
use libboard_zynq::{timer::GlobalTimer, mpcore, gic, slcr};
|
use libboard_zynq::{timer::GlobalTimer, mpcore, gic};
|
||||||
use libasync::{task, block_async};
|
use libasync::{task, block_async};
|
||||||
use libsupport_zynq::ram;
|
use libsupport_zynq::ram;
|
||||||
use nb;
|
use nb;
|
||||||
use void::Void;
|
use void::Void;
|
||||||
use embedded_hal::blocking::delay::DelayMs;
|
|
||||||
use libconfig::Config;
|
use libconfig::Config;
|
||||||
use libregister::RegisterW;
|
|
||||||
use libcortex_a9::l2c::enable_l2_cache;
|
use libcortex_a9::l2c::enable_l2_cache;
|
||||||
|
use libboard_artiq::{logger, identifier_read, init_gateware, pl};
|
||||||
|
|
||||||
mod proto_core_io;
|
|
||||||
mod proto_async;
|
mod proto_async;
|
||||||
mod comms;
|
mod comms;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
#[path = "../../../build/pl.rs"]
|
|
||||||
mod pl;
|
|
||||||
#[cfg(ki_impl = "csr")]
|
#[cfg(ki_impl = "csr")]
|
||||||
#[path = "rtio_csr.rs"]
|
#[path = "rtio_csr.rs"]
|
||||||
mod rtio;
|
mod rtio;
|
||||||
#[cfg(ki_impl = "acp")]
|
#[cfg(ki_impl = "acp")]
|
||||||
#[path = "rtio_acp.rs"]
|
#[path = "rtio_acp.rs"]
|
||||||
mod rtio;
|
mod rtio;
|
||||||
|
mod rtio_mgt;
|
||||||
|
mod rtio_clocking;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
mod moninj;
|
mod moninj;
|
||||||
mod eh_artiq;
|
mod eh_artiq;
|
||||||
mod panic;
|
mod panic;
|
||||||
mod logger;
|
|
||||||
mod mgmt;
|
mod mgmt;
|
||||||
mod analyzer;
|
mod analyzer;
|
||||||
mod irq;
|
mod irq;
|
||||||
mod i2c;
|
mod i2c;
|
||||||
|
|
||||||
fn init_gateware() {
|
static mut SEEN_ASYNC_ERRORS: u8 = 0;
|
||||||
// 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()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn identifier_read(buf: &mut [u8]) -> &str {
|
pub unsafe fn get_async_errors() -> u8 {
|
||||||
unsafe {
|
let errors = SEEN_ASYNC_ERRORS;
|
||||||
pl::csr::identifier::address_write(0);
|
SEEN_ASYNC_ERRORS = 0;
|
||||||
let len = pl::csr::identifier::data_read();
|
errors
|
||||||
let len = cmp::min(len, buf.len() as u8);
|
|
||||||
for i in 0..len {
|
|
||||||
pl::csr::identifier::address_write(1 + i);
|
|
||||||
buf[i as usize] = pl::csr::identifier::data_read();
|
|
||||||
}
|
|
||||||
str::from_utf8_unchecked(&buf[..len as usize])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_rtio(timer: &mut GlobalTimer, cfg: &Config) {
|
|
||||||
let clock_sel =
|
|
||||||
if let Ok(rtioclk) = cfg.read_str("rtioclk") {
|
|
||||||
match rtioclk.as_ref() {
|
|
||||||
"internal" => {
|
|
||||||
info!("using internal RTIO clock");
|
|
||||||
0
|
|
||||||
},
|
|
||||||
"external" => {
|
|
||||||
info!("using external RTIO clock");
|
|
||||||
1
|
|
||||||
},
|
|
||||||
other => {
|
|
||||||
warn!("RTIO clock specification '{}' not recognized", other);
|
|
||||||
info!("using internal RTIO clock");
|
|
||||||
0
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info!("using internal RTIO clock (default)");
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
unsafe {
|
|
||||||
pl::csr::rtio_crg::pll_reset_write(1);
|
|
||||||
pl::csr::rtio_crg::clock_sel_write(clock_sel);
|
|
||||||
pl::csr::rtio_crg::pll_reset_write(0);
|
|
||||||
}
|
|
||||||
timer.delay_ms(1);
|
|
||||||
let locked = unsafe { pl::csr::rtio_crg::pll_locked_read() != 0 };
|
|
||||||
if locked {
|
|
||||||
info!("RTIO PLL locked");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
warn!("RTIO PLL failed to lock, retrying...");
|
|
||||||
timer.delay_ms(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
pl::csr::rtio_core::reset_phy_write(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
fn wait_for_async_rtio_error() -> nb::Result<(), Void> {
|
||||||
|
@ -154,11 +77,14 @@ async fn report_async_rtio_errors() {
|
||||||
error!("RTIO sequence error involving channel {}",
|
error!("RTIO sequence error involving channel {}",
|
||||||
pl::csr::rtio_core::sequence_error_channel_read());
|
pl::csr::rtio_core::sequence_error_channel_read());
|
||||||
}
|
}
|
||||||
|
SEEN_ASYNC_ERRORS = errors;
|
||||||
pl::csr::rtio_core::async_error_write(errors);
|
pl::csr::rtio_core::async_error_write(errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
|
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -191,7 +117,8 @@ pub fn main_core0() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
init_rtio(&mut timer, &cfg);
|
rtio_clocking::init(&mut timer, &cfg);
|
||||||
|
|
||||||
task::spawn(report_async_rtio_errors());
|
task::spawn(report_async_rtio_errors());
|
||||||
|
|
||||||
comms::main(timer, cfg);
|
comms::main(timer, cfg);
|
||||||
|
|
|
@ -6,7 +6,7 @@ use core::cell::RefCell;
|
||||||
use alloc::{rc::Rc, vec::Vec, string::String};
|
use alloc::{rc::Rc, vec::Vec, string::String};
|
||||||
use log::{self, info, debug, warn, error, LevelFilter};
|
use log::{self, info, debug, warn, error, LevelFilter};
|
||||||
|
|
||||||
use crate::logger::{BufferLogger, LogBufferRef};
|
use libboard_artiq::logger::{BufferLogger, LogBufferRef};
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
@ -121,7 +121,7 @@ async fn handle_connection(
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let msg = read_i8(stream).await;
|
let msg = read_i8(stream).await;
|
||||||
if let Err(smoltcp::Error::Illegal) = msg {
|
if let Err(smoltcp::Error::Finished) = msg {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
|
let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?;
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
use core::fmt;
|
use core::{fmt, cell::RefCell};
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::{collections::BTreeMap, rc::Rc};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use void::Void;
|
use void::Void;
|
||||||
|
|
||||||
|
use libboard_artiq::drtio_routing;
|
||||||
|
|
||||||
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
|
use libboard_zynq::{smoltcp, timer::GlobalTimer, time::Milliseconds};
|
||||||
use libasync::{task, smoltcp::TcpStream, block_async, nb};
|
use libasync::{task, smoltcp::TcpStream, block_async, nb};
|
||||||
|
use libcortex_a9::mutex::Mutex;
|
||||||
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
use futures::{pin_mut, select_biased, FutureExt};
|
use futures::{pin_mut, select_biased, FutureExt};
|
||||||
|
|
||||||
use crate::proto_async::*;
|
use crate::proto_async::*;
|
||||||
use crate::pl::csr;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -19,7 +21,6 @@ pub enum Error {
|
||||||
NetworkError(smoltcp::Error),
|
NetworkError(smoltcp::Error),
|
||||||
UnexpectedPattern,
|
UnexpectedPattern,
|
||||||
UnrecognizedPacket,
|
UnrecognizedPacket,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
@ -54,32 +55,109 @@ enum DeviceMessage {
|
||||||
InjectionStatus = 1
|
InjectionStatus = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_probe(channel: i32, probe: i8) -> i32 {
|
#[cfg(has_drtio)]
|
||||||
unsafe {
|
mod remote_moninj {
|
||||||
csr::rtio_moninj::mon_chan_sel_write(channel as _);
|
use super::*;
|
||||||
csr::rtio_moninj::mon_probe_sel_write(probe as _);
|
use libboard_artiq::drtioaux;
|
||||||
csr::rtio_moninj::mon_value_update_write(1);
|
use crate::rtio_mgt::drtio;
|
||||||
csr::rtio_moninj::mon_value_read() as i32
|
use log::error;
|
||||||
|
|
||||||
|
pub fn read_probe(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, probe: i8) -> i32 {
|
||||||
|
let reply = task::block_on(drtio::aux_transact(aux_mutex, linkno, &drtioaux::Packet::MonitorRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
probe: probe as _},
|
||||||
|
timer));
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::MonitorReply { value }) => return value as i32,
|
||||||
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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::send(linkno, &drtioaux::Packet::InjectionRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
overrd: overrd as _,
|
||||||
|
value: value as _
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_injection_status(aux_mutex: &Rc<Mutex<bool>>, timer: GlobalTimer, linkno: u8, destination: u8, channel: i32, overrd: i8) -> i8 {
|
||||||
|
let reply = task::block_on(drtio::aux_transact(aux_mutex,
|
||||||
|
linkno,
|
||||||
|
&drtioaux::Packet::InjectionStatusRequest {
|
||||||
|
destination: destination,
|
||||||
|
channel: channel as _,
|
||||||
|
overrd: overrd as _},
|
||||||
|
timer));
|
||||||
|
match reply {
|
||||||
|
Ok(drtioaux::Packet::InjectionStatusReply { value }) => return value as i8,
|
||||||
|
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
||||||
|
Err(e) => error!("aux packet error ({})", e)
|
||||||
|
}
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inject(channel: i32, overrd: i8, value: i8) {
|
mod local_moninj {
|
||||||
unsafe {
|
use libboard_artiq::pl::csr;
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
pub fn read_probe(channel: i32, probe: i8) -> i32 {
|
||||||
csr::rtio_moninj::inj_value_write(value as _);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
#[cfg(has_drtio)]
|
||||||
unsafe {
|
macro_rules! dispatch {
|
||||||
csr::rtio_moninj::inj_chan_sel_write(channel as _);
|
($timer:ident, $aux_mutex:ident, $routing_table:ident, $channel:expr, $func:ident $(, $param:expr)*) => {{
|
||||||
csr::rtio_moninj::inj_override_sel_write(overrd as _);
|
let destination = ($channel >> 16) as u8;
|
||||||
csr::rtio_moninj::inj_value_read() as i8
|
let channel = $channel;
|
||||||
}
|
let routing_table = $routing_table.borrow_mut();
|
||||||
|
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, )*)
|
||||||
|
}
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
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: &Rc<RefCell<drtio_routing::RoutingTable>>) -> Result<()> {
|
||||||
if !expect(&stream, b"ARTIQ moninj\n").await? {
|
if !expect(&stream, b"ARTIQ moninj\n").await? {
|
||||||
return Err(Error::UnexpectedPattern);
|
return Err(Error::UnexpectedPattern);
|
||||||
}
|
}
|
||||||
|
@ -135,13 +213,13 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
let channel = read_i32(&stream).await?;
|
let channel = read_i32(&stream).await?;
|
||||||
let overrd = read_i8(&stream).await?;
|
let overrd = read_i8(&stream).await?;
|
||||||
let value = read_i8(&stream).await?;
|
let value = read_i8(&stream).await?;
|
||||||
inject(channel, overrd, value);
|
dispatch!(timer, _aux_mutex, _routing_table, channel, inject, overrd, value);
|
||||||
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
|
debug!("INJECT channel {}, overrd {}, value {}", channel, overrd, value);
|
||||||
},
|
},
|
||||||
HostMessage::GetInjectionStatus => {
|
HostMessage::GetInjectionStatus => {
|
||||||
let channel = read_i32(&stream).await?;
|
let channel = read_i32(&stream).await?;
|
||||||
let overrd = read_i8(&stream).await?;
|
let overrd = read_i8(&stream).await?;
|
||||||
let value = 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_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
write_i8(&stream, overrd).await?;
|
write_i8(&stream, overrd).await?;
|
||||||
|
@ -151,7 +229,7 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
},
|
},
|
||||||
_ = timeout_f => {
|
_ = timeout_f => {
|
||||||
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
for (&(channel, probe), previous) in probe_watch_list.iter_mut() {
|
||||||
let current = read_probe(channel, probe);
|
let current = dispatch!(timer, _aux_mutex, _routing_table, channel, read_probe, probe);
|
||||||
if previous.is_none() || previous.unwrap() != current {
|
if previous.is_none() || previous.unwrap() != current {
|
||||||
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
|
write_i8(&stream, DeviceMessage::MonitorStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
|
@ -161,7 +239,7 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (&(channel, overrd), previous) in inject_watch_list.iter_mut() {
|
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 {
|
if previous.is_none() || previous.unwrap() != current {
|
||||||
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
write_i8(&stream, DeviceMessage::InjectionStatus.to_i8().unwrap()).await?;
|
||||||
write_i32(&stream, channel).await?;
|
write_i32(&stream, channel).await?;
|
||||||
|
@ -176,15 +254,17 @@ async fn handle_connection(stream: &TcpStream, timer: GlobalTimer) -> Result<()>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
task::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
let aux_mutex = aux_mutex.clone();
|
||||||
|
let routing_table = routing_table.clone();
|
||||||
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
|
let stream = TcpStream::accept(1383, 2048, 2048).await.unwrap();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
info!("received connection");
|
info!("received connection");
|
||||||
let result = handle_connection(&stream, timer).await;
|
let result = handle_connection(&stream, timer, &aux_mutex, &routing_table).await;
|
||||||
match result {
|
match result {
|
||||||
Err(Error::NetworkError(smoltcp::Error::Illegal)) => info!("peer closed connection"),
|
Err(Error::NetworkError(smoltcp::Error::Finished)) => info!("peer closed connection"),
|
||||||
Err(error) => warn!("connection terminated: {}", error),
|
Err(error) => warn!("connection terminated: {}", error),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use libasync::smoltcp::TcpStream;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
|
||||||
use crate::proto_core_io::ProtoWrite;
|
use io::proto::ProtoWrite;
|
||||||
use crate::proto_async;
|
use crate::proto_async;
|
||||||
use self::tag::{Tag, TagIterator, split_tag};
|
use self::tag::{Tag, TagIterator, split_tag};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
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");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
warn!("RTIO PLL failed to lock, retrying...");
|
||||||
|
timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
pl::csr::rtio_core::reset_phy_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
fn init_drtio(timer: &mut GlobalTimer)
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
pl::csr::drtio_transceiver::stable_clkin_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_ms(2); // wait for CPLL/QPLL lock
|
||||||
|
unsafe {
|
||||||
|
pl::csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) {
|
||||||
|
let si5324_settings = match clk {
|
||||||
|
RtioClock::Ext0_Synth0_10to125 => { // 125 MHz output from 10 MHz CLKINx reference, 504 Hz BW
|
||||||
|
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 10,
|
||||||
|
nc1_ls : 4,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 300,
|
||||||
|
n31 : 6,
|
||||||
|
n32 : 6,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RtioClock::Ext0_Synth0_100to125 => { // 125MHz output, from 100MHz CLKINx reference, 586 Hz loop bandwidth
|
||||||
|
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 10,
|
||||||
|
nc1_ls : 4,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 260,
|
||||||
|
n31 : 52,
|
||||||
|
n32 : 52,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RtioClock::Ext0_Synth0_125to125 => { // 125MHz output, from 125MHz CLKINx reference, 606 Hz loop bandwidth
|
||||||
|
info!("using 10MHz reference to make 125MHz RTIO clock with PLL");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 5,
|
||||||
|
nc1_ls : 8,
|
||||||
|
n2_hs : 7,
|
||||||
|
n2_ls : 360,
|
||||||
|
n31 : 63,
|
||||||
|
n32 : 63,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RtioClock::Int_150 => { // 150MHz output, from crystal
|
||||||
|
info!("using internal 150MHz RTIO clock");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 9,
|
||||||
|
nc1_ls : 4,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 33732,
|
||||||
|
n31 : 7139,
|
||||||
|
n32 : 7139,
|
||||||
|
bwsel : 3,
|
||||||
|
crystal_ref: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RtioClock::Int_100 => { // 100MHz output, from crystal.
|
||||||
|
info!("using internal 100MHz RTIO clock");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 9,
|
||||||
|
nc1_ls : 6,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 33732,
|
||||||
|
n31 : 7139,
|
||||||
|
n32 : 7139,
|
||||||
|
bwsel : 3,
|
||||||
|
crystal_ref: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RtioClock::Int_125 => { // 125MHz output, from crystal, 7 Hz
|
||||||
|
info!("using internal 125MHz RTIO clock");
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 10,
|
||||||
|
nc1_ls : 4,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 19972,
|
||||||
|
n31 : 4565,
|
||||||
|
n32 : 4565,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { // same setting as Int_125, but fallback to default
|
||||||
|
warn!("rtio_clock setting '{:?}' is unsupported. Falling back to default internal 125MHz RTIO clock.", clk);
|
||||||
|
si5324::FrequencySettings {
|
||||||
|
n1_hs : 10,
|
||||||
|
nc1_ls : 4,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 19972,
|
||||||
|
n31 : 4565,
|
||||||
|
n32 : 4565,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let si5324_ref_input = si5324::Input::Ckin2;
|
||||||
|
si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(timer: &mut GlobalTimer, cfg: &Config) {
|
||||||
|
|
||||||
|
let clk = get_rtio_clock_cfg(cfg);
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
{
|
||||||
|
let i2c = unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() };
|
||||||
|
let si5324_ext_input = si5324::Input::Ckin2;
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,352 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use libboard_zynq::timer::GlobalTimer;
|
||||||
|
use libboard_artiq::{pl::csr, drtio_routing};
|
||||||
|
use libcortex_a9::mutex::Mutex;
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(has_drtio)]
|
||||||
|
pub mod drtio {
|
||||||
|
use super::*;
|
||||||
|
use libboard_artiq::drtioaux_async;
|
||||||
|
use libboard_artiq::drtioaux_async::Packet;
|
||||||
|
use libboard_artiq::drtioaux::Error;
|
||||||
|
use log::{warn, error, info};
|
||||||
|
use embedded_hal::blocking::delay::DelayMs;
|
||||||
|
use libasync::{task, delay};
|
||||||
|
use libboard_zynq::time::Milliseconds;
|
||||||
|
|
||||||
|
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
timer: GlobalTimer) {
|
||||||
|
let aux_mutex = aux_mutex.clone();
|
||||||
|
let routing_table = routing_table.clone();
|
||||||
|
let up_destinations = up_destinations.clone();
|
||||||
|
task::spawn(async move {
|
||||||
|
let routing_table = routing_table.borrow();
|
||||||
|
link_task(&aux_mutex, &routing_table, &up_destinations, timer).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn link_rx_up(linkno: u8) -> bool {
|
||||||
|
let linkno = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[linkno].rx_up_read)() == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn recv_aux_timeout(linkno: u8, timeout: u64, timer: GlobalTimer) -> Result<Packet, &'static str> {
|
||||||
|
if !link_rx_up(linkno).await {
|
||||||
|
return Err("link went down");
|
||||||
|
}
|
||||||
|
match drtioaux_async::recv_timeout(linkno, Some(timeout), timer).await {
|
||||||
|
Ok(packet) => return Ok(packet),
|
||||||
|
Err(Error::TimedOut) => return Err("timed out"),
|
||||||
|
Err(_) => return Err("aux packet error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn aux_transact(aux_mutex: &Mutex<bool>, linkno: u8, request: &Packet,
|
||||||
|
timer: GlobalTimer) -> Result<Packet, &'static str> {
|
||||||
|
let _lock = aux_mutex.lock();
|
||||||
|
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.lock();
|
||||||
|
|
||||||
|
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.lock();
|
||||||
|
match drtioaux_async::recv(linkno).await {
|
||||||
|
Ok(Some(packet)) => warn!("[LINK#{}] unsolicited aux packet: {:?}", linkno, packet),
|
||||||
|
Ok(None) => (),
|
||||||
|
Err(_) => warn!("[LINK#{}] aux packet error", linkno)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_local_errors(linkno: u8) {
|
||||||
|
let errors;
|
||||||
|
let linkidx = linkno as usize;
|
||||||
|
unsafe {
|
||||||
|
errors = (csr::DRTIO[linkidx].protocol_error_read)();
|
||||||
|
(csr::DRTIO[linkidx].protocol_error_write)(errors);
|
||||||
|
}
|
||||||
|
if errors != 0 {
|
||||||
|
error!("[LINK#{}] error(s) found (0x{:02x}):", linkno, errors);
|
||||||
|
if errors & 1 != 0 {
|
||||||
|
error!("[LINK#{}] received packet of an unknown type", linkno);
|
||||||
|
}
|
||||||
|
if errors & 2 != 0 {
|
||||||
|
error!("[LINK#{}] received truncated packet", linkno);
|
||||||
|
}
|
||||||
|
if errors & 4 != 0 {
|
||||||
|
error!("[LINK#{}] timeout attempting to get remote buffer space", linkno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn destination_set_up(routing_table: &drtio_routing::RoutingTable,
|
||||||
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
destination: u8, up: bool) {
|
||||||
|
let mut up_destinations = up_destinations.borrow_mut();
|
||||||
|
up_destinations[destination as usize] = up;
|
||||||
|
if up {
|
||||||
|
drtio_routing::interconnect_enable(routing_table, 0, destination);
|
||||||
|
info!("[DEST#{}] destination is up", destination);
|
||||||
|
} else {
|
||||||
|
drtio_routing::interconnect_disable(destination);
|
||||||
|
info!("[DEST#{}] destination is down", destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn destination_up(up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, destination: u8) -> bool {
|
||||||
|
let up_destinations = up_destinations.borrow();
|
||||||
|
up_destinations[destination as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn destination_survey(aux_mutex: &Rc<Mutex<bool>>, routing_table: &drtio_routing::RoutingTable,
|
||||||
|
up_links: &[bool],
|
||||||
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
timer: GlobalTimer) {
|
||||||
|
for destination in 0..drtio_routing::DEST_COUNT {
|
||||||
|
let hop = routing_table.0[destination][0];
|
||||||
|
let destination = destination as u8;
|
||||||
|
|
||||||
|
if hop == 0 {
|
||||||
|
/* local RTIO */
|
||||||
|
if !destination_up(up_destinations, destination).await {
|
||||||
|
destination_set_up(routing_table, up_destinations, destination, true).await;
|
||||||
|
}
|
||||||
|
} else if hop as usize <= csr::DRTIO.len() {
|
||||||
|
let linkno = hop - 1;
|
||||||
|
if destination_up(up_destinations, destination).await {
|
||||||
|
if up_links[linkno as usize] {
|
||||||
|
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
|
||||||
|
destination: destination
|
||||||
|
}, timer).await;
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::DestinationDownReply) =>
|
||||||
|
destination_set_up(routing_table, up_destinations, destination, false).await,
|
||||||
|
Ok(Packet::DestinationOkReply) => (),
|
||||||
|
Ok(Packet::DestinationSequenceErrorReply { channel }) =>
|
||||||
|
error!("[DEST#{}] RTIO sequence error involving channel 0x{:04x}", destination, channel),
|
||||||
|
Ok(Packet::DestinationCollisionReply { channel }) =>
|
||||||
|
error!("[DEST#{}] RTIO collision involving channel 0x{:04x}", destination, channel),
|
||||||
|
Ok(Packet::DestinationBusyReply { channel }) =>
|
||||||
|
error!("[DEST#{}] RTIO busy error involving channel 0x{:04x}", destination, channel),
|
||||||
|
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||||
|
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destination_set_up(routing_table, up_destinations, destination, false).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if up_links[linkno as usize] {
|
||||||
|
let reply = aux_transact(aux_mutex, linkno, &Packet::DestinationStatusRequest {
|
||||||
|
destination: destination
|
||||||
|
}, timer).await;
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::DestinationDownReply) => (),
|
||||||
|
Ok(Packet::DestinationOkReply) => {
|
||||||
|
destination_set_up(routing_table, up_destinations, destination, true).await;
|
||||||
|
init_buffer_space(destination as u8, linkno).await;
|
||||||
|
},
|
||||||
|
Ok(packet) => error!("[DEST#{}] received unexpected aux packet: {:?}", destination, packet),
|
||||||
|
Err(e) => error!("[DEST#{}] communication failed ({})", destination, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn link_task(aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &drtio_routing::RoutingTable,
|
||||||
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
timer: GlobalTimer) {
|
||||||
|
let mut up_links = [false; csr::DRTIO.len()];
|
||||||
|
loop {
|
||||||
|
for linkno in 0..csr::DRTIO.len() {
|
||||||
|
let linkno = linkno as u8;
|
||||||
|
if up_links[linkno as usize] {
|
||||||
|
/* link was previously up */
|
||||||
|
if link_rx_up(linkno).await {
|
||||||
|
process_unsolicited_aux(aux_mutex, linkno).await;
|
||||||
|
process_local_errors(linkno).await;
|
||||||
|
} else {
|
||||||
|
info!("[LINK#{}] link is down", linkno);
|
||||||
|
up_links[linkno as usize] = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* link was previously down */
|
||||||
|
if link_rx_up(linkno).await {
|
||||||
|
info!("[LINK#{}] link RX became up, pinging", linkno);
|
||||||
|
let ping_count = ping_remote(aux_mutex, linkno, timer).await;
|
||||||
|
if ping_count > 0 {
|
||||||
|
info!("[LINK#{}] remote replied after {} packets", linkno, ping_count);
|
||||||
|
up_links[linkno as usize] = true;
|
||||||
|
if let Err(e) = sync_tsc(aux_mutex, linkno, timer).await {
|
||||||
|
error!("[LINK#{}] failed to sync TSC ({})", linkno, e);
|
||||||
|
}
|
||||||
|
if let Err(e) = load_routing_table(aux_mutex, linkno, routing_table, timer).await {
|
||||||
|
error!("[LINK#{}] failed to load routing table ({})", linkno, e);
|
||||||
|
}
|
||||||
|
if let Err(e) = set_rank(aux_mutex, linkno, 1 as u8, timer).await {
|
||||||
|
error!("[LINK#{}] failed to set rank ({})", linkno, e);
|
||||||
|
}
|
||||||
|
info!("[LINK#{}] link initialization completed", linkno);
|
||||||
|
} else {
|
||||||
|
error!("[LINK#{}] ping failed", linkno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destination_survey(aux_mutex, routing_table, &up_links, up_destinations, timer).await;
|
||||||
|
let mut countdown = timer.countdown();
|
||||||
|
delay(&mut countdown, Milliseconds(200)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn reset(aux_mutex: Rc<Mutex<bool>>, mut timer: GlobalTimer) {
|
||||||
|
for linkno in 0..csr::DRTIO.len() {
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[linkno].reset_write)(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.delay_ms(1);
|
||||||
|
for linkno in 0..csr::DRTIO.len() {
|
||||||
|
unsafe {
|
||||||
|
(csr::DRTIO[linkno].reset_write)(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for linkno in 0..csr::DRTIO.len() {
|
||||||
|
let linkno = linkno as u8;
|
||||||
|
if task::block_on(link_rx_up(linkno)) {
|
||||||
|
let reply = task::block_on(aux_transact(&aux_mutex, linkno,
|
||||||
|
&Packet::ResetRequest, timer));
|
||||||
|
match reply {
|
||||||
|
Ok(Packet::ResetAck) => (),
|
||||||
|
Ok(_) => error!("[LINK#{}] reset failed, received unexpected aux packet", linkno),
|
||||||
|
Err(e) => error!("[LINK#{}] reset failed, aux packet error ({})", linkno, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_drtio))]
|
||||||
|
pub mod drtio {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn startup(_aux_mutex: &Rc<Mutex<bool>>, _routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
|
_up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>, _timer: GlobalTimer) {}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn reset(_aux_mutex: Rc<Mutex<bool>>, mut _timer: GlobalTimer) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn startup(aux_mutex: &Rc<Mutex<bool>>,
|
||||||
|
routing_table: &Rc<RefCell<drtio_routing::RoutingTable>>,
|
||||||
|
up_destinations: &Rc<RefCell<[bool; drtio_routing::DEST_COUNT]>>,
|
||||||
|
timer: GlobalTimer) {
|
||||||
|
drtio::startup(aux_mutex, routing_table, up_destinations, timer);
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_core::reset_phy_write(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn reset(aux_mutex: Rc<Mutex<bool>>, timer: GlobalTimer) {
|
||||||
|
unsafe {
|
||||||
|
csr::rtio_core::reset_write(1);
|
||||||
|
}
|
||||||
|
drtio::reset(aux_mutex, timer)
|
||||||
|
}
|
|
@ -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 = ["ipv6"] }
|
||||||
|
|
||||||
|
libboard_artiq = { path = "../libboard_artiq" }
|
||||||
|
unwind = { path = "../libunwind" }
|
||||||
|
libc = { path = "../libc" }
|
|
@ -0,0 +1,6 @@
|
||||||
|
extern crate build_zynq;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_zynq::add_linker_script();
|
||||||
|
build_zynq::cfg();
|
||||||
|
}
|
|
@ -0,0 +1,639 @@
|
||||||
|
#![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();
|
||||||
|
}
|
||||||
|
#[cfg(not(has_rtio_moninj))]
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
let reply = drtioaux::Packet::MonitorReply { value: value as u32 };
|
||||||
|
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::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: 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(has_rtio_crg))]
|
||||||
|
fn init_rtio_crg(_timer: GlobalTimer) { }
|
||||||
|
|
||||||
|
fn hardware_tick(ts: &mut u64, timer: &mut GlobalTimer) {
|
||||||
|
let now = timer.get_time();
|
||||||
|
let mut ts_ms = Milliseconds(*ts);
|
||||||
|
if now > ts_ms {
|
||||||
|
ts_ms = now + Milliseconds(200);
|
||||||
|
*ts = ts_ms.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(has_si5324, rtio_frequency = "125.0"))]
|
||||||
|
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||||
|
= si5324::FrequencySettings {
|
||||||
|
n1_hs : 5,
|
||||||
|
nc1_ls : 8,
|
||||||
|
n2_hs : 7,
|
||||||
|
n2_ls : 360,
|
||||||
|
n31 : 63,
|
||||||
|
n32 : 63,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(has_si5324, rtio_frequency = "100.0"))]
|
||||||
|
const SI5324_SETTINGS: si5324::FrequencySettings
|
||||||
|
= si5324::FrequencySettings {
|
||||||
|
n1_hs : 5,
|
||||||
|
nc1_ls : 10,
|
||||||
|
n2_hs : 10,
|
||||||
|
n2_ls : 250,
|
||||||
|
n31 : 50,
|
||||||
|
n32 : 50,
|
||||||
|
bwsel : 4,
|
||||||
|
crystal_ref: true
|
||||||
|
};
|
||||||
|
|
||||||
|
static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17];
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn main_core0() -> i32 {
|
||||||
|
enable_l2_cache(0x8);
|
||||||
|
|
||||||
|
let mut timer = GlobalTimer::start();
|
||||||
|
|
||||||
|
let buffer_logger = unsafe {
|
||||||
|
logger::BufferLogger::new(&mut LOG_BUFFER[..])
|
||||||
|
};
|
||||||
|
buffer_logger.set_uart_log_level(log::LevelFilter::Info);
|
||||||
|
buffer_logger.register();
|
||||||
|
log::set_max_level(log::LevelFilter::Info);
|
||||||
|
|
||||||
|
init_gateware();
|
||||||
|
|
||||||
|
info!("ARTIQ satellite manager starting...");
|
||||||
|
info!("gateware ident {}", identifier_read(&mut [0; 64]));
|
||||||
|
|
||||||
|
ram::init_alloc_core0();
|
||||||
|
|
||||||
|
let mut i2c = I2c::i2c0();
|
||||||
|
i2c.init().expect("I2C initialization failed");
|
||||||
|
|
||||||
|
#[cfg(has_si5324)]
|
||||||
|
si5324::setup(&mut i2c, &SI5324_SETTINGS, si5324::Input::Ckin1, &mut timer).expect("cannot initialize Si5324");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::stable_clkin_write(1);
|
||||||
|
}
|
||||||
|
timer.delay_us(1500); // wait for CPLL/QPLL lock
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
csr::drtio_transceiver::txenable_write(0xffffffffu32 as _);
|
||||||
|
}
|
||||||
|
init_rtio_crg(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
|
||||||
|
}
|
|
@ -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(()) }
|
||||||
|
}
|
|
@ -3,6 +3,6 @@ let
|
||||||
in
|
in
|
||||||
pkgs.fetchgit {
|
pkgs.fetchgit {
|
||||||
url = "https://git.m-labs.hk/M-Labs/zynq-rs.git";
|
url = "https://git.m-labs.hk/M-Labs/zynq-rs.git";
|
||||||
rev = "2c161720fa12f8b7abecaf60f77b062b08ac9bc1";
|
rev = "57d8d8fbc7087863305721bcb8fdbdd13d65bd65";
|
||||||
sha256 = "0zpazkicqzps86r7lgqf09y9ary94mjvxw6gc41z9kjjyxar5fhr";
|
sha256 = "0gnbaxgingrxrxi2sjd592r1630ld4ckrz4r8ajx8zp7q3bb43jj";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue