From 51c4de43478b76847c081bb80c44a1be72dddba1 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 14 Oct 2020 15:56:10 +0800 Subject: [PATCH] initial commit --- .gitignore | 4 + LICENSE | 165 ++++++++++++++++++++++++++++ README.md | 61 +++++++++++ default.nix | 87 +++++++++++++++ local_run.sh | 48 ++++++++ remote_run.sh | 64 +++++++++++ shell.nix | 33 ++++++ src/.cargo/config | 9 ++ src/Cargo.lock | 213 ++++++++++++++++++++++++++++++++++++ src/Cargo.toml | 14 +++ src/Makefile | 14 +++ src/armv7-none-eabihf.json | 28 +++++ src/firmware/Cargo.toml | 15 +++ src/firmware/build.rs | 18 +++ src/firmware/link.x | 72 ++++++++++++ src/firmware/src/main.rs | 85 ++++++++++++++ src/gateware/rust-pitaya.py | 52 +++++++++ zynq-rs.nix | 8 ++ 18 files changed, 990 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 default.nix create mode 100755 local_run.sh create mode 100755 remote_run.sh create mode 100644 shell.nix create mode 100644 src/.cargo/config create mode 100644 src/Cargo.lock create mode 100644 src/Cargo.toml create mode 100644 src/Makefile create mode 100644 src/armv7-none-eabihf.json create mode 100644 src/firmware/Cargo.toml create mode 100644 src/firmware/build.rs create mode 100644 src/firmware/link.x create mode 100644 src/firmware/src/main.rs create mode 100755 src/gateware/rust-pitaya.py create mode 100644 zynq-rs.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..11232fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +result +__pycache__ + +build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f2e6d47 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +Rust Pitaya +=========== + +Minimalist bare metal Rust firmware for Red Pitaya. + +Development instructions +------------------------ + +Configure Nix channels: + +```shell +nix-channel --add https://nixbld.m-labs.hk/channel/custom/artiq/fast-beta/artiq-fast +nix-channel --update +``` + +Notes: + +- Rust Pitaya does not depend on ARTIQ but uses some packages available in the ARTIQ channel. +- If you are using Nix channels the first time, you need to be aware of this bug: https://github.com/NixOS/nix/issues/3831. + +Pure build with Nix and execution on a remote JTAG server: + +```shell +nix-build -A rust-pitaya-jtag +./remote_run.sh +``` + +Impure incremental build and execution on a remote JTAG server: + +```shell +nix-shell +cd src +gateware/rust-pitaya.py -g ../build/gateware # build gateware +make # build firmware +cd .. +./remote_run.sh -i +``` + +Notes: + +- This is developed with Nixpkgs 20.09, 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. +- If the board is connected to the local machine, use the ``local_run.sh`` script. + +License +------- + +Copyright (C) 2019-2020 M-Labs Limited. + +Rust Pitaya is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Rust Pitaya is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with Rust Pitaya. If not, see . diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..d83c909 --- /dev/null +++ b/default.nix @@ -0,0 +1,87 @@ +let + zynq-rs = (import ./zynq-rs.nix); + pkgs = import { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; }; + rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; }); + cargo-xbuild = (import zynq-rs).cargo-xbuild; + rust-pitaya-szl = (import zynq-rs).rust-pitaya-szl; + mkbootimage = import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; }; + artiqpkgs = import { inherit pkgs; }; + vivado = import { inherit pkgs; }; +in rec { + rust-pitaya-firmware = rustPlatform.buildRustPackage { + name = "rust-pitaya-firmware"; + + src = ./src; + cargoSha256 = "097f4y54cc5m0pib08ypzwbf221knj5a079izixkhhz66g5fmgsq"; + + nativeBuildInputs = [ + pkgs.gnumake + (pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc artiq ]))) + cargo-xbuild + pkgs.llvmPackages_9.llvm + pkgs.llvmPackages_9.clang-unwrapped + ]; + buildPhase = '' + export XARGO_RUST_SRC="${rustPlatform.rust.rustc.src}/library" + export CARGO_HOME=$(mktemp -d cargo-home.XXX) + make + ''; + + installPhase = '' + mkdir -p $out $out/nix-support + cp ../build/runtime.bin $out/firmware.bin + cp ../build/firmware/armv7-none-eabihf/release/firmware $out/firmware.elf + echo file binary-dist $out/firmware.bin >> $out/nix-support/hydra-build-products + echo file binary-dist $out/firmware.elf >> $out/nix-support/hydra-build-products + ''; + + doCheck = false; + dontFixup = true; + }; + rust-pitaya-gateware = pkgs.runCommand "rust-pitaya-gateware" + { + nativeBuildInputs = [ + (pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc ]))) + vivado + ]; + } + '' + python ${./src/gateware}/zc706.py -g build + mkdir -p $out $out/nix-support + cp build/top.bit $out + echo file binary-dist $out/top.bit >> $out/nix-support/hydra-build-products + ''; + + + rust-pitaya-jtag = pkgs.runCommand "rust-pitaya-jtag" {} + '' + mkdir $out + ln -s ${rust-pitaya-szl}/szl.elf $out + ln -s ${rust-pitaya-firmware}/firmware.bin $out + ln -s ${rust-pitaya-gateware}/top.bit $out + ''; + sd = pkgs.runCommand "rust-pitaya-sd" + { + buildInputs = [ mkbootimage ]; + } + '' + # Do not use "long" paths in boot.bif, because embedded developers + # can't write software (mkbootimage will segfault). + bifdir=`mktemp -d` + cd $bifdir + ln -s ${rust-pitaya-szl}/szl.elf szl.elf + ln -s ${rust-pitaya-firmware}/runtime.elf runtime.elf + ln -s ${rust-pitaya-gateware}/top.bit top.bit + cat > boot.bif << EOF + the_ROM_image: + { + [bootloader]szl.elf + top.bit + runtime.elf + } + EOF + mkdir $out $out/nix-support + mkbootimage boot.bif $out/boot.bin + echo file binary-dist $out/boot.bin >> $out/nix-support/hydra-build-products + ''; +} diff --git a/local_run.sh b/local_run.sh new file mode 100755 index 0000000..81ed04a --- /dev/null +++ b/local_run.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e + +if [ -z "$OPENOCD_ZYNQ" ]; then + echo "OPENOCD_ZYNQ environment variable must be set" + exit 1 +fi +if [ -z "$SZL" ]; then + echo "SZL environment variable must be set" + exit 1 +fi + +impure=0 +load_bitstream=1 +board_host="192.168.1.55" + +while getopts "ilb:" opt; do + case "$opt" in + \?) exit 1 + ;; + i) impure=1 + ;; + l) load_bitstream=0 + ;; + b) board_host=$OPTARG + ;; + esac +done + +load_bitstream_cmd="" + +build_dir=`pwd`/build +result_dir=`pwd`/result +cd $OPENOCD_ZYNQ +openocd -f redpitaya.cfg -c "load_image $SZL; resume 0; exit" +sleep 5 +if [ $impure -eq 1 ]; then + if [ $load_bitstream -eq 1 ]; then + load_bitstream_cmd="-g $build_dir/gateware/top.bit" + fi + artiq_netboot $load_bitstream_cmd -f $build_dir/runtime.bin -b $board_host +else + if [ $load_bitstream -eq 1 ]; then + load_bitstream_cmd="-g $result_dir/top.bit" + fi + artiq_netboot $load_bitstream_cmd -f $result_dir/runtime.bin -b $board_host +fi diff --git a/remote_run.sh b/remote_run.sh new file mode 100755 index 0000000..e8e8017 --- /dev/null +++ b/remote_run.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +set -e + +if [ -z "$OPENOCD_ZYNQ" ]; then + echo "OPENOCD_ZYNQ environment variable must be set" + exit 1 +fi +if [ -z "$SZL" ]; then + echo "SZL environment variable must be set" + exit 1 +fi + +target_host="rpi-4.m-labs.hk" +impure=0 +pure_dir="result" +impure_dir="build" +sshopts="" +load_bitstream=1 +board_host="192.168.1.52" + +while getopts "h:id:o:l" opt; do + case "$opt" in + \?) exit 1 + ;; + h) target_host=$OPTARG + ;; + i) impure=1 + ;; + d) pure_dir=$OPTARG; + impure_dir=$OPTARG; + ;; + o) sshopts=$OPTARG + ;; + l) load_bitstream=0 + ;; + b) board_host=$OPTARG + ;; + esac +done + +target_folder="/tmp/zynq-$USER" +load_bitstream_cmd="" + +echo "Creating $target_folder..." +ssh $sshopts $target_host "mkdir -p $target_folder" +echo "Copying files..." +rsync -e "ssh $sshopts" -Lc $OPENOCD_ZYNQ/* $target_host:$target_folder +rsync -e "ssh $sshopts" -Lc $SZL $target_host:$target_folder +if [ $impure -eq 1 ]; then + if [ $load_bitstream -eq 1 ]; then + load_bitstream_cmd="-g build/gateware/top.bit" + fi + firmware="build/runtime.bin" +else + if [ $load_bitstream -eq 1 ]; then + load_bitstream_cmd="-g $pure_dir/top.bit" + fi + firmware="$pure_dir/runtime.bin" +fi +echo "Programming board..." +ssh $sshopts $target_host "cd $target_folder; openocd -f zc706.cfg -c'load_image szl.elf; resume 0; exit'" +sleep 5 +artiq_netboot $load_bitstream_cmd -f $firmware -b $board_host diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..2cc7795 --- /dev/null +++ b/shell.nix @@ -0,0 +1,33 @@ +let + zynq-rs = (import ./zynq-rs.nix); + pkgs = import { overlays = [ (import "${zynq-rs}/nix/mozilla-overlay.nix") ]; }; + rustPlatform = (import "${zynq-rs}/nix/rust-platform.nix" { inherit pkgs; }); + cargo-xbuild = (import zynq-rs).cargo-xbuild; + artiq-fast = ; + artiqpkgs = import "${artiq-fast}/default.nix" { inherit pkgs; }; + vivado = import "${artiq-fast}/vivado.nix" { inherit pkgs; }; + redpitaya-szl = (import zynq-rs).redpitaya-szl; +in + pkgs.stdenv.mkDerivation { + name = "rust-pitaya-env"; + buildInputs = [ + pkgs.gnumake + rustPlatform.rust.rustc + rustPlatform.rust.cargo + pkgs.llvmPackages_9.llvm + pkgs.cacert + cargo-xbuild + + pkgs.openocd + pkgs.openssh pkgs.rsync + + (pkgs.python3.withPackages(ps: (with artiqpkgs; [ migen migen-axi misoc ]))) + vivado + + (import "${zynq-rs}/nix/mkbootimage.nix" { inherit pkgs; }) + ]; + + XARGO_RUST_SRC = "${rustPlatform.rust.rustc.src}/library"; + OPENOCD_ZYNQ = "${zynq-rs}/openocd"; + SZL = "${redpitaya-szl}/szl.elf"; + } diff --git a/src/.cargo/config b/src/.cargo/config new file mode 100644 index 0000000..2b26448 --- /dev/null +++ b/src/.cargo/config @@ -0,0 +1,9 @@ +[target.armv7-none-eabihf] +rustflags = [ + "-C", "link-arg=-Tlink.x", + "-C", "target-feature=a9,armv7-a,neon", + "-C", "target-cpu=cortex-a9", +] + +[build] +target = "armv7-none-eabihf.json" diff --git a/src/Cargo.lock b/src/Cargo.lock new file mode 100644 index 0000000..18bbcb3 --- /dev/null +++ b/src/Cargo.lock @@ -0,0 +1,213 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "compiler_builtins" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" + +[[package]] +name = "core_io" +version = "0.1.20200410" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#6266d280951c3fe5d4963a4b1ca45ce369d6b773" +dependencies = [ + "memchr", +] + +[[package]] +name = "embedded-hal" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "fatfs" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93079df23039e52059e1f03b4c29fb0c72da2c792aad91bb2236c9fb81d3592e" +dependencies = [ + "bitflags", + "byteorder", + "core_io", + "log", +] + +[[package]] +name = "firmware" +version = "0.1.0" +dependencies = [ + "libboard_zynq", + "libconfig", + "libcortex_a9", + "libregister", + "libsupport_zynq", + "log", +] + +[[package]] +name = "libboard_zynq" +version = "0.0.0" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#80d12d57802b9b78aa45b821a147f71444d401f0" +dependencies = [ + "bit_field", + "embedded-hal", + "libcortex_a9", + "libregister", + "log", + "nb 0.1.3", + "smoltcp", + "void", + "volatile-register", +] + +[[package]] +name = "libconfig" +version = "0.1.0" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#80d12d57802b9b78aa45b821a147f71444d401f0" +dependencies = [ + "core_io", + "fatfs", + "libboard_zynq", + "log", +] + +[[package]] +name = "libcortex_a9" +version = "0.0.0" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#80d12d57802b9b78aa45b821a147f71444d401f0" +dependencies = [ + "bit_field", + "libregister", + "volatile-register", +] + +[[package]] +name = "libregister" +version = "0.0.0" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#80d12d57802b9b78aa45b821a147f71444d401f0" +dependencies = [ + "bit_field", + "vcell", + "volatile-register", +] + +[[package]] +name = "libsupport_zynq" +version = "0.0.0" +source = "git+https://git.m-labs.hk/M-Labs/zynq-rs.git#80d12d57802b9b78aa45b821a147f71444d401f0" +dependencies = [ + "compiler_builtins", + "libboard_zynq", + "libcortex_a9", + "libregister", + "linked_list_allocator", + "r0", +] + +[[package]] +name = "linked_list_allocator" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84565678e403453d1a27a0886882b3b271701e65146d972d9d7d9a4c4a0ff498" + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "managed" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.0.0", +] + +[[package]] +name = "nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" + +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "smoltcp" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe46639fd2ec79eadf8fe719f237a7a0bd4dac5d957f1ca5bbdbc1c3c39e53a" +dependencies = [ + "bitflags", + "byteorder", + "managed", +] + +[[package]] +name = "vcell" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" +dependencies = [ + "vcell", +] diff --git a/src/Cargo.toml b/src/Cargo.toml new file mode 100644 index 0000000..b96e597 --- /dev/null +++ b/src/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = [ + "firmware" +] + +[profile.release] +panic = "abort" +debug = true +codegen-units = 1 +opt-level = 2 +lto = true + +[patch.crates-io] +core_io = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git" } diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..f61ec1c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,14 @@ +all: ../build/firmware/armv7-none-eabihf/release/firmware ../build/firmware.bin + +.PHONY: all + + +../build/pl.rs: gateware/* + mkdir -p ../build + python gateware/rust-pitaya.py -r ../build/pl.rs + +../build/firmware/armv7-none-eabihf/release/firmware: ../build/pl.rs $(shell find . -print) + XBUILD_SYSROOT_PATH=`pwd`/../build/sysroot cargo xbuild --release -p firmware --target-dir ../build/firmware + +../build/firmware.bin: ../build/firmware/armv7-none-eabihf/release/firmware + llvm-objcopy -O binary ../build/firmware/armv7-none-eabihf/release/firmware ../build/firmware.bin diff --git a/src/armv7-none-eabihf.json b/src/armv7-none-eabihf.json new file mode 100644 index 0000000..4fc452f --- /dev/null +++ b/src/armv7-none-eabihf.json @@ -0,0 +1,28 @@ +{ + "abi-blacklist": [ + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "win64", + "sysv64" + ], + "arch": "arm", + "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + "emit-debug-gdb-scripts": false, + "env": "", + "executables": true, + "features": "+v7,+vfp3,-d32,+thumb2,-neon", + "is-builtin": false, + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "llvm-target": "armv7-unknown-none-eabihf", + "max-atomic-width": 32, + "os": "none", + "panic-strategy": "abort", + "relocation-model": "static", + "target-c-int-width": "32", + "target-endian": "little", + "target-pointer-width": "32", + "vendor": "" +} diff --git a/src/firmware/Cargo.toml b/src/firmware/Cargo.toml new file mode 100644 index 0000000..0c90a0f --- /dev/null +++ b/src/firmware/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "firmware" +description = "Rust Pitaya firmware" +version = "0.1.0" +authors = ["M-Labs"] +edition = "2018" + +[dependencies] +log = "0.4" + +libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["target_redpitaya", "ipv6"] } +libsupport_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["target_redpitaya"] } +libcortex_a9 = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git", features = ["power_saving"] } +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 = ["target_redpitaya", "ipv6"] } diff --git a/src/firmware/build.rs b/src/firmware/build.rs new file mode 100644 index 0000000..a2ce29f --- /dev/null +++ b/src/firmware/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("link.x")) + .unwrap() + .write_all(include_bytes!("link.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when link.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=link.x"); +} diff --git a/src/firmware/link.x b/src/firmware/link.x new file mode 100644 index 0000000..0a6f853 --- /dev/null +++ b/src/firmware/link.x @@ -0,0 +1,72 @@ +ENTRY(Reset); + +MEMORY +{ + SDRAM : ORIGIN = 0x00100000, LENGTH = 0x1FF00000 +} + +SECTIONS +{ + __text_start = .; + .text : + { + KEEP(*(.text.exceptions)); + *(.text.boot); + *(.text .text.*); + } > SDRAM + __text_end = .; + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > SDRAM + __exidx_end = .; + + .ARM.extab : + { + * (.ARM.extab*) + } > SDRAM + + .rodata : ALIGN(4) + { + *(.rodata .rodata.*); + } > SDRAM + + .data : ALIGN(4) + { + *(.data .data.*); + } > SDRAM + + .bss (NOLOAD) : ALIGN(4) + { + __bss_start = .; + *(.bss .bss.*); + . = ALIGN(4); + __bss_end = .; + } > SDRAM + + .heap (NOLOAD) : ALIGN(8) + { + __heap0_start = .; + . += 0x8000000; + __heap0_end = .; + __heap1_start = .; + . += 0x8000000; + __heap1_end = .; + } > SDRAM + + .stack1 (NOLOAD) : ALIGN(8) + { + __stack1_end = .; + . += 0x1000000; + __stack1_start = .; + } > SDRAM + + .stack0 (NOLOAD) : ALIGN(8) + { + __stack0_end = .; + . += 0x20000; + __stack0_start = .; + } > SDRAM +} diff --git a/src/firmware/src/main.rs b/src/firmware/src/main.rs new file mode 100644 index 0000000..cff090b --- /dev/null +++ b/src/firmware/src/main.rs @@ -0,0 +1,85 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use core::{cmp, str}; +use log::{info, warn}; + +use libcortex_a9::asm; +use libboard_zynq::{timer::GlobalTimer, slcr}; +use libsupport_zynq::ram; +use libconfig::Config; +use libregister::RegisterW; + +#[path = "../../../build/pl.rs"] +mod pl; + +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() + ); + }); +} + +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]) + } +} + + +#[no_mangle] +pub fn main_core0() { + let mut timer = GlobalTimer::start(); + log::set_max_level(log::LevelFilter::Info); + + info!("Rust Pitaya firmware starting..."); + + ram::init_alloc_core0(); + + init_gateware(); + info!("detected gateware: {}", identifier_read(&mut [0; 64])); + + let cfg = match Config::new() { + Ok(cfg) => cfg, + Err(err) => { + warn!("config initialization failed: {}", err); + Config::new_dummy() + } + }; + + loop { + asm::wfe(); + } +} + +#[no_mangle] +pub fn main_core1() { + loop { + asm::wfe(); + } +} diff --git a/src/gateware/rust-pitaya.py b/src/gateware/rust-pitaya.py new file mode 100755 index 0000000..ccf8651 --- /dev/null +++ b/src/gateware/rust-pitaya.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +import argparse +from operator import itemgetter + +from migen import * +from migen.build.generic_platform import * +from migen_axi.integration.soc_core import SoCCore +from migen_axi.platforms import redpitaya +from misoc.integration import cpu_interface + + +class RustPitaya(SoCCore): + def __init__(self): + platform = redpitaya.Platform() + platform.toolchain.bitstream_commands.extend([ + "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", + ]) + ident = self.__class__.__name__ + 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") + + +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 main(): + parser = argparse.ArgumentParser( + description="Rust Pitaya gateware") + parser.add_argument("-r", default=None, metavar="FILE", + help="build Rust interface into the specified file") + parser.add_argument("-g", default=None, metavar="DIRECTORY", + help="build gateware into the specified directory") + args = parser.parse_args() + + soc = RustPitaya() + soc.finalize() + + if args.r is not None: + write_csr_file(soc, args.r) + if args.g is not None: + soc.build(build_dir=args.g) + + +if __name__ == "__main__": + main() diff --git a/zynq-rs.nix b/zynq-rs.nix new file mode 100644 index 0000000..a10c98c --- /dev/null +++ b/zynq-rs.nix @@ -0,0 +1,8 @@ +let + pkgs = import {}; +in + pkgs.fetchgit { + url = "https://git.m-labs.hk/M-Labs/zynq-rs.git"; + rev = "80d12d57802b9b78aa45b821a147f71444d401f0"; + sha256 = "0q91x3kvhaikgzcz1ki24y6vgx5w5w2dwjikx67z2hx5wzrp6jw0"; + }