init commit
This commit is contained in:
commit
f842673e3a
33
.cargo/def-config.toml
Normal file
33
.cargo/def-config.toml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
[target.armv7a-none-eabihf]
|
||||||
|
runner = "./scripts/runner.sh"
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
"-Ctarget-cpu=cortex-a9",
|
||||||
|
"-Ctarget-feature=+vfp3",
|
||||||
|
"-Ctarget-feature=+neon",
|
||||||
|
"-Clink-arg=-Tlink.x",
|
||||||
|
# If this is not enabled, debugging / stepping can become problematic.
|
||||||
|
"-Cforce-frame-pointers=yes",
|
||||||
|
# Can be useful for debugging.
|
||||||
|
# "-Clink-args=-Map=app.map"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Tier 3 target, so no pre-compiled artifacts included.
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "alloc"]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "armv7a-none-eabihf"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
# The following two env variables need to be set for the supplied runner.sh script to work.
|
||||||
|
|
||||||
|
# Absolute path to the Vitis install directory.
|
||||||
|
# AMD_TOOLS = "/tools/Xilinx/Vitis/2024.1"
|
||||||
|
# Absolute path to the PS7 initialization TCL script.
|
||||||
|
# TCL_INIT_SCRIPT = "/home/$user/$project/$sdt_dir/ps7_init.tcl"
|
||||||
|
|
||||||
|
# Absolute path to the Zynq bitstream file, which will be flashed to the target before
|
||||||
|
# running the application. You only need to do this once for unchanged bitstream as long as you
|
||||||
|
# do not reset the whole board.
|
||||||
|
# ZYNQ_BITSTREAM = "/home/$user/$project/$sdt_dir/bitstream.bit"
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/target
|
||||||
|
/app.map
|
||||||
|
/xsct-output.log
|
||||||
|
/.vscode
|
||||||
|
/Cargo.lock
|
||||||
|
/.cargo/config.toml
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
|
members = [
|
||||||
|
"zynq7000-rt",
|
||||||
|
"zynq7000",
|
||||||
|
"zynq7000-hal",
|
||||||
|
"zynq7000-embassy",
|
||||||
|
"examples/simple",
|
||||||
|
"examples/embassy",
|
||||||
|
"examples/zedboard",
|
||||||
|
]
|
||||||
|
exclude = ["experiments"]
|
201
LICENSE-APACHE
Normal file
201
LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
21
LICENSE-MIT
Normal file
21
LICENSE-MIT
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Robin A. Mueller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
1
NOTICE
Normal file
1
NOTICE
Normal file
@ -0,0 +1 @@
|
|||||||
|
This software contains code developed at the University of Stuttgart.
|
164
README.md
Normal file
164
README.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
Zynq 7000 Bare-Metal Rust Support
|
||||||
|
=========
|
||||||
|
|
||||||
|
This crate collection provides support to write bare-metal Rust applications for the AMD Zynq 7000
|
||||||
|
family of SoCs.
|
||||||
|
|
||||||
|
# List of crates
|
||||||
|
|
||||||
|
This workspace contains the following released crates:
|
||||||
|
|
||||||
|
- The [`zynq7000-rt`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-rt)
|
||||||
|
run-time crate containing basic low-level startup code necessary to boot a Rust app on the
|
||||||
|
Zynq7000.
|
||||||
|
- The [`zynq7000`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000) PAC
|
||||||
|
crate containing basic low-level register definition.
|
||||||
|
- The [`zynq7000-hal`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-hal)
|
||||||
|
HAL crate containing higher-level abstractions on top of the PAC register crate.
|
||||||
|
- The [`zynq7000-embassy`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-embassy)
|
||||||
|
crate containing support for running the embassy-rs RTOS.
|
||||||
|
|
||||||
|
It also contains the following helper crates:
|
||||||
|
|
||||||
|
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples)
|
||||||
|
folder contains various example applications crates using the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`embassy`](https://github.com/embassy-rs/embassy) native Rust RTOS.
|
||||||
|
|
||||||
|
The [`zedboard-fpga-design`](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zedboard-fpga-design)
|
||||||
|
folder contains a sample FPGA design and block design which was used in
|
||||||
|
some of the provided software examples. The project was created with Vivado version 2024.1.
|
||||||
|
The folder contains a README with all the steps required to load this project from a TCL script.
|
||||||
|
|
||||||
|
# Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
|
Use the following command to have a starting `config.toml` file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp .cargo/def-config.toml .cargo/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
You then can adapt the `config.toml` to your needs. For example, you can configure runners
|
||||||
|
to conveniently flash with `cargo run`.
|
||||||
|
|
||||||
|
# Using the sample VS Code files
|
||||||
|
|
||||||
|
Use the following command to have a starting configuration for VS Code:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp -rT vscode .vscode
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then adapt the files in `.vscode` to your needs.
|
||||||
|
|
||||||
|
# Building the blinky example
|
||||||
|
|
||||||
|
Building an application requires `nightly` to build the `core` and `alloc` library
|
||||||
|
because the `thumbv7a-none-eabihf` Rust target only has Tier 3 support
|
||||||
|
If you have not installed it yet, you can do so with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rustup toolchain install nightly
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming you have the following segments inside your `.cargo/config.toml`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[target.armv7a-none-eabihf]
|
||||||
|
rustflags = [
|
||||||
|
"-Ctarget-cpu=cortex-a9",
|
||||||
|
"-Ctarget-feature=+vfp3",
|
||||||
|
"-Ctarget-feature=+neon",
|
||||||
|
"-Clink-arg=-Tlink.x",
|
||||||
|
# If this is not enabled, debugging / stepping can become problematic.
|
||||||
|
"-Cforce-frame-pointers=yes",
|
||||||
|
# Can be useful for debugging.
|
||||||
|
# "-Clink-args=-Map=app.map"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Tier 3 target, so no pre-compiled artifacts included.
|
||||||
|
[unstable]
|
||||||
|
build-std = ["core", "alloc"]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "armv7a-none-eabihf"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can build the blinky example app using
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --example simple
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Flashing, running and debugging the software
|
||||||
|
|
||||||
|
This repository was only tested with the [Zedboard](https://digilent.com/reference/programmable-logic/zedboard/start)
|
||||||
|
but should be easily adaptable to other Zynq7000 based platforms and boards.
|
||||||
|
|
||||||
|
If you want to test and use this crate on a Zedboard, make sure that the board jumpers on the
|
||||||
|
Zedboard are configured for JTAG boot.
|
||||||
|
|
||||||
|
### Pre-Requisites
|
||||||
|
|
||||||
|
- [`xsct`](https://docs.amd.com/r/en-US/ug1165-zynq-embedded-design-tutorial/XSCT-Xilinx-Software-Command-Tool)
|
||||||
|
tool installation. You have to install the [Vitis tool](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vitis.html)
|
||||||
|
to get access to this tool. You can minimize the download size by de-selecting all SoC families
|
||||||
|
that you do not require. Vitis also includes a Vivado installation which is required to do
|
||||||
|
anything with the FPGA.
|
||||||
|
- [`hw_server`](https://docs.amd.com/r/en-US/ug908-vivado-programming-debugging/Connecting-to-a-Hardware-Target-Using-hw_server)
|
||||||
|
installation which is generally included with Vitis/Vivado. This tool starts a GDB server
|
||||||
|
which will be used to connect to and debug the target board. It also allows initializing the
|
||||||
|
processing system and uploading the FPGA design via JTAG.
|
||||||
|
- A valid `ps7_init.tcl` script to configure the processing system. This is necessary to even
|
||||||
|
be able flashing application into the DDR via JTAG. There are multiple ways to generate this
|
||||||
|
startup script, but the recommended way here is to the the `sdtgen` tool included in `xsct` which
|
||||||
|
also generates a bitstream for the FPGA configuration. You can find a sample `ps7_init.tcl`
|
||||||
|
script inside the `scripts` folder. However, it is strongly recommended to get familiar with
|
||||||
|
Vivado and generate the SDT folder yourself.
|
||||||
|
- `gdb-multiarch` installation to debug applications.
|
||||||
|
- `python3` installation to use the provided tooling.
|
||||||
|
|
||||||
|
### Programming and Debug Flow
|
||||||
|
|
||||||
|
1. Start the `hw_server` application first. This is required for other tooling provided by this
|
||||||
|
repository as well.
|
||||||
|
|
||||||
|
2. The provided `scripts/zynq7000-init.py` script can be used to initialize the processing system
|
||||||
|
with the `ps7_init.tcl` script, program the bitstream, and load an ELF file to the DDR.
|
||||||
|
You can run `scripts/zynq7000-init.py` to get some help and additional information.
|
||||||
|
Here is an example command to initialize the processing system without loading a bitstream
|
||||||
|
using the provided initialization script (adapt `AMD_TOOLS` to your system):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export AMD_TOOLS="/tools/Xilinx/Vitis/2024.1"
|
||||||
|
./scripts/zynq7000-init.py --itcl ./scripts/ps7_init.tcl
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Assuming you have managed to build the blinky example, you can flash the example now
|
||||||
|
using GDB:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gdb-multiarch -q -x gdb.gdb target/armv7a-none-eabihf/debug/blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use the `-tui` argument to also have a terminal UI.
|
||||||
|
This repository provides a `scripts/runner.sh` which performs all the steps specified above.
|
||||||
|
The `.cargo/def-config.toml` script contains the runner and some template environmental
|
||||||
|
variables that need to be set for this to work. The command above also loaded the app, but
|
||||||
|
this task can be performed by the `zynq7000-init.py` wrapper as well.
|
||||||
|
|
||||||
|
# Using VS Code
|
||||||
|
|
||||||
|
The provided VS Code configuration files can be used to perform the CLI steps specified above
|
||||||
|
in a GUI completely and to also have a graphical debugging interface. You can use this
|
||||||
|
as a starting point for your own application. You need to adapt the `options.env` variables
|
||||||
|
in `.vscode/tasks.json` for this to work.
|
||||||
|
|
||||||
|
# Embedded Rust
|
||||||
|
|
||||||
|
If you have not done this yet, it is recommended to read some of the excellent resources available
|
||||||
|
to learn Rust:
|
||||||
|
|
||||||
|
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
|
||||||
|
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
|
39
examples/embassy/Cargo.toml
Normal file
39
examples/embassy/Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "Embassy examples for the Zynq7000 SoC"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-ar = { path = "/home/rmueller/Rust/cortex-ar/cortex-ar", features = ["critical-section-single-core"] }
|
||||||
|
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||||
|
zynq7000 = { path = "../../zynq7000" }
|
||||||
|
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||||
|
zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||||
|
# dht-sensor = { git = "https://github.com/robamu/dht-sensor.git", branch = "bump-embedded-hal-deps-update-async", features = ["async"] }
|
||||||
|
dht-sensor = { path = "../../../../Rust/dht-sensor", features = ["async"] }
|
||||||
|
static_cell = "2"
|
||||||
|
critical-section = "1"
|
||||||
|
heapless = "0.8"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-hal = "1"
|
||||||
|
fugit = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
embassy-executor = { path = "/home/rmueller/Rust/embassy/embassy-executor", features = [
|
||||||
|
"arch-cortex-ar",
|
||||||
|
"executor-thread",
|
||||||
|
"task-arena-size-65536"
|
||||||
|
]}
|
||||||
|
embassy-time = { path = "/home/rmueller/Rust/embassy/embassy-time", version = "0.4", features = ["tick-hz-1_000_000"] }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = true
|
||||||
|
lto = true
|
207
examples/embassy/src/bin/dht22-open-drain-pins.rs
Normal file
207
examples/embassy/src/bin/dht22-open-drain-pins.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
//! Open-drain pin mode examples
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Delay, Duration, Ticker};
|
||||||
|
use embedded_hal::{delay::DelayNs, digital::StatefulOutputPin};
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{mio, Flex, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
BootMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::PsPeripherals;
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to talk to a DHT22 sensor connected at MIO0.
|
||||||
|
const DHT22_AT_MIO0: bool = true;
|
||||||
|
|
||||||
|
/// Open drain pin testing. MIO9 needs to be tied to MIO14.
|
||||||
|
const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false;
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 DHT22 --\n\r").unwrap();
|
||||||
|
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut delay = Delay;
|
||||||
|
|
||||||
|
let mut one_wire_pin = Flex::new_for_mio(mio_pins.mio0);
|
||||||
|
one_wire_pin.configure_as_output_open_drain(PinState::High, true);
|
||||||
|
|
||||||
|
if OPEN_DRAIN_PINS_MIO9_TO_MIO14 {
|
||||||
|
let mut flex_pin_0 = Flex::new_for_mio(mio_pins.mio9);
|
||||||
|
flex_pin_0.configure_as_output_open_drain(PinState::High, true);
|
||||||
|
let mut flex_pin_1 = Flex::new_for_mio(mio_pins.mio14);
|
||||||
|
flex_pin_1.configure_as_input_floating().unwrap();
|
||||||
|
// Should be high because of pull up.
|
||||||
|
info!(
|
||||||
|
"Flex Pin 1 state (should be high): {}",
|
||||||
|
flex_pin_1.is_high()
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"Flex Pin 0 state (should be high): {}",
|
||||||
|
flex_pin_0.is_high()
|
||||||
|
);
|
||||||
|
flex_pin_0.set_low();
|
||||||
|
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
|
||||||
|
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
|
||||||
|
flex_pin_0.set_high();
|
||||||
|
delay.delay_us(5);
|
||||||
|
info!(
|
||||||
|
"Flex Pin 1 state (should be high): {}",
|
||||||
|
flex_pin_1.is_high()
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"Flex Pin 0 state (should be high): {}",
|
||||||
|
flex_pin_0.is_high()
|
||||||
|
);
|
||||||
|
|
||||||
|
flex_pin_1.configure_as_output_open_drain(PinState::Low, true);
|
||||||
|
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
|
||||||
|
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
|
||||||
|
|
||||||
|
flex_pin_1.set_high();
|
||||||
|
delay.delay_us(5);
|
||||||
|
info!(
|
||||||
|
"Flex Pin 1 state (should be high): {}",
|
||||||
|
flex_pin_1.is_high()
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"Flex Pin 0 state (should be high): {}",
|
||||||
|
flex_pin_0.is_high()
|
||||||
|
);
|
||||||
|
|
||||||
|
flex_pin_1.set_low();
|
||||||
|
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
|
||||||
|
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
|
||||||
|
}
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
loop {
|
||||||
|
if DHT22_AT_MIO0 {
|
||||||
|
let result = dht_sensor::dht22::asynch::read(&mut delay, &mut one_wire_pin).await;
|
||||||
|
match result {
|
||||||
|
Ok(reading) => {
|
||||||
|
info!("Temperature: {} C", reading.temperature);
|
||||||
|
info!("Humidity: {} %", reading.relative_humidity);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("Reading error: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
led.toggle().unwrap();
|
||||||
|
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
149
examples/embassy/src/bin/logger-non-blocking.rs
Normal file
149
examples/embassy/src/bin/logger-non-blocking.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
//! Example which uses the UART1 to send log messages.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000::PsPeripherals;
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{Output, PinState, mio},
|
||||||
|
gtc::Gtc,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, TxAsync, Uart, UartConfig, on_interrupt_tx},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) -> ! {
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
uart.flush().unwrap();
|
||||||
|
let (tx, _rx) = uart.split();
|
||||||
|
let mut logger = TxAsync::new(tx);
|
||||||
|
|
||||||
|
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
spawner.spawn(led_task(led)).unwrap();
|
||||||
|
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||||
|
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
|
||||||
|
loop {
|
||||||
|
let next_frame_len = frame_queue.receive().await;
|
||||||
|
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
|
||||||
|
logger.write(&log_buf[0..next_frame_len]).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn led_task(mut mio_led: Output) {
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
info!("Toggling LED");
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(spi_interrupt) => {
|
||||||
|
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
|
||||||
|
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
157
examples/embassy/src/bin/pwm.rs
Normal file
157
examples/embassy/src/bin/pwm.rs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
//! PWM example which uses a PWM pin routed through EMIO.
|
||||||
|
//!
|
||||||
|
//! This example puts the PWM output on the EMIO channel of TTC0 channel 0.
|
||||||
|
//!
|
||||||
|
//! On the Zedboard, the PWM waveform output will be on the W12 pin of PMOD JB1. The Zedboard
|
||||||
|
//! reference FPGA design must be flashed onto the Zedboard for this to work.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_hal::{digital::StatefulOutputPin, pwm::SetDutyCycle};
|
||||||
|
use embedded_io::Write;
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{Output, PinState, mio},
|
||||||
|
gtc::Gtc,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::PsPeripherals;
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Unwrap is okay, the address is definitely valid.
|
||||||
|
let ttc_0 = zynq7000_hal::ttc::Ttc::new(dp.ttc_0).unwrap();
|
||||||
|
let mut pwm =
|
||||||
|
zynq7000_hal::ttc::Pwm::new_with_cpu_clk(ttc_0.ch0, clocks.arm_clocks(), 1000.Hz())
|
||||||
|
.unwrap();
|
||||||
|
pwm.set_duty_cycle_percent(50).unwrap();
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
let mut current_duty = 0;
|
||||||
|
loop {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
|
||||||
|
pwm.set_duty_cycle_percent(current_duty).unwrap();
|
||||||
|
info!("Setting duty cycle to {}%", current_duty);
|
||||||
|
current_duty += 5;
|
||||||
|
if current_duty > 100 {
|
||||||
|
current_duty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
136
examples/embassy/src/main.rs
Normal file
136
examples/embassy/src/main.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{Output, PinState, mio},
|
||||||
|
gtc::Gtc,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::PsPeripherals;
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The export name is ignored..
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
loop {
|
||||||
|
info!("Hello, world!");
|
||||||
|
led.toggle().unwrap();
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
24
examples/simple/Cargo.toml
Normal file
24
examples/simple/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "blinky"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "Simple examples for the Zynq7000 SoC"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-ar = { git = "https://github.com/us-irs/cortex-ar.git", branch = "cortex-a-addition", features = ["critical-section-single-core"] }
|
||||||
|
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||||
|
zynq7000 = { path = "../../zynq7000" }
|
||||||
|
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-hal = "1"
|
||||||
|
fugit = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = true
|
||||||
|
lto = true
|
136
examples/simple/src/bin/gtc-ticks.rs
Normal file
136
examples/simple/src/bin/gtc-ticks.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
//! Example which uses the global timer for a simple tick counter.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{Output, PinState, mio},
|
||||||
|
gtc::Gtc,
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
pub fn main() -> ! {
|
||||||
|
let dp = zynq7000::PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
// Enable interrupt exception.
|
||||||
|
unsafe { gic.enable_interrupts() };
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
let ticks = gtc.frequency_to_ticks(1000.Hz());
|
||||||
|
gtc.set_auto_increment_value(ticks);
|
||||||
|
gtc.set_comparator(gtc.read_timer() + ticks as u64);
|
||||||
|
gtc.enable_auto_increment();
|
||||||
|
gtc.enable_interrupt();
|
||||||
|
gtc.enable();
|
||||||
|
|
||||||
|
// This structure holds all MIO pins.
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
loop {
|
||||||
|
info!(
|
||||||
|
"MS_TICKS: {}",
|
||||||
|
MS_TICKS.load(core::sync::atomic::Ordering::Relaxed)
|
||||||
|
);
|
||||||
|
led.toggle().unwrap();
|
||||||
|
for _ in 0..5_000_000 {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
139
examples/simple/src/bin/logger.rs
Normal file
139
examples/simple/src/bin/logger.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
//! Example which uses the UART1 to send log messages.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{Output, PinState, mio},
|
||||||
|
gtc::Gtc,
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
pub fn main() -> ! {
|
||||||
|
let dp = zynq7000::PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
// Enable interrupt exception.
|
||||||
|
unsafe { gic.enable_interrupts() };
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
let ticks = gtc.frequency_to_ticks(1000.Hz());
|
||||||
|
gtc.set_auto_increment_value(ticks);
|
||||||
|
gtc.set_comparator(gtc.read_timer() + ticks as u64);
|
||||||
|
gtc.enable_auto_increment();
|
||||||
|
gtc.enable_interrupt();
|
||||||
|
gtc.enable();
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(mio_pins.mio48, mio_pins.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||||
|
loop {
|
||||||
|
let gtc = gtc.read_timer();
|
||||||
|
info!("Hello, world!");
|
||||||
|
info!("GTC ticks: {}", gtc);
|
||||||
|
led.toggle().unwrap();
|
||||||
|
for _ in 0..5_000_000 {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
// TODO: Call embassy on interrupt handler here soon.
|
||||||
|
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
90
examples/simple/src/main.rs
Normal file
90
examples/simple/src/main.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//! Simple blinky app, showing a PAC variant and a HAL variant.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use zynq7000::PsPeripherals;
|
||||||
|
use zynq7000_hal::gpio::{Output, PinState, mio};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
/// One user LED is MIO7
|
||||||
|
const ZEDBOARD_LED_MASK: u32 = 1 << 7;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Lib {
|
||||||
|
Pac,
|
||||||
|
Hal,
|
||||||
|
}
|
||||||
|
|
||||||
|
const LIB: Lib = Lib::Hal;
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
pub fn main() -> ! {
|
||||||
|
match LIB {
|
||||||
|
Lib::Pac => {
|
||||||
|
let mut gpio = unsafe { zynq7000::gpio::Gpio::new_mmio_fixed() };
|
||||||
|
gpio.bank_0().modify_dirm(|v| v | ZEDBOARD_LED_MASK);
|
||||||
|
gpio.bank_0().modify_out_en(|v| v | ZEDBOARD_LED_MASK);
|
||||||
|
loop {
|
||||||
|
gpio.modify_out_0(|v| v ^ ZEDBOARD_LED_MASK);
|
||||||
|
for _ in 0..5_000_000 {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Lib::Hal => {
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
let mio_pins = mio::Pins::new(dp.gpio);
|
||||||
|
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::High);
|
||||||
|
loop {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
for _ in 0..5_000_000 {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
43
examples/zedboard/Cargo.toml
Normal file
43
examples/zedboard/Cargo.toml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
[package]
|
||||||
|
name = "zedboard"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "Embassy examples for the Zynq7000 SoC"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", features = ["critical-section-single-core"] }
|
||||||
|
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||||
|
zynq7000 = { path = "../../zynq7000" }
|
||||||
|
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||||
|
zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||||
|
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" }
|
||||||
|
embedded-io = "0.6"
|
||||||
|
arbitrary-int = "1.3"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
|
critical-section = "1"
|
||||||
|
static_cell = "2"
|
||||||
|
embedded-alloc = "0.6"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
fugit = "0.3"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
embassy-executor = { path = "/home/rmueller/Rust/embassy/embassy-executor", features = [
|
||||||
|
"arch-cortex-ar",
|
||||||
|
"executor-thread"
|
||||||
|
]}
|
||||||
|
embassy-time = { path = "/home/rmueller/Rust/embassy/embassy-time", version = "0.4" }
|
||||||
|
heapless = "0.8"
|
||||||
|
axi-uartlite = { path = "/home/rmueller/Rust/axi-uartlite-rs" }
|
||||||
|
axi-uart16550 = { path = "/home/rmueller/Rust/axi-uart16550-rs" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = true
|
||||||
|
lto = true
|
203
examples/zedboard/src/bin/l3gd20h-i2c-mio.rs
Normal file
203
examples/zedboard/src/bin/l3gd20h-i2c-mio.rs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//! PS I2C example using a L3GD20H sensor.
|
||||||
|
//!
|
||||||
|
//! External HW connections:
|
||||||
|
//!
|
||||||
|
//! - SCK pin to JE4 (MIO 12)
|
||||||
|
//! - SDA pin to JE1 (MIO 13)
|
||||||
|
//! - SDO / SA0 pin to JE3 (MIO 11) to select I2C address.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Delay, Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_hal_async::delay::DelayNs;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use l3gd20::i2c::I2cAddr;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
configure_level_shifter,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{GpioPins, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
i2c,
|
||||||
|
time::Hertz,
|
||||||
|
uart,
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
const I2C_ADDR_SEL: I2cAddr = I2cAddr::Sa0Low;
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// Enable PS-PL level shifters.
|
||||||
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = uart::ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = uart::Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Zedboard I2C L3GD20H example --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let pin_sel = match I2C_ADDR_SEL {
|
||||||
|
I2cAddr::Sa0Low => PinState::Low,
|
||||||
|
I2cAddr::Sa0High => PinState::High,
|
||||||
|
};
|
||||||
|
let _sa0_pin = Output::new_for_mio(gpio_pins.mio.mio11, pin_sel);
|
||||||
|
// The CS pin must be pulled high.
|
||||||
|
let _cs_pin = Output::new_for_mio(gpio_pins.mio.mio10, PinState::High);
|
||||||
|
|
||||||
|
let clk_config = i2c::calculate_divisors(
|
||||||
|
clocks.arm_clocks().cpu_1x_clk(),
|
||||||
|
i2c::I2cSpeed::Normal100kHz,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let i2c = i2c::I2c::new_with_mio(
|
||||||
|
dp.i2c_1,
|
||||||
|
clk_config,
|
||||||
|
(gpio_pins.mio.mio12, gpio_pins.mio.mio13),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut l3gd20 = l3gd20::i2c::L3gd20::new(i2c, l3gd20::i2c::I2cAddr::Sa0Low).unwrap();
|
||||||
|
let who_am_i = l3gd20.who_am_i().unwrap();
|
||||||
|
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
|
||||||
|
|
||||||
|
let mut delay = Delay;
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(400));
|
||||||
|
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||||
|
|
||||||
|
let mut emio_leds: [Output; 8] = [
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||||
|
];
|
||||||
|
for (idx, led) in emio_leds.iter_mut().enumerate() {
|
||||||
|
if idx % 2 == 0 {
|
||||||
|
led.set_high();
|
||||||
|
} else {
|
||||||
|
led.set_low();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Power up time for the sensor to get good readings.
|
||||||
|
delay.delay_ms(50).await;
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
let measurements = l3gd20.all().unwrap();
|
||||||
|
info!("L3GD20: {:?}", measurements);
|
||||||
|
info!("L3GD20 Temp: {:?}", measurements.temp_celcius());
|
||||||
|
for led in emio_leds.iter_mut() {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
292
examples/zedboard/src/bin/l3gd20h-spi-mio.rs
Normal file
292
examples/zedboard/src/bin/l3gd20h-spi-mio.rs
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
//! PS SPI example using a L3GD20H sensor.
|
||||||
|
//!
|
||||||
|
//! External HW connections:
|
||||||
|
//!
|
||||||
|
//! - SCK pin to JE4 (MIO 12)
|
||||||
|
//! - MOSI pin to JE2 (MIO 10)
|
||||||
|
//! - MISO pin to JE3 (MIO 11)
|
||||||
|
//! - SS pin to JE1 (MIO 13)
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Delay, Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_hal_async::delay::DelayNs;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
configure_level_shifter,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{GpioPins, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt},
|
||||||
|
time::Hertz,
|
||||||
|
uart::{self, TxAsync, on_interrupt_tx},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig, spi::DelayControl};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
const DEBUG_SPI_CLK_CONFIG: bool = false;
|
||||||
|
const BLOCKING: bool = false;
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(spawner: Spawner) -> ! {
|
||||||
|
// Enable PS-PL level shifters.
|
||||||
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
|
||||||
|
// SPI reference clock must be larger than the CPU 1x clock.
|
||||||
|
let spi_ref_clk_div = spi::calculate_largest_allowed_spi_ref_clk_divisor(&clocks)
|
||||||
|
.unwrap()
|
||||||
|
.value()
|
||||||
|
- 1;
|
||||||
|
spi::configure_spi_ref_clk(&mut clocks, arbitrary_int::u6::new(spi_ref_clk_div as u8));
|
||||||
|
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = uart::ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = uart::Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
|
||||||
|
.unwrap();
|
||||||
|
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
if DEBUG_SPI_CLK_CONFIG {
|
||||||
|
info!(
|
||||||
|
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}, DIV: {:?}",
|
||||||
|
clocks.arm_clocks().cpu_1x_clk(),
|
||||||
|
clocks.io_clocks().ref_clk(),
|
||||||
|
clocks.io_clocks().spi_clk(),
|
||||||
|
spi_ref_clk_div
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut spi = spi::Spi::new_one_hw_cs(
|
||||||
|
dp.spi_1,
|
||||||
|
clocks.io_clocks(),
|
||||||
|
spi::Config::new(
|
||||||
|
// 10 MHz maximum rating of the sensor.
|
||||||
|
zynq7000::spi::BaudDivSelect::By64,
|
||||||
|
//l3gd20::MODE,
|
||||||
|
embedded_hal::spi::MODE_3,
|
||||||
|
spi::SlaveSelectConfig::AutoWithAutoStart,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
gpio_pins.mio.mio12,
|
||||||
|
gpio_pins.mio.mio10,
|
||||||
|
gpio_pins.mio.mio11,
|
||||||
|
),
|
||||||
|
gpio_pins.mio.mio13,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mod_id = spi.regs().read_mod_id();
|
||||||
|
assert_eq!(mod_id, spi::MODULE_ID);
|
||||||
|
assert!(spi.sclk() <= Hertz::from_raw(10_000_000));
|
||||||
|
let min_delay = (spi.sclk().raw() * 5) / 1_000_000_000;
|
||||||
|
spi.inner().configure_delays(
|
||||||
|
DelayControl::builder()
|
||||||
|
.with_inter_word_cs_deassert(0)
|
||||||
|
.with_between_cs_assertion(0)
|
||||||
|
.with_inter_word(0)
|
||||||
|
.with_cs_to_first_bit(min_delay as u8)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||||
|
let mut emio_leds: [Output; 8] = [
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||||
|
];
|
||||||
|
for (idx, led) in emio_leds.iter_mut().enumerate() {
|
||||||
|
if idx % 2 == 0 {
|
||||||
|
led.set_high();
|
||||||
|
} else {
|
||||||
|
led.set_low();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawner.spawn(logger_task(uart)).unwrap();
|
||||||
|
if BLOCKING {
|
||||||
|
blocking_application(mio_led, emio_leds, spi).await;
|
||||||
|
} else {
|
||||||
|
non_blocking_application(mio_led, emio_leds, spi).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn logger_task(uart: uart::Uart) {
|
||||||
|
let (tx, _) = uart.split();
|
||||||
|
let mut tx_async = TxAsync::new(tx);
|
||||||
|
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
|
||||||
|
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||||
|
loop {
|
||||||
|
let next_frame_len = frame_queue.receive().await;
|
||||||
|
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
|
||||||
|
tx_async.write(&log_buf[0..next_frame_len]).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn blocking_application(
|
||||||
|
mut mio_led: Output,
|
||||||
|
mut emio_leds: [Output; 8],
|
||||||
|
spi: spi::Spi,
|
||||||
|
) -> ! {
|
||||||
|
let mut delay = Delay;
|
||||||
|
let spi_dev = SpiWithHwCs::new(spi, spi::ChipSelect::Slave0, delay.clone());
|
||||||
|
let mut l3gd20 = l3gd20::spi::L3gd20::new(spi_dev).unwrap();
|
||||||
|
let who_am_i = l3gd20.who_am_i().unwrap();
|
||||||
|
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(400));
|
||||||
|
|
||||||
|
// Power up time for the sensor to get good readings.
|
||||||
|
delay.delay_ms(50).await;
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
let measurements = l3gd20.all().unwrap();
|
||||||
|
info!("L3GD20: {:?}", measurements);
|
||||||
|
info!("L3GD20 Temp: {:?}", measurements.temp_celcius());
|
||||||
|
for led in emio_leds.iter_mut() {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn non_blocking_application(
|
||||||
|
mut mio_led: Output,
|
||||||
|
mut emio_leds: [Output; 8],
|
||||||
|
spi: spi::Spi,
|
||||||
|
) -> ! {
|
||||||
|
let mut delay = Delay;
|
||||||
|
let spi_async = SpiAsync::new(spi);
|
||||||
|
let spi_dev = SpiWithHwCsAsync::new(spi_async, spi::ChipSelect::Slave0, delay.clone());
|
||||||
|
let mut l3gd20 = l3gd20::asynchronous::spi::L3gd20::new(spi_dev)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let who_am_i = l3gd20.who_am_i().await.unwrap();
|
||||||
|
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(400));
|
||||||
|
|
||||||
|
// Power up time for the sensor to get good readings.
|
||||||
|
delay.delay_ms(50).await;
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
let measurements = l3gd20.all().await.unwrap();
|
||||||
|
info!("L3GD20: {:?}", measurements);
|
||||||
|
info!("L3GD20 Temp: {:?}", measurements.temp_celcius());
|
||||||
|
for led in emio_leds.iter_mut() {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(spi_interrupt) => {
|
||||||
|
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Spi1 {
|
||||||
|
on_interrupt(SpiId::Spi1);
|
||||||
|
} else if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
|
||||||
|
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
264
examples/zedboard/src/bin/uart-blocking.rs
Normal file
264
examples/zedboard/src/bin/uart-blocking.rs
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use axi_uart16550::AxiUart16550;
|
||||||
|
use axi_uartlite::AxiUartlite;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
use log::{error, info};
|
||||||
|
use zedboard::PS_CLOCK_FREQUENCY;
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
configure_level_shifter,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{GpioPins, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
const INIT_STRING: &str = "-- Zynq 7000 Zedboard blocking UART example --\n\r";
|
||||||
|
|
||||||
|
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
|
||||||
|
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum UartSel {
|
||||||
|
Uart0 = 0b000,
|
||||||
|
Uartlite = 0b001,
|
||||||
|
Uart16550 = 0b010,
|
||||||
|
Uart0ToUartlite = 0b011,
|
||||||
|
Uart0ToUart16550 = 0b100,
|
||||||
|
UartliteToUart16550 = 0b101,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UartMultiplexer {
|
||||||
|
sel_pins: [Output; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UartMultiplexer {
|
||||||
|
pub fn new(mut sel_pins: [Output; 3]) -> Self {
|
||||||
|
for pin in sel_pins.iter_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
}
|
||||||
|
Self { sel_pins }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(&mut self, sel: UartSel) {
|
||||||
|
// TODO: A pin group switcher would be nice to do this in one go.
|
||||||
|
match sel {
|
||||||
|
UartSel::Uart0 => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::Uartlite => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
UartSel::Uart16550 => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_high();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::Uart0ToUartlite => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_high();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
UartSel::Uart0ToUart16550 => {
|
||||||
|
self.sel_pins[2].set_high();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::UartliteToUart16550 => {
|
||||||
|
self.sel_pins[2].set_high();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// Enable PS-PL level shifters.
|
||||||
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut log_uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// Safety: Co-operative multi-tasking is used.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
log_uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// UART0 routed through EMIO to PL pins.
|
||||||
|
let mut uart_0 =
|
||||||
|
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||||
|
// Safety: Valid address of AXI UARTLITE.
|
||||||
|
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||||
|
|
||||||
|
// TODO: Can we determine/read the clock frequency to the FPGAs as well?
|
||||||
|
let (clk_config, error) =
|
||||||
|
axi_uart16550::ClkConfig::new_autocalc_with_error(100.MHz(), 115200).unwrap();
|
||||||
|
assert!(error < 0.02);
|
||||||
|
let mut uart_16550 = unsafe {
|
||||||
|
AxiUart16550::new(
|
||||||
|
AXI_UAR16550_BASE_ADDR,
|
||||||
|
axi_uart16550::UartConfig::new_with_clk_config(clk_config),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
|
||||||
|
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||||
|
let mut emio_leds: [Output; 8] = [
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut uart_mux = UartMultiplexer::new([
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(8).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
|
||||||
|
]);
|
||||||
|
let mut current_sel = UartSel::Uart0;
|
||||||
|
uart_mux.select(current_sel);
|
||||||
|
let mut led_idx = 0;
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
emio_leds[led_idx].toggle().unwrap();
|
||||||
|
led_idx += 1;
|
||||||
|
if led_idx >= emio_leds.len() {
|
||||||
|
led_idx = 0;
|
||||||
|
}
|
||||||
|
uart_0
|
||||||
|
.write_all("Hello, World from UART0!\n\r".as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
uartlite
|
||||||
|
.write_all("Hello, World from AXI UARTLITE!\n\r".as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
uart_16550
|
||||||
|
.write_all("Hello, World from AXI UART16550!\n\r".as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
uart_0.flush().unwrap();
|
||||||
|
uartlite.flush().unwrap();
|
||||||
|
uart_16550.flush().unwrap();
|
||||||
|
match current_sel {
|
||||||
|
UartSel::Uart0 => current_sel = UartSel::Uartlite,
|
||||||
|
UartSel::Uartlite => current_sel = UartSel::Uart16550,
|
||||||
|
UartSel::Uart16550 => current_sel = UartSel::Uart0,
|
||||||
|
UartSel::Uart0ToUartlite | UartSel::Uart0ToUart16550 | UartSel::UartliteToUart16550 => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uart_mux.select(current_sel);
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
570
examples/zedboard/src/bin/uart-non-blocking.rs
Normal file
570
examples/zedboard/src/bin/uart-non-blocking.rs
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
//! This example application shows usage of the non-blocking APIs offered for
|
||||||
|
//! various UART peripherals which are part of the PS or the sample bitstream.
|
||||||
|
//!
|
||||||
|
//! This example ONLY works with the provided `zedboard-fpga-design`.
|
||||||
|
//! The example FPGA design contains the following components relevant for this design
|
||||||
|
//!
|
||||||
|
//! - An AXI UARTLite peripheral, with the interrupt signal connected to the PS
|
||||||
|
//! - An AXI UART16550 peripheral, with the interrupt signal connected to the PS
|
||||||
|
//! - A custom UART multiplexer component. This multiplexer has various configuration modes
|
||||||
|
//! configurable via 3 EMIO pins:
|
||||||
|
//! 1. Route UART0 RX to pin JA1 and TX to JA2.
|
||||||
|
//! 2. Route AXI UARTLite RX to pin JA1 and TX to JA2.
|
||||||
|
//! 3. Route AXI UART16550 RX to pin JA1 and TX to JA2.
|
||||||
|
//! 4. Route UART0 to AXI UARTLite.
|
||||||
|
//! 5. Route UART0 to AXI 16550.
|
||||||
|
//! 6. Route AXI UARTLite to AXI 16550.
|
||||||
|
//! - Options 4, 5 and 6 allow to test and show the non-blocking API without the need for external
|
||||||
|
//! hardware.
|
||||||
|
//!
|
||||||
|
//! This example runs one embassy task for each UART, which periodically sends variable sized
|
||||||
|
//! string content out via its TX port. Each UART peripheral also permanently listens to all
|
||||||
|
//! incoming RX data via interrupts. Received data will be sent from the interrupt handler
|
||||||
|
//! to the main thread and will be printed to the main console on UART 1.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::format;
|
||||||
|
use axi_uart16550::AxiUart16550;
|
||||||
|
use axi_uartlite::AxiUartlite;
|
||||||
|
use core::{cell::RefCell, panic::PanicInfo};
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_alloc::LlffHeap as Heap;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write as _;
|
||||||
|
use heapless::spsc::Queue;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
configure_level_shifter,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{GpioPins, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
time::Hertz,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum UartMode {
|
||||||
|
Uart0ToUartlite,
|
||||||
|
Uart0ToUart16550,
|
||||||
|
UartliteToUart16550,
|
||||||
|
}
|
||||||
|
|
||||||
|
const UART_MODE: UartMode = UartMode::Uart0ToUart16550;
|
||||||
|
const INIT_STRING: &str = "-- Zynq 7000 Zedboard non-blocking UART example --\n\r";
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static HEAP: Heap = Heap::empty();
|
||||||
|
|
||||||
|
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||||
|
|
||||||
|
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
|
||||||
|
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
|
||||||
|
|
||||||
|
pub const UARTLITE_PL_INT_ID: usize = 0;
|
||||||
|
pub const UART16550_PL_INT_ID: usize = 1;
|
||||||
|
|
||||||
|
const RB_SIZE: usize = 512;
|
||||||
|
|
||||||
|
// These queues are used to send all data received in the UART interrupt handlers to the main
|
||||||
|
// processing thread.
|
||||||
|
static QUEUE_UART_0: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static QUEUE_UARTLITE: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static QUEUE_UART16550: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
|
||||||
|
// Those are all used by the interrupt handler, so we have to do the Mutex dance.
|
||||||
|
static RX_UART_0: Mutex<RefCell<Option<zynq7000_hal::uart::Rx>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum UartSel {
|
||||||
|
Uart0 = 0b000,
|
||||||
|
Uartlite = 0b001,
|
||||||
|
Uart16550 = 0b010,
|
||||||
|
Uart0ToUartlite = 0b011,
|
||||||
|
Uart0ToUart16550 = 0b100,
|
||||||
|
UartliteToUart16550 = 0b101,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UartMultiplexer {
|
||||||
|
sel_pins: [Output; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UartMultiplexer {
|
||||||
|
pub fn new(mut sel_pins: [Output; 3]) -> Self {
|
||||||
|
for pin in sel_pins.iter_mut() {
|
||||||
|
pin.set_low();
|
||||||
|
}
|
||||||
|
Self { sel_pins }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(&mut self, sel: UartSel) {
|
||||||
|
// TODO: A pin group switcher would be nice to do this in one go.
|
||||||
|
match sel {
|
||||||
|
UartSel::Uart0 => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::Uartlite => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
UartSel::Uart16550 => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_high();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::Uart0ToUartlite => {
|
||||||
|
self.sel_pins[2].set_low();
|
||||||
|
self.sel_pins[1].set_high();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
UartSel::Uart0ToUart16550 => {
|
||||||
|
self.sel_pins[2].set_high();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_low();
|
||||||
|
}
|
||||||
|
UartSel::UartliteToUart16550 => {
|
||||||
|
self.sel_pins[2].set_high();
|
||||||
|
self.sel_pins[1].set_low();
|
||||||
|
self.sel_pins[0].set_high();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(spawner: Spawner) -> ! {
|
||||||
|
// Enable PS-PL level shifters.
|
||||||
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
// AXI UARTLite documentation mentions that a rising-edge sensitive interrupt is generated,
|
||||||
|
// but the GIC configures high-level sensitivity for the PL interrupts by default. We
|
||||||
|
// need to change it to edge sensitivity.
|
||||||
|
gic.set_pl_interrupt_sensitivity(UARTLITE_PL_INT_ID, zynq7000_hal::gic::SpiSensitivity::Edge)
|
||||||
|
.unwrap();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut log_uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||||
|
|
||||||
|
// Initialize the allocator BEFORE you use it
|
||||||
|
{
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
// 10 kB heap.
|
||||||
|
const HEAP_SIZE: usize = 1024 * 10;
|
||||||
|
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
||||||
|
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
log_uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up UART multiplexing before creating and configuring the UARTs.
|
||||||
|
let mut uart_mux = UartMultiplexer::new([
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(8).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
|
||||||
|
]);
|
||||||
|
match UART_MODE {
|
||||||
|
UartMode::Uart0ToUartlite => uart_mux.select(UartSel::Uart0ToUartlite),
|
||||||
|
UartMode::Uart0ToUart16550 => uart_mux.select(UartSel::Uart0ToUart16550),
|
||||||
|
UartMode::UartliteToUart16550 => uart_mux.select(UartSel::UartliteToUart16550),
|
||||||
|
}
|
||||||
|
|
||||||
|
// UART0 routed through EMIO to PL pins.
|
||||||
|
let uart_0 =
|
||||||
|
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||||
|
// Safety: Valid address of AXI UARTLITE.
|
||||||
|
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||||
|
// We need to call this before splitting the structure, because the interrupt signal is
|
||||||
|
// used for both TX and RX, so the API is only exposed for this structure.
|
||||||
|
uartlite.enable_interrupt();
|
||||||
|
|
||||||
|
let (clk_config, error) =
|
||||||
|
axi_uart16550::ClkConfig::new_autocalc_with_error(clocks.pl_clocks()[0], 115200).unwrap();
|
||||||
|
assert!(error < 0.02);
|
||||||
|
let _uart_16550 = unsafe {
|
||||||
|
AxiUart16550::new(
|
||||||
|
AXI_UAR16550_BASE_ADDR,
|
||||||
|
axi_uart16550::UartConfig::new_with_clk_config(clk_config),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||||
|
let emio_leds: [Output; 8] = [
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||||
|
];
|
||||||
|
|
||||||
|
let (uart_0_tx, mut uart_0_rx) = uart_0.split();
|
||||||
|
let (uartlite_tx, _uartlite_rx) = uartlite.split();
|
||||||
|
let (uart_16550_tx, mut uart_16550_rx) = _uart_16550.split();
|
||||||
|
let (uart_0_prod, mut uart_0_cons) = QUEUE_UART_0.take().split();
|
||||||
|
let (uartlite_prod, mut uartlite_cons) = QUEUE_UARTLITE.take().split();
|
||||||
|
let (uart16550_prod, mut uart16550_cons) = QUEUE_UART16550.take().split();
|
||||||
|
// Use our helper function to start RX handling.
|
||||||
|
uart_0_rx.start_interrupt_driven_reception();
|
||||||
|
// Use our helper function to start RX handling.
|
||||||
|
uart_16550_rx.start_interrupt_driven_reception();
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
UART_0_PROD.borrow(cs).borrow_mut().replace(uart_0_prod);
|
||||||
|
UARTLITE_PROD.borrow(cs).borrow_mut().replace(uartlite_prod);
|
||||||
|
UART16550_PROD
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.replace(uart16550_prod);
|
||||||
|
RX_UART_0.borrow(cs).borrow_mut().replace(uart_0_rx);
|
||||||
|
});
|
||||||
|
spawner.spawn(led_task(mio_led, emio_leds)).unwrap();
|
||||||
|
|
||||||
|
match UART_MODE {
|
||||||
|
UartMode::Uart0ToUartlite => {
|
||||||
|
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
|
||||||
|
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
|
||||||
|
}
|
||||||
|
UartMode::Uart0ToUart16550 => {
|
||||||
|
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
|
||||||
|
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
|
||||||
|
}
|
||||||
|
UartMode::UartliteToUart16550 => {
|
||||||
|
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
|
||||||
|
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut read_buf: [u8; RB_SIZE] = [0; RB_SIZE];
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||||
|
loop {
|
||||||
|
let read_bytes = uart_0_cons.len();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
read_buf[i] = unsafe { uart_0_cons.dequeue_unchecked() };
|
||||||
|
});
|
||||||
|
if read_bytes > 0 {
|
||||||
|
info!("Received {} bytes on UART0", read_bytes);
|
||||||
|
info!(
|
||||||
|
"Data: {:?}",
|
||||||
|
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let read_bytes = uartlite_cons.len();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
read_buf[i] = unsafe { uartlite_cons.dequeue_unchecked() };
|
||||||
|
});
|
||||||
|
if read_bytes > 0 {
|
||||||
|
info!("Received {} bytes on UARTLITE", read_bytes);
|
||||||
|
info!(
|
||||||
|
"Data: {:?}",
|
||||||
|
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let read_bytes = uart16550_cons.len();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
read_buf[i] = unsafe { uart16550_cons.dequeue_unchecked() };
|
||||||
|
});
|
||||||
|
if read_bytes > 0 {
|
||||||
|
info!("Received {} bytes on UART16550", read_bytes);
|
||||||
|
info!(
|
||||||
|
"Data: {:?}",
|
||||||
|
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_print_string(prefix: &str, base_str: &str) -> alloc::string::String {
|
||||||
|
format!("{} {}\n\r", prefix, base_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn led_task(mut mio_led: Output, mut emio_leds: [Output; 8]) {
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut led_idx = 0;
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
emio_leds[led_idx].toggle().unwrap();
|
||||||
|
led_idx += 1;
|
||||||
|
if led_idx >= emio_leds.len() {
|
||||||
|
led_idx = 0;
|
||||||
|
}
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uartlite_task(uartlite: axi_uartlite::Tx) {
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let str0 = build_print_string("UARTLITE:", "Hello World");
|
||||||
|
let str1 = build_print_string(
|
||||||
|
"UARTLITE:",
|
||||||
|
"Long Hello which should completely fill the FIFO",
|
||||||
|
);
|
||||||
|
let mut idx = 0;
|
||||||
|
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||||
|
let mut tx_async = axi_uartlite::tx_async::TxAsync::new(uartlite, 0).unwrap();
|
||||||
|
loop {
|
||||||
|
tx_async.write(print_strs[idx]).await;
|
||||||
|
idx += 1;
|
||||||
|
if idx == 2 {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx);
|
||||||
|
let str0 = build_print_string("UART0:", "Hello World");
|
||||||
|
let str1 = build_print_string(
|
||||||
|
"UART0:",
|
||||||
|
"Long Hello which should completely fill the 64 byte FIFO of the UART0 peripheral",
|
||||||
|
);
|
||||||
|
let mut idx = 0;
|
||||||
|
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||||
|
loop {
|
||||||
|
tx_async.write(print_strs[idx]).await;
|
||||||
|
idx += 1;
|
||||||
|
if idx == 2 {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uart_16550_task(uart_tx: axi_uart16550::Tx) {
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||||
|
let mut tx_async = axi_uart16550::TxAsync::new(uart_tx, 0, embassy_time::Delay).unwrap();
|
||||||
|
let str0 = build_print_string("UART16550:", "Hello World");
|
||||||
|
let str1 = build_print_string(
|
||||||
|
"UART16550:",
|
||||||
|
"Long Hello which should completely fill the FIFO",
|
||||||
|
);
|
||||||
|
let mut idx = 0;
|
||||||
|
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||||
|
loop {
|
||||||
|
tx_async.write(print_strs[idx]).await;
|
||||||
|
idx += 1;
|
||||||
|
if idx == 2 {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(spi_interrupt) => match spi_interrupt {
|
||||||
|
zynq7000_hal::gic::SpiInterrupt::Pl0 => {
|
||||||
|
on_interrupt_axi_uartlite();
|
||||||
|
}
|
||||||
|
zynq7000_hal::gic::SpiInterrupt::Pl1 => {
|
||||||
|
on_interrupt_axi_16550();
|
||||||
|
}
|
||||||
|
zynq7000_hal::gic::SpiInterrupt::Uart0 => {
|
||||||
|
on_interrupt_uart_0();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_axi_uartlite() {
|
||||||
|
let mut buf = [0; axi_uartlite::FIFO_DEPTH];
|
||||||
|
let mut rx = unsafe { axi_uartlite::Rx::steal(AXI_UARTLITE_BASE_ADDR as usize) };
|
||||||
|
// Handle RX first: Empty the FIFO content into local buffer.
|
||||||
|
let read_bytes = rx.on_interrupt_rx(&mut buf);
|
||||||
|
// Handle TX next: Handle pending asynchronous TX operations.
|
||||||
|
let mut tx = unsafe { axi_uartlite::Tx::steal(AXI_UARTLITE_BASE_ADDR as usize) };
|
||||||
|
axi_uartlite::on_interrupt_tx(&mut tx, 0);
|
||||||
|
// Send received RX data to main task.
|
||||||
|
if read_bytes > 0 {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut prod_ref_mut = UARTLITE_PROD.borrow(cs).borrow_mut();
|
||||||
|
let prod = prod_ref_mut.as_mut().unwrap();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt_axi_16550() {
|
||||||
|
let mut buf = [0; axi_uart16550::FIFO_DEPTH];
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
let mut rx = unsafe { axi_uart16550::Rx::steal(AXI_UAR16550_BASE_ADDR as usize) };
|
||||||
|
let iir = rx.read_iir();
|
||||||
|
if let Ok(int_id) = iir.int_id() {
|
||||||
|
match int_id {
|
||||||
|
axi_uart16550::registers::IntId2::ReceiverLineStatus => {
|
||||||
|
let errors = rx.on_interrupt_receiver_line_status(iir);
|
||||||
|
warn!("Receiver line status error: {:?}", errors);
|
||||||
|
}
|
||||||
|
axi_uart16550::registers::IntId2::RxDataAvailable
|
||||||
|
| axi_uart16550::registers::IntId2::CharTimeout => {
|
||||||
|
read_bytes = rx.on_interrupt_data_available_or_char_timeout(int_id, &mut buf);
|
||||||
|
}
|
||||||
|
axi_uart16550::registers::IntId2::ThrEmpty => {
|
||||||
|
let mut tx = unsafe { axi_uart16550::Tx::steal(AXI_UAR16550_BASE_ADDR as usize) };
|
||||||
|
axi_uart16550::tx_async::on_interrupt_tx(&mut tx, 0);
|
||||||
|
}
|
||||||
|
axi_uart16550::registers::IntId2::ModemStatus => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Send received RX data to main task.
|
||||||
|
if read_bytes > 0 {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut prod_ref_mut = UART16550_PROD.borrow(cs).borrow_mut();
|
||||||
|
let prod = prod_ref_mut.as_mut().unwrap();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_uart_0() {
|
||||||
|
let mut buf = [0; zynq7000_hal::uart::FIFO_DEPTH];
|
||||||
|
let mut read_bytes = 0;
|
||||||
|
// Handle RX first: Empty the FIFO content into local buffer.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut uart0 = RX_UART_0.borrow(cs).borrow_mut();
|
||||||
|
read_bytes = uart0
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.on_interrupt(&mut buf, true)
|
||||||
|
.read_bytes();
|
||||||
|
});
|
||||||
|
// Handle TX next: Handle pending asynchronous TX operations.
|
||||||
|
zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0);
|
||||||
|
// Send received RX data to main task.
|
||||||
|
if read_bytes > 0 {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut prod_ref_mut = UART_0_PROD.borrow(cs).borrow_mut();
|
||||||
|
let prod = prod_ref_mut.as_mut().unwrap();
|
||||||
|
(0..read_bytes).for_each(|i| {
|
||||||
|
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
5
examples/zedboard/src/lib.rs
Normal file
5
examples/zedboard/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#![no_std]
|
||||||
|
use zynq7000_hal::time::Hertz;
|
||||||
|
|
||||||
|
// Define the clock frequency as a constant
|
||||||
|
pub const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
153
examples/zedboard/src/main.rs
Normal file
153
examples/zedboard/src/main.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use cortex_ar::asm::nop;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Ticker};
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use log::{error, info};
|
||||||
|
use zedboard::PS_CLOCK_FREQUENCY;
|
||||||
|
use zynq7000_hal::{
|
||||||
|
BootMode,
|
||||||
|
clocks::Clocks,
|
||||||
|
configure_level_shifter,
|
||||||
|
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||||
|
gpio::{GpioPins, Output, PinState},
|
||||||
|
gtc::Gtc,
|
||||||
|
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||||
|
use zynq7000_rt as _;
|
||||||
|
|
||||||
|
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
|
||||||
|
|
||||||
|
/// Entry point (not called like a normal main function)
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||||
|
if cpu_id != 0 {
|
||||||
|
panic!("unexpected CPU ID {}", cpu_id);
|
||||||
|
}
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
#[unsafe(export_name = "main")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// Enable PS-PL level shifters.
|
||||||
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||||
|
let dp = PsPeripherals::take().unwrap();
|
||||||
|
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||||
|
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||||
|
// Set up the global interrupt controller.
|
||||||
|
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||||
|
gic.enable_all_interrupts();
|
||||||
|
gic.set_all_spi_interrupt_targets_cpu0();
|
||||||
|
gic.enable();
|
||||||
|
unsafe {
|
||||||
|
gic.enable_interrupts();
|
||||||
|
}
|
||||||
|
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||||
|
|
||||||
|
// Set up global timer counter and embassy time driver.
|
||||||
|
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||||
|
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||||
|
|
||||||
|
// Set up the UART, we are logging with it.
|
||||||
|
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
let mut uart = Uart::new_with_mio(
|
||||||
|
dp.uart_1,
|
||||||
|
UartConfig::new_with_clk_config(uart_clk_config),
|
||||||
|
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||||
|
// Safety: We are not multi-threaded yet.
|
||||||
|
unsafe {
|
||||||
|
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||||
|
uart,
|
||||||
|
log::LevelFilter::Trace,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let boot_mode = BootMode::new();
|
||||||
|
info!("Boot mode: {:?}", boot_mode);
|
||||||
|
|
||||||
|
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||||
|
|
||||||
|
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
|
||||||
|
let mut emio_leds: [Output; 8] = [
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
|
||||||
|
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
|
||||||
|
];
|
||||||
|
loop {
|
||||||
|
mio_led.toggle().unwrap();
|
||||||
|
|
||||||
|
// Create a wave pattern for emio_leds
|
||||||
|
for led in emio_leds.iter_mut() {
|
||||||
|
led.toggle().unwrap();
|
||||||
|
ticker.next().await; // Wait for the next ticker for each toggle
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker.next().await; // Wait for the next cycle of the ticker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _irq_handler() {
|
||||||
|
let mut gic_helper = GicInterruptHelper::new();
|
||||||
|
let irq_info = gic_helper.acknowledge_interrupt();
|
||||||
|
match irq_info.interrupt() {
|
||||||
|
Interrupt::Sgi(_) => (),
|
||||||
|
Interrupt::Ppi(ppi_interrupt) => {
|
||||||
|
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||||
|
unsafe {
|
||||||
|
zynq7000_embassy::on_interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Interrupt::Spi(_spi_interrupt) => (),
|
||||||
|
Interrupt::Invalid(_) => (),
|
||||||
|
Interrupt::Spurious => (),
|
||||||
|
}
|
||||||
|
gic_helper.end_of_interrupt(irq_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _abort_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _undefined_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn _prefetch_handler() {
|
||||||
|
loop {
|
||||||
|
nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Panic handler
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
error!("Panic: {:?}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
1
experiments/.cargo/config.toml
Normal file
1
experiments/.cargo/config.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
[build]
|
1
experiments/.gitignore
vendored
Normal file
1
experiments/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
171
experiments/Cargo.lock
generated
Normal file
171
experiments/Cargo.lock
generated
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary-int"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitbybit"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d317eeca82e7d88d606419a430590d83552bdceb899cb29904f63d694344b7fc"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary-int",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-mmio"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"derive-mmio-macro",
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-mmio-macro"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "experiments"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"derive-mmio",
|
||||||
|
"static_assertions",
|
||||||
|
"zynq7000",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zynq7000"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary-int",
|
||||||
|
"bitbybit",
|
||||||
|
"derive-mmio",
|
||||||
|
"once_cell",
|
||||||
|
"static_assertions",
|
||||||
|
"thiserror",
|
||||||
|
]
|
9
experiments/Cargo.toml
Normal file
9
experiments/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "experiments"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
static_assertions = "1.1"
|
||||||
|
derive-mmio = { path = "../../derive-mmio", default-features = false }
|
||||||
|
zynq7000 = { path = "../zynq7000", default-features = false }
|
11
experiments/src/main.rs
Normal file
11
experiments/src/main.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use zynq7000::slcr::{ClockControl, Slcr};
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let size = core::mem::size_of::<ClockControl>();
|
||||||
|
println!("Size of ClockControl: {}", size);
|
||||||
|
let size = core::mem::size_of::<Slcr>();
|
||||||
|
println!("Size of SLCR: {}", size);
|
||||||
|
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
8
gdb.gdb
Normal file
8
gdb.gdb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target remote localhost:3000
|
||||||
|
|
||||||
|
# *try* to stop at the user entry point (it might be gone due to inlining)
|
||||||
|
break main
|
||||||
|
|
||||||
|
load
|
||||||
|
|
||||||
|
continue
|
8
memory.x
Normal file
8
memory.x
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* Zedboard: 512 MB DDR3. Only use 256 MB for now, should be plenty for a bare-metal app. */
|
||||||
|
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 256M
|
||||||
|
}
|
||||||
|
|
||||||
|
REGION_ALIAS("DATA", CODE);
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
# channel = "stable"
|
||||||
|
channel = "nightly"
|
1
scripts/.gitignore
vendored
Normal file
1
scripts/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/xsct-output.log
|
847
scripts/ps7_init.tcl
Normal file
847
scripts/ps7_init.tcl
Normal file
@ -0,0 +1,847 @@
|
|||||||
|
proc ps7_pll_init_data_3_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000110 0x003FFFF0 0x000FA220
|
||||||
|
mask_write 0XF8000100 0x0007F000 0x00027000
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000120 0x1F003F30 0x1F000200
|
||||||
|
mask_write 0XF8000114 0x003FFFF0 0x0012C220
|
||||||
|
mask_write 0XF8000104 0x0007F000 0x00020000
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000002
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000124 0xFFF00003 0x0C200003
|
||||||
|
mask_write 0XF8000118 0x003FFFF0 0x001452C0
|
||||||
|
mask_write 0XF8000108 0x0007F000 0x0001E000
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000004
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_clock_init_data_3_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000128 0x03F03F01 0x00700F01
|
||||||
|
mask_write 0XF8000138 0x00000011 0x00000001
|
||||||
|
mask_write 0XF8000140 0x03F03F71 0x00100801
|
||||||
|
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||||
|
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||||
|
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||||
|
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||||
|
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_ddr_init_data_3_0 {} {
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000080
|
||||||
|
mask_write 0XF8006004 0x0007FFFF 0x00001082
|
||||||
|
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
|
||||||
|
mask_write 0XF800600C 0x03FFFFFF 0x02001001
|
||||||
|
mask_write 0XF8006010 0x03FFFFFF 0x00014001
|
||||||
|
mask_write 0XF8006014 0x001FFFFF 0x0004159B
|
||||||
|
mask_write 0XF8006018 0xF7FFFFFF 0x44E458D3
|
||||||
|
mask_write 0XF800601C 0xFFFFFFFF 0x7282BCE5
|
||||||
|
mask_write 0XF8006020 0x7FDFFFFC 0x270872D0
|
||||||
|
mask_write 0XF8006024 0x0FFFFFC3 0x00000000
|
||||||
|
mask_write 0XF8006028 0x00003FFF 0x00002007
|
||||||
|
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
|
||||||
|
mask_write 0XF8006030 0xFFFFFFFF 0x00040B30
|
||||||
|
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
|
||||||
|
mask_write 0XF8006038 0x00000003 0x00000000
|
||||||
|
mask_write 0XF800603C 0x000FFFFF 0x00000777
|
||||||
|
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
|
||||||
|
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
|
||||||
|
mask_write 0XF8006048 0x0003F03F 0x0003C008
|
||||||
|
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
|
||||||
|
mask_write 0XF8006058 0x00010000 0x00000000
|
||||||
|
mask_write 0XF800605C 0x0000FFFF 0x00005003
|
||||||
|
mask_write 0XF8006060 0x000017FF 0x0000003E
|
||||||
|
mask_write 0XF8006064 0x00021FE0 0x00020000
|
||||||
|
mask_write 0XF8006068 0x03FFFFFF 0x00284141
|
||||||
|
mask_write 0XF800606C 0x0000FFFF 0x00001610
|
||||||
|
mask_write 0XF8006078 0x03FFFFFF 0x00466111
|
||||||
|
mask_write 0XF800607C 0x000FFFFF 0x00032222
|
||||||
|
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
|
||||||
|
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
|
||||||
|
mask_write 0XF80060AC 0x000001FF 0x000001FE
|
||||||
|
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
|
||||||
|
mask_write 0XF80060B4 0x00000200 0x00000200
|
||||||
|
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
|
||||||
|
mask_write 0XF80060C4 0x00000003 0x00000000
|
||||||
|
mask_write 0XF80060C8 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF80060DC 0x00000001 0x00000000
|
||||||
|
mask_write 0XF80060F0 0x0000FFFF 0x00000000
|
||||||
|
mask_write 0XF80060F4 0x0000000F 0x00000008
|
||||||
|
mask_write 0XF8006114 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF8006118 0x7FFFFFCF 0x40000001
|
||||||
|
mask_write 0XF800611C 0x7FFFFFCF 0x40000001
|
||||||
|
mask_write 0XF8006120 0x7FFFFFCF 0x40000001
|
||||||
|
mask_write 0XF8006124 0x7FFFFFCF 0x40000001
|
||||||
|
mask_write 0XF800612C 0x000FFFFF 0x00024000
|
||||||
|
mask_write 0XF8006130 0x000FFFFF 0x00022C00
|
||||||
|
mask_write 0XF8006134 0x000FFFFF 0x00023000
|
||||||
|
mask_write 0XF8006138 0x000FFFFF 0x00024C00
|
||||||
|
mask_write 0XF8006140 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006144 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006148 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF800614C 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006154 0x000FFFFF 0x00000077
|
||||||
|
mask_write 0XF8006158 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF800615C 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF8006160 0x000FFFFF 0x00000075
|
||||||
|
mask_write 0XF8006168 0x001FFFFF 0x000000E5
|
||||||
|
mask_write 0XF800616C 0x001FFFFF 0x000000E0
|
||||||
|
mask_write 0XF8006170 0x001FFFFF 0x000000E1
|
||||||
|
mask_write 0XF8006174 0x001FFFFF 0x000000E8
|
||||||
|
mask_write 0XF800617C 0x000FFFFF 0x000000B7
|
||||||
|
mask_write 0XF8006180 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006184 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006188 0x000FFFFF 0x000000B5
|
||||||
|
mask_write 0XF8006190 0x6FFFFEFE 0x00040080
|
||||||
|
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
|
||||||
|
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF8006208 0x000703FF 0x000003FF
|
||||||
|
mask_write 0XF800620C 0x000703FF 0x000003FF
|
||||||
|
mask_write 0XF8006210 0x000703FF 0x000003FF
|
||||||
|
mask_write 0XF8006214 0x000703FF 0x000003FF
|
||||||
|
mask_write 0XF8006218 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF800621C 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006220 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006224 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF80062A8 0x00000FF5 0x00000000
|
||||||
|
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF80062B0 0x003FFFFF 0x00005125
|
||||||
|
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
|
||||||
|
mask_poll 0XF8000B74 0x00002000
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000081
|
||||||
|
mask_poll 0XF8006054 0x00000007
|
||||||
|
}
|
||||||
|
proc ps7_mio_init_data_3_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B00 0x00000071 0x00000001
|
||||||
|
mask_write 0XF8000B40 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B44 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B48 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B4C 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B50 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B54 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B58 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
|
||||||
|
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B6C 0x00007FFF 0x00000260
|
||||||
|
mask_write 0XF8000B70 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000B70 0x00000021 0x00000020
|
||||||
|
mask_write 0XF8000B70 0x07FEFFFF 0x00000823
|
||||||
|
mask_write 0XF8000700 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000704 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000708 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800070C 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000710 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||||
|
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000730 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000734 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000738 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800073C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000740 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000744 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000748 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF800074C 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000750 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000754 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000758 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800075C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000760 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000764 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000768 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800076C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000770 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000774 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000778 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800077C 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000780 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000784 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000788 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800078C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000790 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000794 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000798 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800079C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF80007A0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A8 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007AC 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||||
|
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||||
|
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||||
|
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||||
|
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_peripherals_init_data_3_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B48 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B4C 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B50 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B54 0x00000180 0x00000180
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
mask_write 0XE0001034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0001018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0001000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0001004 0x000003FF 0x00000020
|
||||||
|
mask_write 0XE0000034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0000018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0000000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0000004 0x000003FF 0x00000020
|
||||||
|
mask_write 0XE000D000 0x00080000 0x00080000
|
||||||
|
mask_write 0XF8007000 0x20000000 0x00000000
|
||||||
|
mask_write 0XE000A244 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
mask_write 0XE000A248 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF0000
|
||||||
|
mask_delay 0XF8F00200 1
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
}
|
||||||
|
proc ps7_post_config_3_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000900 0x0000000F 0x0000000F
|
||||||
|
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_debug_3_0 {} {
|
||||||
|
mwr -force 0XF8898FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8899FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8809FB0 0xC5ACCE55
|
||||||
|
}
|
||||||
|
proc ps7_pll_init_data_2_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000110 0x003FFFF0 0x000FA220
|
||||||
|
mask_write 0XF8000100 0x0007F000 0x00027000
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000120 0x1F003F30 0x1F000200
|
||||||
|
mask_write 0XF8000114 0x003FFFF0 0x0012C220
|
||||||
|
mask_write 0XF8000104 0x0007F000 0x00020000
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000002
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000124 0xFFF00003 0x0C200003
|
||||||
|
mask_write 0XF8000118 0x003FFFF0 0x001452C0
|
||||||
|
mask_write 0XF8000108 0x0007F000 0x0001E000
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000004
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_clock_init_data_2_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000128 0x03F03F01 0x00700F01
|
||||||
|
mask_write 0XF8000138 0x00000011 0x00000001
|
||||||
|
mask_write 0XF8000140 0x03F03F71 0x00100801
|
||||||
|
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||||
|
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||||
|
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||||
|
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||||
|
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_ddr_init_data_2_0 {} {
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000080
|
||||||
|
mask_write 0XF8006004 0x1FFFFFFF 0x00081082
|
||||||
|
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
|
||||||
|
mask_write 0XF800600C 0x03FFFFFF 0x02001001
|
||||||
|
mask_write 0XF8006010 0x03FFFFFF 0x00014001
|
||||||
|
mask_write 0XF8006014 0x001FFFFF 0x0004159B
|
||||||
|
mask_write 0XF8006018 0xF7FFFFFF 0x44E458D3
|
||||||
|
mask_write 0XF800601C 0xFFFFFFFF 0x7282BCE5
|
||||||
|
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
|
||||||
|
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
|
||||||
|
mask_write 0XF8006028 0x00003FFF 0x00002007
|
||||||
|
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
|
||||||
|
mask_write 0XF8006030 0xFFFFFFFF 0x00040B30
|
||||||
|
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
|
||||||
|
mask_write 0XF8006038 0x00001FC3 0x00000000
|
||||||
|
mask_write 0XF800603C 0x000FFFFF 0x00000777
|
||||||
|
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
|
||||||
|
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
|
||||||
|
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
|
||||||
|
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
|
||||||
|
mask_write 0XF8006058 0x0001FFFF 0x00000101
|
||||||
|
mask_write 0XF800605C 0x0000FFFF 0x00005003
|
||||||
|
mask_write 0XF8006060 0x000017FF 0x0000003E
|
||||||
|
mask_write 0XF8006064 0x00021FE0 0x00020000
|
||||||
|
mask_write 0XF8006068 0x03FFFFFF 0x00284141
|
||||||
|
mask_write 0XF800606C 0x0000FFFF 0x00001610
|
||||||
|
mask_write 0XF8006078 0x03FFFFFF 0x00466111
|
||||||
|
mask_write 0XF800607C 0x000FFFFF 0x00032222
|
||||||
|
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
|
||||||
|
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
|
||||||
|
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
|
||||||
|
mask_write 0XF80060AC 0x000001FF 0x000001FE
|
||||||
|
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
|
||||||
|
mask_write 0XF80060B4 0x000007FF 0x00000200
|
||||||
|
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
|
||||||
|
mask_write 0XF80060C4 0x00000003 0x00000000
|
||||||
|
mask_write 0XF80060C8 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF80060DC 0x00000001 0x00000000
|
||||||
|
mask_write 0XF80060F0 0x0000FFFF 0x00000000
|
||||||
|
mask_write 0XF80060F4 0x0000000F 0x00000008
|
||||||
|
mask_write 0XF8006114 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF800612C 0x000FFFFF 0x00024000
|
||||||
|
mask_write 0XF8006130 0x000FFFFF 0x00022C00
|
||||||
|
mask_write 0XF8006134 0x000FFFFF 0x00023000
|
||||||
|
mask_write 0XF8006138 0x000FFFFF 0x00024C00
|
||||||
|
mask_write 0XF8006140 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006144 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006148 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF800614C 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006154 0x000FFFFF 0x00000077
|
||||||
|
mask_write 0XF8006158 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF800615C 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF8006160 0x000FFFFF 0x00000075
|
||||||
|
mask_write 0XF8006168 0x001FFFFF 0x000000E5
|
||||||
|
mask_write 0XF800616C 0x001FFFFF 0x000000E0
|
||||||
|
mask_write 0XF8006170 0x001FFFFF 0x000000E1
|
||||||
|
mask_write 0XF8006174 0x001FFFFF 0x000000E8
|
||||||
|
mask_write 0XF800617C 0x000FFFFF 0x000000B7
|
||||||
|
mask_write 0XF8006180 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006184 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006188 0x000FFFFF 0x000000B5
|
||||||
|
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
|
||||||
|
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
|
||||||
|
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF8006208 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF800620C 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006210 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006214 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006218 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF800621C 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006220 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006224 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF80062A8 0x00000FF7 0x00000000
|
||||||
|
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF80062B0 0x003FFFFF 0x00005125
|
||||||
|
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
|
||||||
|
mask_poll 0XF8000B74 0x00002000
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000081
|
||||||
|
mask_poll 0XF8006054 0x00000007
|
||||||
|
}
|
||||||
|
proc ps7_mio_init_data_2_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B00 0x00000303 0x00000001
|
||||||
|
mask_write 0XF8000B40 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B44 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B48 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B4C 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B50 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B54 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B58 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
|
||||||
|
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B6C 0x00007FFF 0x00000260
|
||||||
|
mask_write 0XF8000B70 0x00000021 0x00000021
|
||||||
|
mask_write 0XF8000B70 0x00000021 0x00000020
|
||||||
|
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
|
||||||
|
mask_write 0XF8000700 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000704 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000708 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800070C 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000710 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||||
|
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000730 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000734 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000738 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800073C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000740 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000744 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000748 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF800074C 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000750 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000754 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000758 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800075C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000760 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000764 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000768 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800076C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000770 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000774 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000778 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800077C 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000780 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000784 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000788 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800078C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000790 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000794 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000798 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800079C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF80007A0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A8 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007AC 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||||
|
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||||
|
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||||
|
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||||
|
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_peripherals_init_data_2_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B48 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B4C 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B50 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B54 0x00000180 0x00000180
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
mask_write 0XE0001034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0001018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0001000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0001004 0x00000FFF 0x00000020
|
||||||
|
mask_write 0XE0000034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0000018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0000000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0000004 0x00000FFF 0x00000020
|
||||||
|
mask_write 0XE000D000 0x00080000 0x00080000
|
||||||
|
mask_write 0XF8007000 0x20000000 0x00000000
|
||||||
|
mask_write 0XE000A244 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
mask_write 0XE000A248 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF0000
|
||||||
|
mask_delay 0XF8F00200 1
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
}
|
||||||
|
proc ps7_post_config_2_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000900 0x0000000F 0x0000000F
|
||||||
|
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_debug_2_0 {} {
|
||||||
|
mwr -force 0XF8898FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8899FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8809FB0 0xC5ACCE55
|
||||||
|
}
|
||||||
|
proc ps7_pll_init_data_1_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000110 0x003FFFF0 0x000FA220
|
||||||
|
mask_write 0XF8000100 0x0007F000 0x00027000
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000001
|
||||||
|
mask_write 0XF8000100 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000120 0x1F003F30 0x1F000200
|
||||||
|
mask_write 0XF8000114 0x003FFFF0 0x0012C220
|
||||||
|
mask_write 0XF8000104 0x0007F000 0x00020000
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000104 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000002
|
||||||
|
mask_write 0XF8000104 0x00000010 0x00000000
|
||||||
|
mask_write 0XF8000124 0xFFF00003 0x0C200003
|
||||||
|
mask_write 0XF8000118 0x003FFFF0 0x001452C0
|
||||||
|
mask_write 0XF8000108 0x0007F000 0x0001E000
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000010
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000001
|
||||||
|
mask_write 0XF8000108 0x00000001 0x00000000
|
||||||
|
mask_poll 0XF800010C 0x00000004
|
||||||
|
mask_write 0XF8000108 0x00000010 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_clock_init_data_1_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000128 0x03F03F01 0x00700F01
|
||||||
|
mask_write 0XF8000138 0x00000011 0x00000001
|
||||||
|
mask_write 0XF8000140 0x03F03F71 0x00100801
|
||||||
|
mask_write 0XF800014C 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000150 0x00003F33 0x00001401
|
||||||
|
mask_write 0XF8000154 0x00003F33 0x00000A03
|
||||||
|
mask_write 0XF8000168 0x00003F31 0x00000501
|
||||||
|
mask_write 0XF8000170 0x03F03F30 0x00200500
|
||||||
|
mask_write 0XF80001C4 0x00000001 0x00000001
|
||||||
|
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_ddr_init_data_1_0 {} {
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000080
|
||||||
|
mask_write 0XF8006004 0x1FFFFFFF 0x00081082
|
||||||
|
mask_write 0XF8006008 0x03FFFFFF 0x03C0780F
|
||||||
|
mask_write 0XF800600C 0x03FFFFFF 0x02001001
|
||||||
|
mask_write 0XF8006010 0x03FFFFFF 0x00014001
|
||||||
|
mask_write 0XF8006014 0x001FFFFF 0x0004159B
|
||||||
|
mask_write 0XF8006018 0xF7FFFFFF 0x44E458D3
|
||||||
|
mask_write 0XF800601C 0xFFFFFFFF 0x7282BCE5
|
||||||
|
mask_write 0XF8006020 0xFFFFFFFC 0x272872D0
|
||||||
|
mask_write 0XF8006024 0x0FFFFFFF 0x0000003C
|
||||||
|
mask_write 0XF8006028 0x00003FFF 0x00002007
|
||||||
|
mask_write 0XF800602C 0xFFFFFFFF 0x00000008
|
||||||
|
mask_write 0XF8006030 0xFFFFFFFF 0x00040B30
|
||||||
|
mask_write 0XF8006034 0x13FF3FFF 0x000116D4
|
||||||
|
mask_write 0XF8006038 0x00001FC3 0x00000000
|
||||||
|
mask_write 0XF800603C 0x000FFFFF 0x00000777
|
||||||
|
mask_write 0XF8006040 0xFFFFFFFF 0xFFF00000
|
||||||
|
mask_write 0XF8006044 0x0FFFFFFF 0x0FF66666
|
||||||
|
mask_write 0XF8006048 0x3FFFFFFF 0x0003C248
|
||||||
|
mask_write 0XF8006050 0xFF0F8FFF 0x77010800
|
||||||
|
mask_write 0XF8006058 0x0001FFFF 0x00000101
|
||||||
|
mask_write 0XF800605C 0x0000FFFF 0x00005003
|
||||||
|
mask_write 0XF8006060 0x000017FF 0x0000003E
|
||||||
|
mask_write 0XF8006064 0x00021FE0 0x00020000
|
||||||
|
mask_write 0XF8006068 0x03FFFFFF 0x00284141
|
||||||
|
mask_write 0XF800606C 0x0000FFFF 0x00001610
|
||||||
|
mask_write 0XF80060A0 0x00FFFFFF 0x00008000
|
||||||
|
mask_write 0XF80060A4 0xFFFFFFFF 0x10200802
|
||||||
|
mask_write 0XF80060A8 0x0FFFFFFF 0x0690CB73
|
||||||
|
mask_write 0XF80060AC 0x000001FF 0x000001FE
|
||||||
|
mask_write 0XF80060B0 0x1FFFFFFF 0x1CFFFFFF
|
||||||
|
mask_write 0XF80060B4 0x000007FF 0x00000200
|
||||||
|
mask_write 0XF80060B8 0x01FFFFFF 0x00200066
|
||||||
|
mask_write 0XF80060C4 0x00000003 0x00000000
|
||||||
|
mask_write 0XF80060C8 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF80060DC 0x00000001 0x00000000
|
||||||
|
mask_write 0XF80060F0 0x0000FFFF 0x00000000
|
||||||
|
mask_write 0XF80060F4 0x0000000F 0x00000008
|
||||||
|
mask_write 0XF8006114 0x000000FF 0x00000000
|
||||||
|
mask_write 0XF8006118 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF800611C 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF8006120 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF8006124 0x7FFFFFFF 0x40000001
|
||||||
|
mask_write 0XF800612C 0x000FFFFF 0x00024000
|
||||||
|
mask_write 0XF8006130 0x000FFFFF 0x00022C00
|
||||||
|
mask_write 0XF8006134 0x000FFFFF 0x00023000
|
||||||
|
mask_write 0XF8006138 0x000FFFFF 0x00024C00
|
||||||
|
mask_write 0XF8006140 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006144 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006148 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF800614C 0x000FFFFF 0x00000035
|
||||||
|
mask_write 0XF8006154 0x000FFFFF 0x00000077
|
||||||
|
mask_write 0XF8006158 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF800615C 0x000FFFFF 0x0000007C
|
||||||
|
mask_write 0XF8006160 0x000FFFFF 0x00000075
|
||||||
|
mask_write 0XF8006168 0x001FFFFF 0x000000E5
|
||||||
|
mask_write 0XF800616C 0x001FFFFF 0x000000E0
|
||||||
|
mask_write 0XF8006170 0x001FFFFF 0x000000E1
|
||||||
|
mask_write 0XF8006174 0x001FFFFF 0x000000E8
|
||||||
|
mask_write 0XF800617C 0x000FFFFF 0x000000B7
|
||||||
|
mask_write 0XF8006180 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006184 0x000FFFFF 0x000000BC
|
||||||
|
mask_write 0XF8006188 0x000FFFFF 0x000000B5
|
||||||
|
mask_write 0XF8006190 0xFFFFFFFF 0x10040080
|
||||||
|
mask_write 0XF8006194 0x000FFFFF 0x0001FC82
|
||||||
|
mask_write 0XF8006204 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF8006208 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF800620C 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006210 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006214 0x000F03FF 0x000803FF
|
||||||
|
mask_write 0XF8006218 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF800621C 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006220 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF8006224 0x000F03FF 0x000003FF
|
||||||
|
mask_write 0XF80062A8 0x00000FF7 0x00000000
|
||||||
|
mask_write 0XF80062AC 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0XF80062B0 0x003FFFFF 0x00005125
|
||||||
|
mask_write 0XF80062B4 0x0003FFFF 0x000012A8
|
||||||
|
mask_poll 0XF8000B74 0x00002000
|
||||||
|
mask_write 0XF8006000 0x0001FFFF 0x00000081
|
||||||
|
mask_poll 0XF8006054 0x00000007
|
||||||
|
}
|
||||||
|
proc ps7_mio_init_data_1_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B00 0x00000303 0x00000001
|
||||||
|
mask_write 0XF8000B40 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B44 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B48 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B4C 0x00000FFF 0x00000672
|
||||||
|
mask_write 0XF8000B50 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B54 0x00000FFF 0x00000674
|
||||||
|
mask_write 0XF8000B58 0x00000FFF 0x00000600
|
||||||
|
mask_write 0XF8000B5C 0xFFFFFFFF 0x0018C61C
|
||||||
|
mask_write 0XF8000B60 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B64 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B68 0xFFFFFFFF 0x00F9861C
|
||||||
|
mask_write 0XF8000B6C 0x000073FF 0x00000260
|
||||||
|
mask_write 0XF8000B70 0x00000021 0x00000021
|
||||||
|
mask_write 0XF8000B70 0x00000021 0x00000020
|
||||||
|
mask_write 0XF8000B70 0x07FFFFFF 0x00000823
|
||||||
|
mask_write 0XF8000700 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000704 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000708 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800070C 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000710 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000714 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000718 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF800071C 0x00003FFF 0x00000600
|
||||||
|
mask_write 0XF8000720 0x00003FFF 0x00000702
|
||||||
|
mask_write 0XF8000724 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000728 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800072C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000730 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000734 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000738 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF800073C 0x00003FFF 0x00001600
|
||||||
|
mask_write 0XF8000740 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000744 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000748 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF800074C 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000750 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000754 0x00003FFF 0x00002902
|
||||||
|
mask_write 0XF8000758 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800075C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000760 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000764 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000768 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF800076C 0x00003FFF 0x00000903
|
||||||
|
mask_write 0XF8000770 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000774 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000778 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800077C 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000780 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000784 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000788 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800078C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000790 0x00003FFF 0x00000305
|
||||||
|
mask_write 0XF8000794 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF8000798 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF800079C 0x00003FFF 0x00000304
|
||||||
|
mask_write 0XF80007A0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007A8 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007AC 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B0 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B4 0x00003FFF 0x00000380
|
||||||
|
mask_write 0XF80007B8 0x00003FFF 0x00001200
|
||||||
|
mask_write 0XF80007BC 0x00003F01 0x00000201
|
||||||
|
mask_write 0XF80007C0 0x00003FFF 0x000002E0
|
||||||
|
mask_write 0XF80007C4 0x00003FFF 0x000002E1
|
||||||
|
mask_write 0XF80007C8 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007CC 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D0 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF80007D4 0x00003FFF 0x00000200
|
||||||
|
mask_write 0XF8000830 0x003F003F 0x002F0037
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_peripherals_init_data_1_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000B48 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B4C 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B50 0x00000180 0x00000180
|
||||||
|
mask_write 0XF8000B54 0x00000180 0x00000180
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
mask_write 0XE0001034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0001018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0001000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0001004 0x00000FFF 0x00000020
|
||||||
|
mask_write 0XE0000034 0x000000FF 0x00000006
|
||||||
|
mask_write 0XE0000018 0x0000FFFF 0x0000007C
|
||||||
|
mask_write 0XE0000000 0x000001FF 0x00000017
|
||||||
|
mask_write 0XE0000004 0x00000FFF 0x00000020
|
||||||
|
mask_write 0XE000D000 0x00080000 0x00080000
|
||||||
|
mask_write 0XF8007000 0x20000000 0x00000000
|
||||||
|
mask_write 0XE000A244 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
mask_write 0XE000A248 0x003FFFFF 0x00004000
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF0000
|
||||||
|
mask_delay 0XF8F00200 1
|
||||||
|
mask_write 0XE000A008 0xFFFFFFFF 0xBFFF4000
|
||||||
|
}
|
||||||
|
proc ps7_post_config_1_0 {} {
|
||||||
|
mwr -force 0XF8000008 0x0000DF0D
|
||||||
|
mask_write 0XF8000900 0x0000000F 0x0000000F
|
||||||
|
mask_write 0XF8000240 0xFFFFFFFF 0x00000000
|
||||||
|
mwr -force 0XF8000004 0x0000767B
|
||||||
|
}
|
||||||
|
proc ps7_debug_1_0 {} {
|
||||||
|
mwr -force 0XF8898FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8899FB0 0xC5ACCE55
|
||||||
|
mwr -force 0XF8809FB0 0xC5ACCE55
|
||||||
|
}
|
||||||
|
set PCW_SILICON_VER_1_0 "0x0"
|
||||||
|
set PCW_SILICON_VER_2_0 "0x1"
|
||||||
|
set PCW_SILICON_VER_3_0 "0x2"
|
||||||
|
set APU_FREQ 650000000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc mask_poll { addr mask } {
|
||||||
|
set count 1
|
||||||
|
set curval "0x[string range [mrd $addr] end-8 end]"
|
||||||
|
set maskedval [expr {$curval & $mask}]
|
||||||
|
while { $maskedval == 0 } {
|
||||||
|
set curval "0x[string range [mrd $addr] end-8 end]"
|
||||||
|
set maskedval [expr {$curval & $mask}]
|
||||||
|
set count [ expr { $count + 1 } ]
|
||||||
|
if { $count == 100000000 } {
|
||||||
|
puts "Timeout Reached. Mask poll failed at ADDRESS: $addr MASK: $mask"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc mask_delay { addr val } {
|
||||||
|
set delay [ get_number_of_cycles_for_delay $val ]
|
||||||
|
perf_reset_and_start_timer
|
||||||
|
set curval "0x[string range [mrd $addr] end-8 end]"
|
||||||
|
set maskedval [expr {$curval < $delay}]
|
||||||
|
while { $maskedval == 1 } {
|
||||||
|
set curval "0x[string range [mrd $addr] end-8 end]"
|
||||||
|
set maskedval [expr {$curval < $delay}]
|
||||||
|
}
|
||||||
|
perf_reset_clock
|
||||||
|
}
|
||||||
|
|
||||||
|
proc ps_version { } {
|
||||||
|
set si_ver "0x[string range [mrd 0xF8007080] end-8 end]"
|
||||||
|
set mask_sil_ver "0x[expr {$si_ver >> 28}]"
|
||||||
|
return $mask_sil_ver;
|
||||||
|
}
|
||||||
|
|
||||||
|
proc ps7_post_config {} {
|
||||||
|
set saved_mode [configparams force-mem-accesses]
|
||||||
|
configparams force-mem-accesses 1
|
||||||
|
|
||||||
|
variable PCW_SILICON_VER_1_0
|
||||||
|
variable PCW_SILICON_VER_2_0
|
||||||
|
variable PCW_SILICON_VER_3_0
|
||||||
|
set sil_ver [ps_version]
|
||||||
|
|
||||||
|
if { $sil_ver == $PCW_SILICON_VER_1_0} {
|
||||||
|
ps7_post_config_1_0
|
||||||
|
} elseif { $sil_ver == $PCW_SILICON_VER_2_0 } {
|
||||||
|
ps7_post_config_2_0
|
||||||
|
} else {
|
||||||
|
ps7_post_config_3_0
|
||||||
|
}
|
||||||
|
configparams force-mem-accesses $saved_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
proc ps7_debug {} {
|
||||||
|
variable PCW_SILICON_VER_1_0
|
||||||
|
variable PCW_SILICON_VER_2_0
|
||||||
|
variable PCW_SILICON_VER_3_0
|
||||||
|
set sil_ver [ps_version]
|
||||||
|
|
||||||
|
if { $sil_ver == $PCW_SILICON_VER_1_0} {
|
||||||
|
ps7_debug_1_0
|
||||||
|
} elseif { $sil_ver == $PCW_SILICON_VER_2_0 } {
|
||||||
|
ps7_debug_2_0
|
||||||
|
} else {
|
||||||
|
ps7_debug_3_0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
proc ps7_init {} {
|
||||||
|
variable PCW_SILICON_VER_1_0
|
||||||
|
variable PCW_SILICON_VER_2_0
|
||||||
|
variable PCW_SILICON_VER_3_0
|
||||||
|
set sil_ver [ps_version]
|
||||||
|
if { $sil_ver == $PCW_SILICON_VER_1_0} {
|
||||||
|
ps7_mio_init_data_1_0
|
||||||
|
ps7_pll_init_data_1_0
|
||||||
|
ps7_clock_init_data_1_0
|
||||||
|
ps7_ddr_init_data_1_0
|
||||||
|
ps7_peripherals_init_data_1_0
|
||||||
|
#puts "PCW Silicon Version : 1.0"
|
||||||
|
} elseif { $sil_ver == $PCW_SILICON_VER_2_0 } {
|
||||||
|
ps7_mio_init_data_2_0
|
||||||
|
ps7_pll_init_data_2_0
|
||||||
|
ps7_clock_init_data_2_0
|
||||||
|
ps7_ddr_init_data_2_0
|
||||||
|
ps7_peripherals_init_data_2_0
|
||||||
|
#puts "PCW Silicon Version : 2.0"
|
||||||
|
} else {
|
||||||
|
ps7_mio_init_data_3_0
|
||||||
|
ps7_pll_init_data_3_0
|
||||||
|
ps7_clock_init_data_3_0
|
||||||
|
ps7_ddr_init_data_3_0
|
||||||
|
ps7_peripherals_init_data_3_0
|
||||||
|
#puts "PCW Silicon Version : 3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# For delay calculation using global timer
|
||||||
|
|
||||||
|
# start timer
|
||||||
|
proc perf_start_clock { } {
|
||||||
|
|
||||||
|
#writing SCU_GLOBAL_TIMER_CONTROL register
|
||||||
|
|
||||||
|
mask_write 0xF8F00208 0x00000109 0x00000009
|
||||||
|
}
|
||||||
|
|
||||||
|
# stop timer and reset timer count regs
|
||||||
|
proc perf_reset_clock { } {
|
||||||
|
perf_disable_clock
|
||||||
|
mask_write 0xF8F00200 0xFFFFFFFF 0x00000000
|
||||||
|
mask_write 0xF8F00204 0xFFFFFFFF 0x00000000
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compute mask for given delay in miliseconds
|
||||||
|
proc get_number_of_cycles_for_delay { delay } {
|
||||||
|
|
||||||
|
# GTC is always clocked at 1/2 of the CPU frequency (CPU_3x2x)
|
||||||
|
variable APU_FREQ
|
||||||
|
return [ expr ($delay * $APU_FREQ /(2 * 1000))]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# stop timer
|
||||||
|
proc perf_disable_clock {} {
|
||||||
|
mask_write 0xF8F00208 0xFFFFFFFF 0x00000000
|
||||||
|
}
|
||||||
|
|
||||||
|
proc perf_reset_and_start_timer {} {
|
||||||
|
perf_reset_clock
|
||||||
|
perf_start_clock
|
||||||
|
}
|
||||||
|
|
||||||
|
|
20
scripts/runner.sh
Executable file
20
scripts/runner.sh
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Exit if no arguments are provided
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
echo "Error: No arguments provided."
|
||||||
|
echo "Usage: run.sh <binary>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the absolute path to the `scripts/` directory
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
|
||||||
|
|
||||||
|
# Get the absolute path to the project root
|
||||||
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# Run the initialization script
|
||||||
|
"$SCRIPT_DIR/zynq7000-init.py"
|
||||||
|
|
||||||
|
# Run the GDB debugger in GUI mode.
|
||||||
|
gdb-multiarch -q -x gdb.gdb "$@" -tui
|
3
scripts/unittest.sh
Executable file
3
scripts/unittest.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cargo +stable test --target $(rustc -vV | grep host | cut -d ' ' -f2) -p zynq7000-hal
|
||||||
|
cargo +stable test --target $(rustc -vV | grep host | cut -d ' ' -f2) -p zynq7000
|
86
scripts/xsct-init.tcl
Normal file
86
scripts/xsct-init.tcl
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
if {[info exists env(ip_address_hw_server)]} {
|
||||||
|
set ip $env(ip_address_hw_server)
|
||||||
|
} else {
|
||||||
|
set ip "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
set init_tcl ""
|
||||||
|
if {[llength $argv] >= 1} {
|
||||||
|
set init_tcl [lindex $argv 0]
|
||||||
|
} else {
|
||||||
|
puts "error: missing required first argument pointing to initialization TCL script"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if {![file exists $init_tcl]} {
|
||||||
|
puts "the ps init tcl script '$init_tcl' does not exist"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse command-line arguments
|
||||||
|
set bitstream ""
|
||||||
|
if {[llength $argv] >= 2} {
|
||||||
|
set bitstream [lindex $argv 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
puts "Hardware server IP address: $ip"
|
||||||
|
connect -url tcp:$ip:3121
|
||||||
|
|
||||||
|
set devices [targets]
|
||||||
|
|
||||||
|
set apu_line [string trim [targets -nocase -filter {name =~ "APU"}]]
|
||||||
|
set arm_core_0_line [string trim [targets -nocase -filter {name =~ "ARM Cortex-A9 MPCore #0"}]]
|
||||||
|
set fpga_line [string trim [targets -nocase -filter {name =~ "xc7z020"}]]
|
||||||
|
|
||||||
|
set apu_device_num [string index $apu_line 0]
|
||||||
|
set arm_core_0_num [string index $arm_core_0_line 0]
|
||||||
|
set fpga_device_num [string index $fpga_line 0]
|
||||||
|
|
||||||
|
puts "Select ps target with number: $apu_device_num"
|
||||||
|
|
||||||
|
# Select the target
|
||||||
|
target $apu_device_num
|
||||||
|
|
||||||
|
# Resetting the target involved problems when an image is stored on the flash.
|
||||||
|
# It has turned out that it is not essential to reset the system before loading
|
||||||
|
# the software components into the device.
|
||||||
|
puts "Reset target"
|
||||||
|
# TODO: Make the reset optional/configurable via input argument.
|
||||||
|
# Reset the target
|
||||||
|
rst
|
||||||
|
|
||||||
|
# Check if bitstream is set and the file exists before programming FPGA
|
||||||
|
if {$bitstream eq ""} {
|
||||||
|
puts "Skipping bitstream programming (bitstream argument not set)"
|
||||||
|
} elseif {![file exists $bitstream]} {
|
||||||
|
puts "Error: The bitstream file '$bitstream' does not exist"
|
||||||
|
} else {
|
||||||
|
puts "Set FPGA target with number: $fpga_device_num"
|
||||||
|
target $fpga_device_num
|
||||||
|
|
||||||
|
# Without this delay, the FPGA programming may fail
|
||||||
|
after 1500
|
||||||
|
|
||||||
|
puts "Programming FPGA with bitstream: $bitstream"
|
||||||
|
fpga -f $bitstream
|
||||||
|
}
|
||||||
|
|
||||||
|
puts "Set ps target with device number: $arm_core_0_num"
|
||||||
|
targets $arm_core_0_num
|
||||||
|
|
||||||
|
puts "Initialize processing system"
|
||||||
|
# Init processing system
|
||||||
|
source $init_tcl
|
||||||
|
|
||||||
|
ps7_init
|
||||||
|
ps7_post_config
|
||||||
|
|
||||||
|
puts "Set arm core 0 target with number: $arm_core_0_num"
|
||||||
|
target $arm_core_0_num
|
||||||
|
|
||||||
|
if {[info exists env(APP)] && [file exists $env(APP)]} {
|
||||||
|
puts "Download app $env(APP) to target"
|
||||||
|
dow $env(APP)
|
||||||
|
}
|
||||||
|
|
||||||
|
puts "Successful"
|
153
scripts/zynq7000-init.py
Executable file
153
scripts/zynq7000-init.py
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import shlex
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Define the default values
|
||||||
|
TCL_SCRIPT_NAME = "xsct-init.tcl"
|
||||||
|
SCRIPTS_DIR = "scripts"
|
||||||
|
DEFAULT_IP_ADDRESS_HW_SERVER = "localhost"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Wrapper script for xsct-init.tcl\n"
|
||||||
|
"It launches xsct, initialize and prepare the processing system for debugging and "
|
||||||
|
"allows loading a bitstream and/or bare-metal application to the board.",
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter, # This preserves line breaks
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--tools",
|
||||||
|
# Required only if env var is not set
|
||||||
|
required=not bool(os.getenv("AMD_TOOLS")),
|
||||||
|
# Use env var if set
|
||||||
|
default=os.getenv("AMD_TOOLS"),
|
||||||
|
help="The path to the tool to use. Must point to a valid Vivado tools installation which"
|
||||||
|
"also provides xsct, for example a Vitis installation.\nThe directory where the path "
|
||||||
|
"points to should contain a shell script named settings64.sh.\nYou can also set the "
|
||||||
|
"AMD_TOOLS env variable to set this.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--sdt",
|
||||||
|
dest="sdt",
|
||||||
|
help="Path to a SDT output folder which contains a PS7 init TCL script and a bitstream.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-bit",
|
||||||
|
dest="no_bit",
|
||||||
|
action="store_true",
|
||||||
|
help="No bitstream flashing for initialization with SDT.",
|
||||||
|
)
|
||||||
|
parser.add_argument("-a", "--app", dest="app", help="Path to the app to program")
|
||||||
|
parser.add_argument(
|
||||||
|
"-i",
|
||||||
|
"--ip",
|
||||||
|
default=DEFAULT_IP_ADDRESS_HW_SERVER,
|
||||||
|
help="The IP address of the hardware server (default: localhost)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--itcl",
|
||||||
|
dest="init_tcl",
|
||||||
|
default=os.getenv("TCL_INIT_SCRIPT"),
|
||||||
|
help="Path to the ps7 initialization TCL file to prepare the processing system.\n"
|
||||||
|
"You can also set the TCL_INIT_SCRIPT env variable to set this.\n"
|
||||||
|
"It is also set implicitely when specifying the SDT folder with --sdt"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-b",
|
||||||
|
"--bit",
|
||||||
|
dest="bit",
|
||||||
|
default=os.getenv("ZYNQ_BITSTREAM"),
|
||||||
|
help="Optional path to the bitstream which will also be programed to the device. It is"
|
||||||
|
" also searched automatically if the --sdt option is used.\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
settings_script = os.path.join(args.tools, "settings64.sh")
|
||||||
|
|
||||||
|
if not os.path.isfile(settings_script):
|
||||||
|
print(f"Invalid tool path {args.tools}, did not find settings file.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Source the settings script and check for xsdb availability
|
||||||
|
command = f"source {settings_script} && command -v xsct"
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
capture_output=True,
|
||||||
|
executable="/bin/bash",
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("Error: 'xsct' could not be found after sourcing settings64.sh.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.app and not os.path.isfile(args.app):
|
||||||
|
print(f"The app '{args.app}' does not exist")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Export environment variables
|
||||||
|
if args.app:
|
||||||
|
os.environ["APP"] = args.app
|
||||||
|
os.environ["IP_ADDRESS_HW_SERVER"] = args.ip
|
||||||
|
init_tcl = None
|
||||||
|
bitstream = None
|
||||||
|
if args.bit:
|
||||||
|
bitstream = args.bit
|
||||||
|
if args.sdt:
|
||||||
|
sdt_path = Path(args.sdt)
|
||||||
|
# CLI bitstream argument overrides implicit SDT bitstream.
|
||||||
|
if not args.no_bit and not bitstream:
|
||||||
|
# Search for .bit files in the SDT folder
|
||||||
|
bit_files = list(sdt_path.glob("*.bit"))
|
||||||
|
if len(bit_files) == 0:
|
||||||
|
print("Error: No .bit file found in the SDT folder.")
|
||||||
|
elif len(bit_files) > 1:
|
||||||
|
print("Error: Multiple .bit files found in the SDT folder.")
|
||||||
|
bitstream = str(bit_files[0])
|
||||||
|
# Search for the ps7_init.tcl file
|
||||||
|
init_script = sdt_path / "ps7_init.tcl"
|
||||||
|
if not init_script.exists():
|
||||||
|
sys.exit("Error: ps7_init.tcl file not found in the SDT folder.")
|
||||||
|
|
||||||
|
init_tcl = str(init_script)
|
||||||
|
else:
|
||||||
|
if not args.init_tcl:
|
||||||
|
print("Error: No ps7_init.tcl file specified.")
|
||||||
|
sys.exit(1)
|
||||||
|
if args.bit:
|
||||||
|
bitstream = args.bit
|
||||||
|
init_tcl = args.init_tcl
|
||||||
|
|
||||||
|
xsct_script = Path(TCL_SCRIPT_NAME)
|
||||||
|
|
||||||
|
if not xsct_script.exists():
|
||||||
|
xsct_script = Path(os.path.join(SCRIPTS_DIR, TCL_SCRIPT_NAME))
|
||||||
|
if not xsct_script.exists():
|
||||||
|
print(f"Could not find the xsct initialization script {TCL_SCRIPT_NAME}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Launch xsct with the initialization script
|
||||||
|
# Prepare tcl_args as a list to avoid manual string concatenation issues
|
||||||
|
cmd_list = ["xsct", str(xsct_script), init_tcl]
|
||||||
|
if bitstream:
|
||||||
|
cmd_list.append(bitstream)
|
||||||
|
|
||||||
|
# Join safely for shell execution
|
||||||
|
xsct_cmd = shlex.join(cmd_list)
|
||||||
|
print(f"Running xsct command: {xsct_cmd}")
|
||||||
|
command = f"bash -c 'source {settings_script} && {xsct_cmd} | tee xsct-output.log'"
|
||||||
|
subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
155
vscode/launch.json
Normal file
155
vscode/launch.json
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Blinky Example",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/armv7a-none-eabihf/debug/simple",
|
||||||
|
"miDebuggerServerAddress": "localhost:3000",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb-multiarch",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"useExtendedRemote": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"launchCompleteCommand": "None",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Load symbols for the program",
|
||||||
|
"text": "symbol-file ${workspaceFolder}/target/armv7a-none-eabihf/debug/simple",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "set output-radix 16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "flash-blinky"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Logger Example",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/armv7a-none-eabihf/debug/logger",
|
||||||
|
"miDebuggerServerAddress": "localhost:3000",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb-multiarch",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"useExtendedRemote": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"launchCompleteCommand": "None",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Load symbols for the program",
|
||||||
|
"text": "symbol-file ${workspaceFolder}/target/armv7a-none-eabihf/debug/logger",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "set output-radix 16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "flash-logger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Embassy Example",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/armv7a-none-eabihf/debug/embassy-examples",
|
||||||
|
"miDebuggerServerAddress": "localhost:3000",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb-multiarch",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"useExtendedRemote": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"launchCompleteCommand": "None",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Load symbols for the program",
|
||||||
|
"text": "symbol-file ${workspaceFolder}/target/armv7a-none-eabihf/debug/embassy-examples",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "set output-radix 16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "flash-embassy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Zedboard GPIOs",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/armv7a-none-eabihf/debug/zedboard",
|
||||||
|
"miDebuggerServerAddress": "localhost:3000",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb-multiarch",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"useExtendedRemote": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"launchCompleteCommand": "None",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Load symbols for the program",
|
||||||
|
"text": "symbol-file ${workspaceFolder}/target/armv7a-none-eabihf/debug/zedboard",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "set output-radix 16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "flash-zed-gpios"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug UART Non-Blocking",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/target/armv7a-none-eabihf/debug/uart-non-blocking",
|
||||||
|
"miDebuggerServerAddress": "localhost:3000",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb-multiarch",
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"useExtendedRemote": true,
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"launchCompleteCommand": "None",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Load symbols for the program",
|
||||||
|
"text": "symbol-file ${workspaceFolder}/target/armv7a-none-eabihf/debug/uart-non-blocking",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "set output-radix 16"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "flash-uart-non-blocking"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
163
vscode/tasks.json
Normal file
163
vscode/tasks.json
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
// Pass environment variables to the zynq7000-init.py script.
|
||||||
|
"AMD_TOOLS": "/tools/Xilinx/Vitis/2024.1",
|
||||||
|
"TCL_INIT_SCRIPT": "${workspaceFolder}/zedboard-fpga-design/sdt_out/ps7_init.tcl",
|
||||||
|
// Leading _, otherwise the Python script will always flash it.
|
||||||
|
"_ZYNQ_BITSTREAM": "${workspaceFolder}/zedboard-fpga-design/sdt_out/zedboard-rust.bit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "zedboard-init",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"--bit",
|
||||||
|
"${BITSTREAM}"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "flash-blinky",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"-a",
|
||||||
|
"${workspaceFolder}/target/armv7a-none-eabihf/debug/simple"
|
||||||
|
],
|
||||||
|
"dependsOn": [
|
||||||
|
"build-blinky"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-blinky",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"-p",
|
||||||
|
"simple"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "flash-logger",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"-a",
|
||||||
|
"${workspaceFolder}/target/armv7a-none-eabihf/debug/logger"
|
||||||
|
],
|
||||||
|
"dependsOn": [
|
||||||
|
"build-logger"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-logger",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"logger"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "flash-embassy",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"-a",
|
||||||
|
"${workspaceFolder}/target/armv7a-none-eabihf/debug/embassy-examples"
|
||||||
|
],
|
||||||
|
"dependsOn": [
|
||||||
|
"build-embassy"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-embassy",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"-p",
|
||||||
|
"embassy-examples"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "flash-zed-gpios",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"-a",
|
||||||
|
"${workspaceFolder}/target/armv7a-none-eabihf/debug/zedboard",
|
||||||
|
// The bitstream does not necesarilly need to be flashed each time, but doing so here requires one
|
||||||
|
// less extra step to prepare the system.
|
||||||
|
"--bit",
|
||||||
|
"${_ZYNQ_BITSTREAM}"
|
||||||
|
],
|
||||||
|
"dependsOn": [
|
||||||
|
"build-zed-gpios"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-zed-gpios",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"zedboard"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "flash-uart-non-blocking",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${workspaceFolder}/scripts/zynq7000-init.py",
|
||||||
|
"args": [
|
||||||
|
"-a",
|
||||||
|
"${workspaceFolder}/target/armv7a-none-eabihf/debug/uart-non-blocking",
|
||||||
|
// The bitstream does not necesarilly need to be flashed each time, but doing so here requires one
|
||||||
|
// less extra step to prepare the system.
|
||||||
|
"--bit",
|
||||||
|
"${_ZYNQ_BITSTREAM}"
|
||||||
|
],
|
||||||
|
"dependsOn": [
|
||||||
|
"build-uart-non-blocking"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "build-uart-non-blocking",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"uart-non-blocking"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
zedboard-fpga-design/.gitignore
vendored
Normal file
5
zedboard-fpga-design/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/zedboard-rust
|
||||||
|
/.Xil
|
||||||
|
/vivado*
|
||||||
|
/sdt_out
|
||||||
|
/xsct-output.log
|
40
zedboard-fpga-design/README.md
Normal file
40
zedboard-fpga-design/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Zedboard FPGA design for Rust
|
||||||
|
=======
|
||||||
|
|
||||||
|
This is an example/reference design which was used to verify various components provided
|
||||||
|
by this library. To minimize the amount of HW designs required, one project is provided.
|
||||||
|
The design was kept as generic as possible. In principle, it should be possible to adapt the
|
||||||
|
hardware design to other boards with modifications.
|
||||||
|
|
||||||
|
# Pre-Requisites
|
||||||
|
|
||||||
|
- [Vivado installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools.html)
|
||||||
|
or [Vitis installation](https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vitis.html)
|
||||||
|
which includes Vivado. This example design was created with/for Vivado 2024.1, but also might work
|
||||||
|
for newer versions.
|
||||||
|
- [Zedboard board files](https://github.com/Digilent/vivado-boards) added to the Vivado installation.
|
||||||
|
|
||||||
|
# Loading the project and the block design with the GUI
|
||||||
|
|
||||||
|
You can load the project using the batch mode of `vivado` inside the folder where you want to
|
||||||
|
create the `zedboard-rust` project:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vivado -mode batch -source <path to zedboard-rust.tcl> -tclargs --overwrite
|
||||||
|
```
|
||||||
|
|
||||||
|
for example, to create the directory directly insdie this directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vivado -mode batch -source zedboard-rust.tcl -tclargs --overwrite
|
||||||
|
```
|
||||||
|
|
||||||
|
This should create a `zedboard-rust` Vivado project folder containing a `zedboard-rust.xpr`
|
||||||
|
project file. You can load this project file with Vivado:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vivado zedboard-rust.xpr
|
||||||
|
```
|
||||||
|
|
||||||
|
You can perform all the steps specified in the Vivado GUI as well using `Execute TCL script` and
|
||||||
|
`Load Project`.
|
2
zedboard-fpga-design/export-hw.tcl
Normal file
2
zedboard-fpga-design/export-hw.tcl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
set hw_file "[get_property DIRECTORY [current_project]]/zedboard-rust.xsa"
|
||||||
|
write_hw_platform -fixed -include_bit -force -file $hw_file
|
69
zedboard-fpga-design/sdtgen.py
Executable file
69
zedboard-fpga-design/sdtgen.py
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Script to generate SDT files from a XSA file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--tools",
|
||||||
|
# Required only if env var is not set
|
||||||
|
required=not bool(os.getenv("AMD_TOOLS")),
|
||||||
|
# Use env var if set
|
||||||
|
default=os.getenv("AMD_TOOLS"),
|
||||||
|
help="The path to the tool to use. Must point to a valid Vivado tools installation which"
|
||||||
|
"also provides xsct, for example a Vitis installation.\nThe directory where the path "
|
||||||
|
"points to should contain a shell script named settings64.sh.\n You can also set the "
|
||||||
|
"AMD_TOOLS env variable to set this.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-x",
|
||||||
|
"--xsa",
|
||||||
|
help="Path to the input XSA file",
|
||||||
|
default="zedboard-rust/zedboard-rust.xsa",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--out",
|
||||||
|
default="sdt_out",
|
||||||
|
help="Directory to store the generated SDT files",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
settings_script = os.path.join(args.tools, "settings64.sh")
|
||||||
|
|
||||||
|
if not os.path.isfile(settings_script):
|
||||||
|
print(f"Invalid tool path {args.tools}, did not find settings file.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Source the settings script and check for xsdb availability
|
||||||
|
command = f"source {settings_script} && command -v xsct"
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
capture_output=True,
|
||||||
|
executable="/bin/bash",
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("Error: 'xsct' could not be found after sourcing settings64.sh.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
xsct_script = "sdtgen.tcl"
|
||||||
|
|
||||||
|
command = f"bash -c 'source {settings_script} && xsct {xsct_script} {args.xsa} {args.out} | tee xsct-output.log'"
|
||||||
|
subprocess.run(
|
||||||
|
command,
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
4
zedboard-fpga-design/sdtgen.tcl
Normal file
4
zedboard-fpga-design/sdtgen.tcl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
set outdir [lindex $argv 1]
|
||||||
|
set xsa [lindex $argv 0]
|
||||||
|
sdtgen set_dt_param -xsa $xsa -dir $outdir -board_dts zedboard
|
||||||
|
sdtgen generate_sdt
|
88
zedboard-fpga-design/src/uart_mux.vhd
Normal file
88
zedboard-fpga-design/src/uart_mux.vhd
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
----------------------------------------------------------------------------------
|
||||||
|
-- Company: Institute of Space Systems, University of Stuttgart
|
||||||
|
-- Engineer: Robin Mueller
|
||||||
|
--
|
||||||
|
-- Description:
|
||||||
|
-- The module multiplexes three UART modules.
|
||||||
|
-- It can be used to select between UART0 through EMIO, UARTLITE or UART16550.
|
||||||
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
library IEEE;
|
||||||
|
use IEEE.STD_LOGIC_1164.ALL;
|
||||||
|
|
||||||
|
entity uart_mux is
|
||||||
|
port(
|
||||||
|
sys_clk : in std_logic;
|
||||||
|
|
||||||
|
uart_0_tx : in std_logic;
|
||||||
|
uart_0_rx : out std_logic;
|
||||||
|
|
||||||
|
uart_1_tx : in std_logic;
|
||||||
|
uart_1_rx : out std_logic;
|
||||||
|
|
||||||
|
uart_2_tx : in std_logic;
|
||||||
|
uart_2_rx : out std_logic;
|
||||||
|
|
||||||
|
tx_out: out std_logic;
|
||||||
|
rx_in: in std_logic;
|
||||||
|
|
||||||
|
-- "000" -> UART0
|
||||||
|
-- "001" -> UART1
|
||||||
|
-- "010" -> UART2
|
||||||
|
-- "011" -> UART0 to UART1
|
||||||
|
-- "100" -> UART0 to UART2
|
||||||
|
-- "101" -> UART1 to UART2
|
||||||
|
sel : in std_logic_vector(2 downto 0)
|
||||||
|
|
||||||
|
);
|
||||||
|
end uart_mux;
|
||||||
|
|
||||||
|
architecture Behavioral of uart_mux is
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
switch : process(sys_clk)
|
||||||
|
begin
|
||||||
|
if rising_edge(sys_clk) then
|
||||||
|
case sel is
|
||||||
|
when "000" =>
|
||||||
|
tx_out <= uart_0_tx;
|
||||||
|
uart_0_rx <= rx_in;
|
||||||
|
uart_1_rx <= '1';
|
||||||
|
uart_2_rx <= '1';
|
||||||
|
when "001" =>
|
||||||
|
tx_out <= uart_1_tx;
|
||||||
|
uart_1_rx <= rx_in;
|
||||||
|
uart_2_rx <= '1';
|
||||||
|
uart_0_rx <= '1';
|
||||||
|
when "010" =>
|
||||||
|
tx_out <= uart_2_tx;
|
||||||
|
uart_2_rx <= rx_in;
|
||||||
|
uart_1_rx <= '1';
|
||||||
|
uart_0_rx <= '1';
|
||||||
|
when "011" =>
|
||||||
|
tx_out <= '1';
|
||||||
|
uart_1_rx <= uart_0_tx;
|
||||||
|
uart_0_rx <= uart_1_tx;
|
||||||
|
uart_2_rx <= '1';
|
||||||
|
when "100" =>
|
||||||
|
tx_out <= '1';
|
||||||
|
uart_2_rx <= uart_0_tx;
|
||||||
|
uart_0_rx <= uart_2_tx;
|
||||||
|
uart_1_rx <= '1';
|
||||||
|
when "101" =>
|
||||||
|
tx_out <= '1';
|
||||||
|
uart_1_rx <= uart_2_tx;
|
||||||
|
uart_2_rx <= uart_1_tx;
|
||||||
|
uart_0_rx <= '1';
|
||||||
|
when others =>
|
||||||
|
tx_out <= '1';
|
||||||
|
uart_0_rx <= '1';
|
||||||
|
uart_1_rx <= '1';
|
||||||
|
uart_2_rx <= '1';
|
||||||
|
end case;
|
||||||
|
end if;
|
||||||
|
end process; -- switch
|
||||||
|
|
||||||
|
end Behavioral;
|
698
zedboard-fpga-design/src/zedboard-bd.tcl
Normal file
698
zedboard-fpga-design/src/zedboard-bd.tcl
Normal file
@ -0,0 +1,698 @@
|
|||||||
|
|
||||||
|
################################################################
|
||||||
|
# This is a generated script based on design: zedboard
|
||||||
|
#
|
||||||
|
# Though there are limitations about the generated script,
|
||||||
|
# the main purpose of this utility is to make learning
|
||||||
|
# IP Integrator Tcl commands easier.
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
namespace eval _tcl {
|
||||||
|
proc get_script_folder {} {
|
||||||
|
set script_path [file normalize [info script]]
|
||||||
|
set script_folder [file dirname $script_path]
|
||||||
|
return $script_folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variable script_folder
|
||||||
|
set script_folder [_tcl::get_script_folder]
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Check if script is running in correct Vivado version.
|
||||||
|
################################################################
|
||||||
|
set scripts_vivado_version 2024.1
|
||||||
|
set current_vivado_version [version -short]
|
||||||
|
|
||||||
|
if { [string first $scripts_vivado_version $current_vivado_version] == -1 } {
|
||||||
|
puts ""
|
||||||
|
if { [string compare $scripts_vivado_version $current_vivado_version] > 0 } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2042 -severity "ERROR" " This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Sourcing the script failed since it was created with a future version of Vivado."}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2041 -severity "ERROR" "This script was generated using Vivado <$scripts_vivado_version> and is being run in <$current_vivado_version> of Vivado. Please run the script in Vivado <$scripts_vivado_version> then open the design in Vivado <$current_vivado_version>. Upgrade the design by running \"Tools => Report => Report IP Status...\", then run write_bd_tcl to create an updated script."}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# START
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# To test this script, run the following commands from Vivado Tcl console:
|
||||||
|
# source zedboard_script.tcl
|
||||||
|
|
||||||
|
|
||||||
|
# The design that will be created by this Tcl script contains the following
|
||||||
|
# module references:
|
||||||
|
# uart_mux
|
||||||
|
|
||||||
|
# Please add the sources of those modules before sourcing this Tcl script.
|
||||||
|
|
||||||
|
# If there is no project opened, this script will create a
|
||||||
|
# project, but make sure you do not have an existing project
|
||||||
|
# <./myproj/project_1.xpr> in the current working folder.
|
||||||
|
|
||||||
|
set list_projs [get_projects -quiet]
|
||||||
|
if { $list_projs eq "" } {
|
||||||
|
create_project project_1 myproj -part xc7z020clg484-1
|
||||||
|
set_property BOARD_PART digilentinc.com:zedboard:part0:1.1 [current_project]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# CHANGE DESIGN NAME HERE
|
||||||
|
variable design_name
|
||||||
|
set design_name zedboard
|
||||||
|
|
||||||
|
# If you do not already have an existing IP Integrator design open,
|
||||||
|
# you can create a design using the following command:
|
||||||
|
# create_bd_design $design_name
|
||||||
|
|
||||||
|
# Creating design if needed
|
||||||
|
set errMsg ""
|
||||||
|
set nRet 0
|
||||||
|
|
||||||
|
set cur_design [current_bd_design -quiet]
|
||||||
|
set list_cells [get_bd_cells -quiet]
|
||||||
|
|
||||||
|
if { ${design_name} eq "" } {
|
||||||
|
# USE CASES:
|
||||||
|
# 1) Design_name not set
|
||||||
|
|
||||||
|
set errMsg "Please set the variable <design_name> to a non-empty value."
|
||||||
|
set nRet 1
|
||||||
|
|
||||||
|
} elseif { ${cur_design} ne "" && ${list_cells} eq "" } {
|
||||||
|
# USE CASES:
|
||||||
|
# 2): Current design opened AND is empty AND names same.
|
||||||
|
# 3): Current design opened AND is empty AND names diff; design_name NOT in project.
|
||||||
|
# 4): Current design opened AND is empty AND names diff; design_name exists in project.
|
||||||
|
|
||||||
|
if { $cur_design ne $design_name } {
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2001 -severity "INFO" "Changing value of <design_name> from <$design_name> to <$cur_design> since current design is empty."
|
||||||
|
set design_name [get_property NAME $cur_design]
|
||||||
|
}
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2002 -severity "INFO" "Constructing design in IPI design <$cur_design>..."
|
||||||
|
|
||||||
|
} elseif { ${cur_design} ne "" && $list_cells ne "" && $cur_design eq $design_name } {
|
||||||
|
# USE CASES:
|
||||||
|
# 5) Current design opened AND has components AND same names.
|
||||||
|
|
||||||
|
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
|
||||||
|
set nRet 1
|
||||||
|
} elseif { [get_files -quiet ${design_name}.bd] ne "" } {
|
||||||
|
# USE CASES:
|
||||||
|
# 6) Current opened design, has components, but diff names, design_name exists in project.
|
||||||
|
# 7) No opened design, design_name exists in project.
|
||||||
|
|
||||||
|
set errMsg "Design <$design_name> already exists in your project, please set the variable <design_name> to another value."
|
||||||
|
set nRet 2
|
||||||
|
|
||||||
|
} else {
|
||||||
|
# USE CASES:
|
||||||
|
# 8) No opened design, design_name not in project.
|
||||||
|
# 9) Current opened design, has components, but diff names, design_name not in project.
|
||||||
|
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2003 -severity "INFO" "Currently there is no design <$design_name> in project, so creating one..."
|
||||||
|
|
||||||
|
create_bd_design $design_name
|
||||||
|
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2004 -severity "INFO" "Making design <$design_name> as current_bd_design."
|
||||||
|
current_bd_design $design_name
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2005 -severity "INFO" "Currently the variable <design_name> is equal to \"$design_name\"."
|
||||||
|
|
||||||
|
if { $nRet != 0 } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2006 -severity "ERROR" $errMsg}
|
||||||
|
return $nRet
|
||||||
|
}
|
||||||
|
|
||||||
|
set bCheckIPsPassed 1
|
||||||
|
##################################################################
|
||||||
|
# CHECK IPs
|
||||||
|
##################################################################
|
||||||
|
set bCheckIPs 1
|
||||||
|
if { $bCheckIPs == 1 } {
|
||||||
|
set list_check_ips "\
|
||||||
|
xilinx.com:ip:processing_system7:5.5\
|
||||||
|
xilinx.com:ip:axi_uartlite:2.0\
|
||||||
|
xilinx.com:ip:proc_sys_reset:5.0\
|
||||||
|
xilinx.com:ip:axi_uart16550:2.0\
|
||||||
|
xilinx.com:ip:xlconcat:2.1\
|
||||||
|
xilinx.com:ip:xlslice:1.0\
|
||||||
|
xilinx.com:ip:xlconstant:1.1\
|
||||||
|
"
|
||||||
|
|
||||||
|
set list_ips_missing ""
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2011 -severity "INFO" "Checking if the following IPs exist in the project's IP catalog: $list_check_ips ."
|
||||||
|
|
||||||
|
foreach ip_vlnv $list_check_ips {
|
||||||
|
set ip_obj [get_ipdefs -all $ip_vlnv]
|
||||||
|
if { $ip_obj eq "" } {
|
||||||
|
lappend list_ips_missing $ip_vlnv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $list_ips_missing ne "" } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2012 -severity "ERROR" "The following IPs are not found in the IP Catalog:\n $list_ips_missing\n\nResolution: Please add the repository containing the IP(s) to the project." }
|
||||||
|
set bCheckIPsPassed 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# CHECK Modules
|
||||||
|
##################################################################
|
||||||
|
set bCheckModules 1
|
||||||
|
if { $bCheckModules == 1 } {
|
||||||
|
set list_check_mods "\
|
||||||
|
uart_mux\
|
||||||
|
"
|
||||||
|
|
||||||
|
set list_mods_missing ""
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2020 -severity "INFO" "Checking if the following modules exist in the project's sources: $list_check_mods ."
|
||||||
|
|
||||||
|
foreach mod_vlnv $list_check_mods {
|
||||||
|
if { [can_resolve_reference $mod_vlnv] == 0 } {
|
||||||
|
lappend list_mods_missing $mod_vlnv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $list_mods_missing ne "" } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2021 -severity "ERROR" "The following module(s) are not found in the project: $list_mods_missing" }
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2022 -severity "INFO" "Please add source files for the missing module(s) above."
|
||||||
|
set bCheckIPsPassed 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if { $bCheckIPsPassed != 1 } {
|
||||||
|
common::send_gid_msg -ssname BD::TCL -id 2023 -severity "WARNING" "Will not continue with creation of design due to the error(s) above."
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# DESIGN PROCs
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Procedure to create entire design; Provide argument to make
|
||||||
|
# procedure reusable. If parentCell is "", will use root.
|
||||||
|
proc create_root_design { parentCell } {
|
||||||
|
|
||||||
|
variable script_folder
|
||||||
|
variable design_name
|
||||||
|
|
||||||
|
if { $parentCell eq "" } {
|
||||||
|
set parentCell [get_bd_cells /]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get object for parentCell
|
||||||
|
set parentObj [get_bd_cells $parentCell]
|
||||||
|
if { $parentObj == "" } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2090 -severity "ERROR" "Unable to find parent cell <$parentCell>!"}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure parentObj is hier blk
|
||||||
|
set parentType [get_property TYPE $parentObj]
|
||||||
|
if { $parentType ne "hier" } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2091 -severity "ERROR" "Parent <$parentObj> has TYPE = <$parentType>. Expected to be <hier>."}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save current instance; Restore later
|
||||||
|
set oldCurInst [current_bd_instance .]
|
||||||
|
|
||||||
|
# Set parent object as current
|
||||||
|
current_bd_instance $parentObj
|
||||||
|
|
||||||
|
|
||||||
|
# Create interface ports
|
||||||
|
set DDR [ create_bd_intf_port -mode Master -vlnv xilinx.com:interface:ddrx_rtl:1.0 DDR ]
|
||||||
|
|
||||||
|
set FIXED_IO [ create_bd_intf_port -mode Master -vlnv xilinx.com:display_processing_system7:fixedio_rtl:1.0 FIXED_IO ]
|
||||||
|
|
||||||
|
|
||||||
|
# Create ports
|
||||||
|
set LEDS [ create_bd_port -dir O -from 7 -to 0 LEDS ]
|
||||||
|
set SWITCHES [ create_bd_port -dir I -from 7 -to 0 SWITCHES ]
|
||||||
|
set BTTNS [ create_bd_port -dir I -from 4 -to 0 BTTNS ]
|
||||||
|
set UART_txd [ create_bd_port -dir O UART_txd ]
|
||||||
|
set UART_rxd [ create_bd_port -dir I UART_rxd ]
|
||||||
|
set TTC0_WAVEOUT [ create_bd_port -dir O TTC0_WAVEOUT ]
|
||||||
|
|
||||||
|
# Create instance: processing_system7_0, and set properties
|
||||||
|
set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.PCW_ACT_APU_PERIPHERAL_FREQMHZ {650.000000} \
|
||||||
|
CONFIG.PCW_ACT_CAN_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_DCI_PERIPHERAL_FREQMHZ {10.158730} \
|
||||||
|
CONFIG.PCW_ACT_ENET0_PERIPHERAL_FREQMHZ {125.000000} \
|
||||||
|
CONFIG.PCW_ACT_ENET1_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_FPGA0_PERIPHERAL_FREQMHZ {100.000000} \
|
||||||
|
CONFIG.PCW_ACT_FPGA1_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_FPGA2_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_FPGA3_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_PCAP_PERIPHERAL_FREQMHZ {200.000000} \
|
||||||
|
CONFIG.PCW_ACT_QSPI_PERIPHERAL_FREQMHZ {200.000000} \
|
||||||
|
CONFIG.PCW_ACT_SDIO_PERIPHERAL_FREQMHZ {50.000000} \
|
||||||
|
CONFIG.PCW_ACT_SMC_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_SPI_PERIPHERAL_FREQMHZ {10.000000} \
|
||||||
|
CONFIG.PCW_ACT_TPIU_PERIPHERAL_FREQMHZ {200.000000} \
|
||||||
|
CONFIG.PCW_ACT_TTC0_CLK0_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_TTC0_CLK1_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_TTC0_CLK2_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_TTC1_CLK0_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_TTC1_CLK1_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_TTC1_CLK2_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_ACT_UART_PERIPHERAL_FREQMHZ {100.000000} \
|
||||||
|
CONFIG.PCW_ACT_WDT_PERIPHERAL_FREQMHZ {108.333336} \
|
||||||
|
CONFIG.PCW_APU_PERIPHERAL_FREQMHZ {650} \
|
||||||
|
CONFIG.PCW_CLK0_FREQ {100000000} \
|
||||||
|
CONFIG.PCW_CLK1_FREQ {10000000} \
|
||||||
|
CONFIG.PCW_CLK2_FREQ {10000000} \
|
||||||
|
CONFIG.PCW_CLK3_FREQ {10000000} \
|
||||||
|
CONFIG.PCW_CORE0_FIQ_INTR {0} \
|
||||||
|
CONFIG.PCW_CRYSTAL_PERIPHERAL_FREQMHZ {33.333333} \
|
||||||
|
CONFIG.PCW_DDR_RAM_HIGHADDR {0x1FFFFFFF} \
|
||||||
|
CONFIG.PCW_ENET0_ENET0_IO {MIO 16 .. 27} \
|
||||||
|
CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {1} \
|
||||||
|
CONFIG.PCW_ENET0_GRP_MDIO_IO {EMIO} \
|
||||||
|
CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_ENET0_PERIPHERAL_FREQMHZ {1000 Mbps} \
|
||||||
|
CONFIG.PCW_ENET0_RESET_ENABLE {0} \
|
||||||
|
CONFIG.PCW_ENET_RESET_ENABLE {1} \
|
||||||
|
CONFIG.PCW_ENET_RESET_SELECT {Share reset pin} \
|
||||||
|
CONFIG.PCW_EN_EMIO_GPIO {1} \
|
||||||
|
CONFIG.PCW_EN_EMIO_TTC0 {1} \
|
||||||
|
CONFIG.PCW_EN_EMIO_TTC1 {0} \
|
||||||
|
CONFIG.PCW_EN_EMIO_UART0 {1} \
|
||||||
|
CONFIG.PCW_EN_EMIO_WP_SDIO0 {1} \
|
||||||
|
CONFIG.PCW_EN_ENET0 {1} \
|
||||||
|
CONFIG.PCW_EN_GPIO {1} \
|
||||||
|
CONFIG.PCW_EN_QSPI {1} \
|
||||||
|
CONFIG.PCW_EN_SDIO0 {1} \
|
||||||
|
CONFIG.PCW_EN_TTC0 {1} \
|
||||||
|
CONFIG.PCW_EN_TTC1 {0} \
|
||||||
|
CONFIG.PCW_EN_UART0 {1} \
|
||||||
|
CONFIG.PCW_EN_UART1 {1} \
|
||||||
|
CONFIG.PCW_EN_USB0 {1} \
|
||||||
|
CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ {100} \
|
||||||
|
CONFIG.PCW_FPGA_FCLK0_ENABLE {1} \
|
||||||
|
CONFIG.PCW_GPIO_EMIO_GPIO_ENABLE {1} \
|
||||||
|
CONFIG.PCW_GPIO_EMIO_GPIO_IO {64} \
|
||||||
|
CONFIG.PCW_GPIO_EMIO_GPIO_WIDTH {64} \
|
||||||
|
CONFIG.PCW_GPIO_MIO_GPIO_ENABLE {1} \
|
||||||
|
CONFIG.PCW_GPIO_MIO_GPIO_IO {MIO} \
|
||||||
|
CONFIG.PCW_I2C_RESET_ENABLE {1} \
|
||||||
|
CONFIG.PCW_IRQ_F2P_INTR {1} \
|
||||||
|
CONFIG.PCW_MIO_0_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_0_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_0_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_10_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_10_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_10_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_11_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_11_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_11_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_12_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_12_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_12_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_13_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_13_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_13_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_14_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_14_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_14_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_15_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_15_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_15_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_16_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_16_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_16_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_17_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_17_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_17_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_18_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_18_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_18_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_19_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_19_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_19_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_1_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_1_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_1_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_20_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_20_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_20_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_21_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_21_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_21_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_22_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_22_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_22_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_23_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_23_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_23_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_24_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_24_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_24_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_25_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_25_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_25_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_26_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_26_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_26_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_27_IOTYPE {HSTL 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_27_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_27_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_28_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_28_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_28_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_29_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_29_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_29_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_2_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_2_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_30_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_30_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_30_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_31_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_31_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_31_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_32_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_32_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_32_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_33_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_33_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_33_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_34_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_34_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_34_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_35_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_35_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_35_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_36_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_36_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_36_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_37_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_37_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_37_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_38_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_38_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_38_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_39_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_39_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_39_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_3_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_3_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_40_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_40_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_40_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_41_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_41_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_41_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_42_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_42_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_42_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_43_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_43_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_43_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_44_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_44_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_44_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_45_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_45_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_45_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_46_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_46_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_46_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_47_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_47_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_47_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_48_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_48_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_48_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_49_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_49_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_49_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_4_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_4_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_50_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_50_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_50_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_51_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_51_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_51_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_52_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_52_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_52_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_53_IOTYPE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_MIO_53_PULLUP {disabled} \
|
||||||
|
CONFIG.PCW_MIO_53_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_5_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_5_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_6_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_6_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_7_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_7_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_8_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_8_SLEW {fast} \
|
||||||
|
CONFIG.PCW_MIO_9_IOTYPE {LVCMOS 3.3V} \
|
||||||
|
CONFIG.PCW_MIO_9_PULLUP {enabled} \
|
||||||
|
CONFIG.PCW_MIO_9_SLEW {slow} \
|
||||||
|
CONFIG.PCW_MIO_TREE_PERIPHERALS {GPIO#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#GPIO#Quad SPI Flash#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#Enet 0#Enet 0#Enet\
|
||||||
|
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#USB Reset#SD 0#UART 1#UART 1#GPIO#GPIO#GPIO#GPIO}\
|
||||||
|
\
|
||||||
|
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#qspi_fbclk#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#reset#cd#tx#rx#gpio[50]#gpio[51]#gpio[52]#gpio[53]}\
|
||||||
|
\
|
||||||
|
CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_FBCLK_ENABLE {1} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_FBCLK_IO {MIO 8} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_IO1_ENABLE {0} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_SINGLE_SS_ENABLE {1} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_SINGLE_SS_IO {MIO 1 .. 6} \
|
||||||
|
CONFIG.PCW_QSPI_GRP_SS1_ENABLE {0} \
|
||||||
|
CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200} \
|
||||||
|
CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \
|
||||||
|
CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \
|
||||||
|
CONFIG.PCW_SD0_GRP_CD_IO {MIO 47} \
|
||||||
|
CONFIG.PCW_SD0_GRP_POW_ENABLE {0} \
|
||||||
|
CONFIG.PCW_SD0_GRP_WP_ENABLE {1} \
|
||||||
|
CONFIG.PCW_SD0_GRP_WP_IO {EMIO} \
|
||||||
|
CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \
|
||||||
|
CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {50} \
|
||||||
|
CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \
|
||||||
|
CONFIG.PCW_SINGLE_QSPI_DATA_MODE {x4} \
|
||||||
|
CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_TTC0_TTC0_IO {EMIO} \
|
||||||
|
CONFIG.PCW_TTC1_PERIPHERAL_ENABLE {0} \
|
||||||
|
CONFIG.PCW_TTC_PERIPHERAL_FREQMHZ {50} \
|
||||||
|
CONFIG.PCW_UART0_GRP_FULL_ENABLE {0} \
|
||||||
|
CONFIG.PCW_UART0_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_UART0_UART0_IO {EMIO} \
|
||||||
|
CONFIG.PCW_UART1_GRP_FULL_ENABLE {0} \
|
||||||
|
CONFIG.PCW_UART1_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_UART1_UART1_IO {MIO 48 .. 49} \
|
||||||
|
CONFIG.PCW_UART_PERIPHERAL_FREQMHZ {100} \
|
||||||
|
CONFIG.PCW_UART_PERIPHERAL_VALID {1} \
|
||||||
|
CONFIG.PCW_UIPARAM_ACT_DDR_FREQ_MHZ {533.333374} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.176} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.159} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.162} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.187} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {-0.073} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {-0.034} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.03} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.082} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_FREQ_MHZ {525} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_PARTNO {MT41K128M16 JT-125} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_TRAIN_DATA_EYE {1} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_TRAIN_READ_GATE {1} \
|
||||||
|
CONFIG.PCW_UIPARAM_DDR_TRAIN_WRITE_LEVEL {1} \
|
||||||
|
CONFIG.PCW_USB0_PERIPHERAL_ENABLE {1} \
|
||||||
|
CONFIG.PCW_USB0_RESET_ENABLE {1} \
|
||||||
|
CONFIG.PCW_USB0_RESET_IO {MIO 46} \
|
||||||
|
CONFIG.PCW_USB0_USB0_IO {MIO 28 .. 39} \
|
||||||
|
CONFIG.PCW_USB_RESET_ENABLE {1} \
|
||||||
|
CONFIG.PCW_USB_RESET_SELECT {Share reset pin} \
|
||||||
|
CONFIG.PCW_USE_FABRIC_INTERRUPT {1} \
|
||||||
|
] $processing_system7_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: axi_uartlite_0, and set properties
|
||||||
|
set axi_uartlite_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_uartlite:2.0 axi_uartlite_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.C_BAUDRATE {115200} \
|
||||||
|
CONFIG.UARTLITE_BOARD_INTERFACE {Custom} \
|
||||||
|
CONFIG.USE_BOARD_FLOW {true} \
|
||||||
|
] $axi_uartlite_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: ps7_0_axi_periph, and set properties
|
||||||
|
set ps7_0_axi_periph [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 ps7_0_axi_periph ]
|
||||||
|
set_property CONFIG.NUM_MI {2} $ps7_0_axi_periph
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: rst_ps7_0_100M, and set properties
|
||||||
|
set rst_ps7_0_100M [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 rst_ps7_0_100M ]
|
||||||
|
|
||||||
|
# Create instance: axi_uart16550_0, and set properties
|
||||||
|
set axi_uart16550_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_uart16550:2.0 axi_uart16550_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.UART_BOARD_INTERFACE {Custom} \
|
||||||
|
CONFIG.USE_BOARD_FLOW {true} \
|
||||||
|
] $axi_uart16550_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: IRQ_F2P, and set properties
|
||||||
|
set IRQ_F2P [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 IRQ_F2P ]
|
||||||
|
|
||||||
|
# Create instance: LEDS, and set properties
|
||||||
|
set LEDS [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 LEDS ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.DIN_FROM {7} \
|
||||||
|
CONFIG.DIN_WIDTH {16} \
|
||||||
|
] $LEDS
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: EMIO_O_0, and set properties
|
||||||
|
set EMIO_O_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 EMIO_O_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.DIN_FROM {15} \
|
||||||
|
CONFIG.DIN_WIDTH {64} \
|
||||||
|
] $EMIO_O_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: EMIO_O_1, and set properties
|
||||||
|
set EMIO_O_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 EMIO_O_1 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.DIN_FROM {47} \
|
||||||
|
CONFIG.DIN_TO {32} \
|
||||||
|
CONFIG.DIN_WIDTH {64} \
|
||||||
|
] $EMIO_O_1
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: EMIO_I, and set properties
|
||||||
|
set EMIO_I [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 EMIO_I ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.IN0_WIDTH {16} \
|
||||||
|
CONFIG.IN1_WIDTH {16} \
|
||||||
|
CONFIG.IN2_WIDTH {16} \
|
||||||
|
CONFIG.IN3_WIDTH {16} \
|
||||||
|
CONFIG.NUM_PORTS {4} \
|
||||||
|
] $EMIO_I
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: EMIO_I_0, and set properties
|
||||||
|
set EMIO_I_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 EMIO_I_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.IN0_WIDTH {8} \
|
||||||
|
CONFIG.IN1_WIDTH {5} \
|
||||||
|
CONFIG.IN2_WIDTH {3} \
|
||||||
|
CONFIG.NUM_PORTS {3} \
|
||||||
|
] $EMIO_I_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: xlconstant_0, and set properties
|
||||||
|
set xlconstant_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_0 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.CONST_VAL {0} \
|
||||||
|
CONFIG.CONST_WIDTH {3} \
|
||||||
|
] $xlconstant_0
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: EMIO_I_1, and set properties
|
||||||
|
set EMIO_I_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 EMIO_I_1 ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.CONST_VAL {0} \
|
||||||
|
CONFIG.CONST_WIDTH {16} \
|
||||||
|
] $EMIO_I_1
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: uart_mux_0, and set properties
|
||||||
|
set block_name uart_mux
|
||||||
|
set block_cell_name uart_mux_0
|
||||||
|
if { [catch {set uart_mux_0 [create_bd_cell -type module -reference $block_name $block_cell_name] } errmsg] } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2095 -severity "ERROR" "Unable to add referenced block <$block_name>. Please add the files for ${block_name}'s definition into the project."}
|
||||||
|
return 1
|
||||||
|
} elseif { $uart_mux_0 eq "" } {
|
||||||
|
catch {common::send_gid_msg -ssname BD::TCL -id 2096 -severity "ERROR" "Unable to referenced block <$block_name>. Please add the files for ${block_name}'s definition into the project."}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create instance: UART_MUX, and set properties
|
||||||
|
set UART_MUX [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 UART_MUX ]
|
||||||
|
set_property -dict [list \
|
||||||
|
CONFIG.DIN_FROM {10} \
|
||||||
|
CONFIG.DIN_TO {8} \
|
||||||
|
CONFIG.DIN_WIDTH {16} \
|
||||||
|
] $UART_MUX
|
||||||
|
|
||||||
|
|
||||||
|
# Create instance: xlconstant_1, and set properties
|
||||||
|
set xlconstant_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_1 ]
|
||||||
|
|
||||||
|
# Create interface connections
|
||||||
|
connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports DDR] [get_bd_intf_pins processing_system7_0/DDR]
|
||||||
|
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports FIXED_IO] [get_bd_intf_pins processing_system7_0/FIXED_IO]
|
||||||
|
connect_bd_intf_net -intf_net processing_system7_0_M_AXI_GP0 [get_bd_intf_pins processing_system7_0/M_AXI_GP0] [get_bd_intf_pins ps7_0_axi_periph/S00_AXI]
|
||||||
|
connect_bd_intf_net -intf_net ps7_0_axi_periph_M00_AXI [get_bd_intf_pins ps7_0_axi_periph/M00_AXI] [get_bd_intf_pins axi_uartlite_0/S_AXI]
|
||||||
|
connect_bd_intf_net -intf_net ps7_0_axi_periph_M01_AXI [get_bd_intf_pins ps7_0_axi_periph/M01_AXI] [get_bd_intf_pins axi_uart16550_0/S_AXI]
|
||||||
|
|
||||||
|
# Create port connections
|
||||||
|
connect_bd_net -net EMIO_O_1_Dout [get_bd_pins EMIO_O_1/Dout] [get_bd_pins EMIO_I/In2]
|
||||||
|
connect_bd_net -net In0_0_1 [get_bd_ports SWITCHES] [get_bd_pins EMIO_I_0/In0]
|
||||||
|
connect_bd_net -net In1_0_1 [get_bd_ports BTTNS] [get_bd_pins EMIO_I_0/In1]
|
||||||
|
connect_bd_net -net axi_uart16550_0_ip2intc_irpt [get_bd_pins axi_uart16550_0/ip2intc_irpt] [get_bd_pins IRQ_F2P/In1]
|
||||||
|
connect_bd_net -net axi_uart16550_0_sout [get_bd_pins axi_uart16550_0/sout] [get_bd_pins uart_mux_0/uart_2_tx]
|
||||||
|
connect_bd_net -net axi_uartlite_0_interrupt [get_bd_pins axi_uartlite_0/interrupt] [get_bd_pins IRQ_F2P/In0]
|
||||||
|
connect_bd_net -net axi_uartlite_0_tx [get_bd_pins axi_uartlite_0/tx] [get_bd_pins uart_mux_0/uart_1_tx]
|
||||||
|
connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins processing_system7_0/FCLK_CLK0] [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] [get_bd_pins ps7_0_axi_periph/S00_ACLK] [get_bd_pins rst_ps7_0_100M/slowest_sync_clk] [get_bd_pins axi_uartlite_0/s_axi_aclk] [get_bd_pins ps7_0_axi_periph/M00_ACLK] [get_bd_pins ps7_0_axi_periph/ACLK] [get_bd_pins axi_uart16550_0/s_axi_aclk] [get_bd_pins ps7_0_axi_periph/M01_ACLK] [get_bd_pins uart_mux_0/sys_clk]
|
||||||
|
connect_bd_net -net processing_system7_0_FCLK_RESET0_N [get_bd_pins processing_system7_0/FCLK_RESET0_N] [get_bd_pins rst_ps7_0_100M/ext_reset_in]
|
||||||
|
connect_bd_net -net processing_system7_0_GPIO_O [get_bd_pins processing_system7_0/GPIO_O] [get_bd_pins EMIO_O_0/Din] [get_bd_pins EMIO_O_1/Din]
|
||||||
|
connect_bd_net -net processing_system7_0_TTC0_WAVE0_OUT [get_bd_pins processing_system7_0/TTC0_WAVE0_OUT] [get_bd_ports TTC0_WAVEOUT]
|
||||||
|
connect_bd_net -net processing_system7_0_UART0_TX [get_bd_pins processing_system7_0/UART0_TX] [get_bd_pins uart_mux_0/uart_0_tx]
|
||||||
|
connect_bd_net -net rst_ps7_0_100M_peripheral_aresetn [get_bd_pins rst_ps7_0_100M/peripheral_aresetn] [get_bd_pins ps7_0_axi_periph/S00_ARESETN] [get_bd_pins axi_uartlite_0/s_axi_aresetn] [get_bd_pins ps7_0_axi_periph/M00_ARESETN] [get_bd_pins ps7_0_axi_periph/ARESETN] [get_bd_pins axi_uart16550_0/s_axi_aresetn] [get_bd_pins ps7_0_axi_periph/M01_ARESETN]
|
||||||
|
connect_bd_net -net rx_in_0_1 [get_bd_ports UART_rxd] [get_bd_pins uart_mux_0/rx_in]
|
||||||
|
connect_bd_net -net uart_mux_0_tx_out [get_bd_pins uart_mux_0/tx_out] [get_bd_ports UART_txd]
|
||||||
|
connect_bd_net -net uart_mux_0_uart_0_rx [get_bd_pins uart_mux_0/uart_0_rx] [get_bd_pins processing_system7_0/UART0_RX]
|
||||||
|
connect_bd_net -net uart_mux_0_uart_1_rx [get_bd_pins uart_mux_0/uart_1_rx] [get_bd_pins axi_uartlite_0/rx]
|
||||||
|
connect_bd_net -net uart_mux_0_uart_2_rx [get_bd_pins uart_mux_0/uart_2_rx] [get_bd_pins axi_uart16550_0/sin]
|
||||||
|
connect_bd_net -net xlconcat_0_dout [get_bd_pins IRQ_F2P/dout] [get_bd_pins processing_system7_0/IRQ_F2P]
|
||||||
|
connect_bd_net -net xlconcat_1_dout [get_bd_pins EMIO_I/dout] [get_bd_pins processing_system7_0/GPIO_I]
|
||||||
|
connect_bd_net -net xlconcat_1_dout1 [get_bd_pins EMIO_I_0/dout] [get_bd_pins EMIO_I/In1]
|
||||||
|
connect_bd_net -net xlconstant_0_dout [get_bd_pins xlconstant_0/dout] [get_bd_pins EMIO_I_0/In2]
|
||||||
|
connect_bd_net -net xlconstant_1_dout [get_bd_pins EMIO_I_1/dout] [get_bd_pins EMIO_I/In3]
|
||||||
|
connect_bd_net -net xlconstant_1_dout1 [get_bd_pins xlconstant_1/dout] [get_bd_pins axi_uart16550_0/rin] [get_bd_pins axi_uart16550_0/dsrn] [get_bd_pins axi_uart16550_0/ctsn] [get_bd_pins axi_uart16550_0/dcdn]
|
||||||
|
connect_bd_net -net xlslice_0_Dout [get_bd_pins LEDS/Dout] [get_bd_ports LEDS]
|
||||||
|
connect_bd_net -net xlslice_0_Dout1 [get_bd_pins UART_MUX/Dout] [get_bd_pins uart_mux_0/sel]
|
||||||
|
connect_bd_net -net xlslice_1_Dout [get_bd_pins EMIO_O_0/Dout] [get_bd_pins LEDS/Din] [get_bd_pins EMIO_I/In0] [get_bd_pins UART_MUX/Din]
|
||||||
|
|
||||||
|
# Create address segments
|
||||||
|
assign_bd_address -offset 0x43C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uart16550_0/S_AXI/Reg] -force
|
||||||
|
assign_bd_address -offset 0x42C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uartlite_0/S_AXI/Reg] -force
|
||||||
|
|
||||||
|
|
||||||
|
# Restore current instance
|
||||||
|
current_bd_instance $oldCurInst
|
||||||
|
|
||||||
|
validate_bd_design
|
||||||
|
save_bd_design
|
||||||
|
}
|
||||||
|
# End of create_root_design()
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# MAIN FLOW
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
create_root_design ""
|
||||||
|
|
||||||
|
|
75
zedboard-fpga-design/src/zedboard.xdc
Normal file
75
zedboard-fpga-design/src/zedboard.xdc
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Zedboard LD0
|
||||||
|
set_property PACKAGE_PIN T22 [get_ports {LEDS[0]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[0]}]
|
||||||
|
# Zedboard LD1
|
||||||
|
set_property PACKAGE_PIN T21 [get_ports {LEDS[1]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[1]}]
|
||||||
|
# Zedboard LD2
|
||||||
|
set_property PACKAGE_PIN U22 [get_ports {LEDS[2]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[2]}]
|
||||||
|
# Zedboard LD3
|
||||||
|
set_property PACKAGE_PIN U21 [get_ports {LEDS[3]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[3]}]
|
||||||
|
# Zedboard LD4
|
||||||
|
set_property PACKAGE_PIN V22 [get_ports {LEDS[4]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[4]}]
|
||||||
|
# Zedboard LD5
|
||||||
|
set_property PACKAGE_PIN W22 [get_ports {LEDS[5]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[5]}]
|
||||||
|
# Zedboard LD6
|
||||||
|
set_property PACKAGE_PIN U19 [get_ports {LEDS[6]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[6]}]
|
||||||
|
# Zedboard LD7
|
||||||
|
set_property PACKAGE_PIN U14 [get_ports {LEDS[7]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {LEDS[7]}]
|
||||||
|
|
||||||
|
# Zedboard SW0
|
||||||
|
set_property PACKAGE_PIN F22 [get_ports {SWITCHES[0]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[0]}]
|
||||||
|
# Zedboard SW1
|
||||||
|
set_property PACKAGE_PIN G22 [get_ports {SWITCHES[1]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[1]}]
|
||||||
|
# Zedboard SW2
|
||||||
|
set_property PACKAGE_PIN H22 [get_ports {SWITCHES[2]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[2]}]
|
||||||
|
# Zedboard SW3
|
||||||
|
set_property PACKAGE_PIN F21 [get_ports {SWITCHES[3]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[3]}]
|
||||||
|
# Zedboard SW4
|
||||||
|
set_property PACKAGE_PIN H19 [get_ports {SWITCHES[4]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[4]}]
|
||||||
|
# Zedboard SW5
|
||||||
|
set_property PACKAGE_PIN H18 [get_ports {SWITCHES[5]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[5]}]
|
||||||
|
# Zedboard SW6
|
||||||
|
set_property PACKAGE_PIN H17 [get_ports {SWITCHES[6]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[6]}]
|
||||||
|
# Zedboard SW7
|
||||||
|
set_property PACKAGE_PIN M15 [get_ports {SWITCHES[7]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {SWITCHES[7]}]
|
||||||
|
|
||||||
|
# Zedboard BTNC
|
||||||
|
set_property PACKAGE_PIN P16 [get_ports {BTTNS[0]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {BTTNS[0]}]
|
||||||
|
# Zedboard BTND
|
||||||
|
set_property PACKAGE_PIN R16 [get_ports {BTTNS[1]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {BTTNS[1]}]
|
||||||
|
# Zedboard BTNL
|
||||||
|
set_property PACKAGE_PIN N15 [get_ports {BTTNS[2]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {BTTNS[2]}]
|
||||||
|
# Zedboard BTNR
|
||||||
|
set_property PACKAGE_PIN R18 [get_ports {BTTNS[3]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {BTTNS[3]}]
|
||||||
|
# Zedboard BTNU
|
||||||
|
set_property PACKAGE_PIN T18 [get_ports {BTTNS[4]}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {BTTNS[4]}]
|
||||||
|
|
||||||
|
# UART
|
||||||
|
set_property PACKAGE_PIN Y11 [get_ports UART_rxd]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports UART_rxd]
|
||||||
|
set_property PACKAGE_PIN AA11 [get_ports UART_txd]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports UART_txd]
|
||||||
|
|
||||||
|
# TTC0 Wave Out
|
||||||
|
set_property PACKAGE_PIN W12 [get_ports {TTC0_WAVEOUT}]
|
||||||
|
set_property IOSTANDARD LVCMOS33 [get_ports {TTC0_WAVEOUT}]
|
528
zedboard-fpga-design/zedboard-rust.tcl
Normal file
528
zedboard-fpga-design/zedboard-rust.tcl
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
#*****************************************************************************************
|
||||||
|
# Vivado (TM) v2024.1 (64-bit)
|
||||||
|
#
|
||||||
|
# zedboard-rust.tcl: Tcl script for re-creating project 'zedboard-rust'
|
||||||
|
#
|
||||||
|
# Generated by Vivado on Fri Mar 14 13:10:19 CET 2025
|
||||||
|
# IP Build 5075265 on Wed May 22 21:45:21 MDT 2024
|
||||||
|
#
|
||||||
|
# This file contains the Vivado Tcl commands for re-creating the project to the state*
|
||||||
|
# when this script was generated. In order to re-create the project, please source this
|
||||||
|
# file in the Vivado Tcl Shell.
|
||||||
|
#
|
||||||
|
# * Note that the runs in the created project will be configured the same way as the
|
||||||
|
# original project, however they will not be launched automatically. To regenerate the
|
||||||
|
# run results please launch the synthesis/implementation runs as needed.
|
||||||
|
#
|
||||||
|
#*****************************************************************************************
|
||||||
|
# NOTE: In order to use this script for source control purposes, please make sure that the
|
||||||
|
# following files are added to the source control system:-
|
||||||
|
#
|
||||||
|
# 1. This project restoration tcl script (zedboard-rust.tcl) that was generated.
|
||||||
|
# 2. Constraints file in src directory
|
||||||
|
# 3. BD export file in src directory
|
||||||
|
#
|
||||||
|
#*****************************************************************************************
|
||||||
|
|
||||||
|
# Set the reference directory for source file relative paths (by default the value is script directory path)
|
||||||
|
set script_dir [file dirname [info script]]
|
||||||
|
set origin_dir "."
|
||||||
|
|
||||||
|
set constr_file [file normalize "$script_dir/src/zedboard.xdc"]
|
||||||
|
set bd_file [file normalize "$script_dir/src/zedboard-bd.tcl"]
|
||||||
|
|
||||||
|
set uart_mux_file [file normalize "$script_dir/src/uart_mux.vhd"]
|
||||||
|
|
||||||
|
# Check file required for this script exists
|
||||||
|
proc checkRequiredFiles { origin_dir} {
|
||||||
|
set status true
|
||||||
|
set files [list \
|
||||||
|
"[file normalize $constr_file]"\
|
||||||
|
"[file normalize $bd_file]"\
|
||||||
|
]
|
||||||
|
foreach ifile $files {
|
||||||
|
if { ![file isfile $ifile] } {
|
||||||
|
puts " Could not find local file $ifile "
|
||||||
|
set status false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use origin directory path location variable, if specified in the tcl shell
|
||||||
|
if { [info exists ::origin_dir_loc] } {
|
||||||
|
set origin_dir $::origin_dir_loc
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the project name
|
||||||
|
set _xil_proj_name_ "zedboard-rust"
|
||||||
|
|
||||||
|
# Use project name variable, if specified in the tcl shell
|
||||||
|
if { [info exists ::user_project_name] } {
|
||||||
|
set _xil_proj_name_ $::user_project_name
|
||||||
|
}
|
||||||
|
|
||||||
|
variable script_file
|
||||||
|
set script_file "zedboard-rust.tcl"
|
||||||
|
|
||||||
|
# Help information for this script
|
||||||
|
proc print_help {} {
|
||||||
|
variable script_file
|
||||||
|
puts "\nDescription:"
|
||||||
|
puts "Recreate a Vivado project from this script. The created project will be"
|
||||||
|
puts "functionally equivalent to the original project for which this script was"
|
||||||
|
puts "generated. The script contains commands for creating a project, filesets,"
|
||||||
|
puts "runs, adding/importing sources and setting properties on various objects.\n"
|
||||||
|
puts "Syntax:"
|
||||||
|
puts "$script_file"
|
||||||
|
puts "$script_file -tclargs \[--origin_dir <path>\]"
|
||||||
|
puts "$script_file -tclargs \[--project_name <name>\]"
|
||||||
|
puts "$script_file -tclargs \[--help\]\n"
|
||||||
|
puts "$script_file -tclargs \[--overwrite\]\n"
|
||||||
|
puts "Usage:"
|
||||||
|
puts "Name Description"
|
||||||
|
puts "-------------------------------------------------------------------------"
|
||||||
|
puts "\[--origin_dir <path>\] Determine source file paths wrt this path. Default"
|
||||||
|
puts " origin_dir path value is \".\", otherwise, the value"
|
||||||
|
puts " that was set with the \"-paths_relative_to\" switch"
|
||||||
|
puts " when this script was generated.\n"
|
||||||
|
puts "\[--project_name <name>\] Create project with the specified name. Default"
|
||||||
|
puts " name is the name of the project from where this"
|
||||||
|
puts " script was generated.\n"
|
||||||
|
puts "\[--help\] Print help information for this script"
|
||||||
|
puts "\[--overwrite\] Ovewrite the existing project if it exists"
|
||||||
|
puts "-------------------------------------------------------------------------\n"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set _force ""
|
||||||
|
|
||||||
|
if { $::argc > 0 } {
|
||||||
|
for {set i 0} {$i < $::argc} {incr i} {
|
||||||
|
set option [string trim [lindex $::argv $i]]
|
||||||
|
switch -regexp -- $option {
|
||||||
|
"--origin_dir" { incr i; set origin_dir [lindex $::argv $i] }
|
||||||
|
"--project_name" { incr i; set _xil_proj_name_ [lindex $::argv $i] }
|
||||||
|
"--overwrite" { incr i; set _force "-force"}
|
||||||
|
"--help" { print_help }
|
||||||
|
default {
|
||||||
|
if { [regexp {^-} $option] } {
|
||||||
|
puts "ERROR: Unknown option '$option' specified, please type '$script_file -tclargs --help' for usage info.\n"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create project
|
||||||
|
if { $_force ne "" } {
|
||||||
|
create_project ${_xil_proj_name_} ${origin_dir}/${_xil_proj_name_} -part xc7z020clg484-1 $_force
|
||||||
|
} else {
|
||||||
|
create_project ${_xil_proj_name_} ${origin_dir}/${_xil_proj_name_} -part xc7z020clg484-1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the directory path for the new project
|
||||||
|
set proj_dir [get_property directory [current_project]]
|
||||||
|
|
||||||
|
# Reconstruct message rules
|
||||||
|
# None
|
||||||
|
|
||||||
|
# Set project properties
|
||||||
|
set obj [current_project]
|
||||||
|
set_property -name "board_part" -value "digilentinc.com:zedboard:part0:1.1" -objects $obj
|
||||||
|
set_property -name "default_lib" -value "xil_defaultlib" -objects $obj
|
||||||
|
set_property -name "enable_resource_estimation" -value "0" -objects $obj
|
||||||
|
set_property -name "enable_vhdl_2008" -value "1" -objects $obj
|
||||||
|
set_property -name "ip_cache_permissions" -value "read write" -objects $obj
|
||||||
|
set_property -name "ip_output_repo" -value "$proj_dir/${_xil_proj_name_}.cache/ip" -objects $obj
|
||||||
|
set_property -name "mem.enable_memory_map_generation" -value "1" -objects $obj
|
||||||
|
set_property -name "platform.board_id" -value "zedboard" -objects $obj
|
||||||
|
set_property -name "revised_directory_structure" -value "1" -objects $obj
|
||||||
|
set_property -name "sim.central_dir" -value "$proj_dir/${_xil_proj_name_}.ip_user_files" -objects $obj
|
||||||
|
set_property -name "sim.ip.auto_export_scripts" -value "1" -objects $obj
|
||||||
|
set_property -name "simulator_language" -value "Mixed" -objects $obj
|
||||||
|
set_property -name "sim_compile_state" -value "1" -objects $obj
|
||||||
|
|
||||||
|
# Create 'sources_1' fileset (if not found)
|
||||||
|
if {[string equal [get_filesets -quiet sources_1] ""]} {
|
||||||
|
create_fileset -srcset sources_1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set 'sources_1' fileset object
|
||||||
|
set obj [get_filesets sources_1]
|
||||||
|
set files [list \
|
||||||
|
$uart_mux_file \
|
||||||
|
]
|
||||||
|
add_files -norecurse -fileset $obj $files
|
||||||
|
|
||||||
|
set_property file_type "VHDL" [get_files $uart_mux_file]
|
||||||
|
|
||||||
|
# Set 'sources_1' fileset file properties for remote files
|
||||||
|
# None
|
||||||
|
|
||||||
|
# Set 'sources_1' fileset file properties for local files
|
||||||
|
|
||||||
|
# Create 'constrs_1' fileset (if not found)
|
||||||
|
if {[string equal [get_filesets -quiet constrs_1] ""]} {
|
||||||
|
create_fileset -constrset constrs_1
|
||||||
|
}
|
||||||
|
add_files -fileset constrs_1 -norecurse $constr_file
|
||||||
|
|
||||||
|
# Retrieve the file object correctly
|
||||||
|
set file_obj [get_files -of_objects [get_filesets constrs_1]]
|
||||||
|
# Set the file type property
|
||||||
|
set_property -name "file_type" -value "XDC" -objects $file_obj
|
||||||
|
# Set as target constraints file.
|
||||||
|
# set_property -name "target_constrs_file" -value $constr_file -objects $file_obj
|
||||||
|
set_property target_constrs_file $constr_file [current_fileset -constrset]
|
||||||
|
set_property target_ucf $constr_file [current_fileset -constrset]
|
||||||
|
|
||||||
|
# Load block design.
|
||||||
|
set ret [source $bd_file]
|
||||||
|
if {$ret != ""} {
|
||||||
|
error "Failed to generate block design with return value $ret"
|
||||||
|
}
|
||||||
|
set design_name [get_bd_designs]
|
||||||
|
make_wrapper -files [get_files $design_name.bd] -top -import
|
||||||
|
|
||||||
|
# Create 'sim_1' fileset (if not found)
|
||||||
|
if {[string equal [get_filesets -quiet sim_1] ""]} {
|
||||||
|
create_fileset -simset sim_1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set 'sim_1' fileset object
|
||||||
|
set obj [get_filesets sim_1]
|
||||||
|
# Empty (no sources present)
|
||||||
|
|
||||||
|
# Set 'sim_1' fileset properties
|
||||||
|
set obj [get_filesets sim_1]
|
||||||
|
set_property -name "top" -value "zedboard_wrapper" -objects $obj
|
||||||
|
set_property -name "top_lib" -value "xil_defaultlib" -objects $obj
|
||||||
|
|
||||||
|
# Set 'utils_1' fileset object
|
||||||
|
set obj [get_filesets utils_1]
|
||||||
|
|
||||||
|
# Set 'utils_1' fileset file properties for remote files
|
||||||
|
# None
|
||||||
|
|
||||||
|
# Set 'utils_1' fileset file properties for local files
|
||||||
|
# None
|
||||||
|
|
||||||
|
# Set 'utils_1' fileset properties
|
||||||
|
set obj [get_filesets utils_1]
|
||||||
|
|
||||||
|
set idrFlowPropertiesConstraints ""
|
||||||
|
catch {
|
||||||
|
set idrFlowPropertiesConstraints [get_param runs.disableIDRFlowPropertyConstraints]
|
||||||
|
set_param runs.disableIDRFlowPropertyConstraints 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create 'synth_1' run (if not found)
|
||||||
|
if {[string equal [get_runs -quiet synth_1] ""]} {
|
||||||
|
create_run -name synth_1 -part xc7z020clg484-1 -flow {Vivado Synthesis 2024} -strategy "Vivado Synthesis Defaults" -report_strategy {No Reports} -constrset constrs_1
|
||||||
|
} else {
|
||||||
|
set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]
|
||||||
|
set_property flow "Vivado Synthesis 2024" [get_runs synth_1]
|
||||||
|
}
|
||||||
|
set obj [get_runs synth_1]
|
||||||
|
set_property set_report_strategy_name 1 $obj
|
||||||
|
set_property report_strategy {Vivado Synthesis Default Reports} $obj
|
||||||
|
set_property set_report_strategy_name 0 $obj
|
||||||
|
# Create 'synth_1_synth_report_utilization_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs synth_1] synth_1_synth_report_utilization_0] "" ] } {
|
||||||
|
create_report_config -report_name synth_1_synth_report_utilization_0 -report_type report_utilization:1.0 -steps synth_design -runs synth_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs synth_1] synth_1_synth_report_utilization_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
set obj [get_runs synth_1]
|
||||||
|
set_property -name "needs_refresh" -value "1" -objects $obj
|
||||||
|
set_property -name "auto_incremental_checkpoint" -value "1" -objects $obj
|
||||||
|
set_property -name "strategy" -value "Vivado Synthesis Defaults" -objects $obj
|
||||||
|
|
||||||
|
# set the current synth run
|
||||||
|
current_run -synthesis [get_runs synth_1]
|
||||||
|
|
||||||
|
# Create 'impl_1' run (if not found)
|
||||||
|
if {[string equal [get_runs -quiet impl_1] ""]} {
|
||||||
|
create_run -name impl_1 -part xc7z020clg484-1 -flow {Vivado Implementation 2024} -strategy "Vivado Implementation Defaults" -report_strategy {No Reports} -constrset constrs_1 -parent_run synth_1
|
||||||
|
} else {
|
||||||
|
set_property strategy "Vivado Implementation Defaults" [get_runs impl_1]
|
||||||
|
set_property flow "Vivado Implementation 2024" [get_runs impl_1]
|
||||||
|
}
|
||||||
|
set obj [get_runs impl_1]
|
||||||
|
set_property set_report_strategy_name 1 $obj
|
||||||
|
set_property report_strategy {Vivado Implementation Default Reports} $obj
|
||||||
|
set_property set_report_strategy_name 0 $obj
|
||||||
|
# Create 'impl_1_init_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_init_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_init_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps init_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_init_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_opt_report_drc_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_drc_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_opt_report_drc_0 -report_type report_drc:1.0 -steps opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_drc_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_opt_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_opt_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_power_opt_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_power_opt_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_power_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps power_opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_power_opt_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_io_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_io_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_io_0 -report_type report_io:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_io_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_utilization_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_utilization_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_utilization_0 -report_type report_utilization:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_utilization_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_control_sets_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_control_sets_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_control_sets_0 -report_type report_control_sets:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_control_sets_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "options.verbose" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_incremental_reuse_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_incremental_reuse_0 -report_type report_incremental_reuse:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_incremental_reuse_1' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_1] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_incremental_reuse_1 -report_type report_incremental_reuse:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_incremental_reuse_1]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_place_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_place_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps place_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_place_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_post_place_power_opt_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_place_power_opt_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_post_place_power_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps post_place_power_opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_place_power_opt_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_phys_opt_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_phys_opt_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_phys_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps phys_opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_phys_opt_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "is_enabled" -value "0" -objects $obj
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_drc_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_drc_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_drc_0 -report_type report_drc:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_drc_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_methodology_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_methodology_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_methodology_0 -report_type report_methodology:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_methodology_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_power_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_power_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_power_0 -report_type report_power:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_power_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_route_status_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_route_status_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_route_status_0 -report_type report_route_status:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_route_status_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_incremental_reuse_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_incremental_reuse_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_incremental_reuse_0 -report_type report_incremental_reuse:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_incremental_reuse_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_clock_utilization_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_clock_utilization_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_clock_utilization_0 -report_type report_clock_utilization:1.0 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_clock_utilization_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_route_report_bus_skew_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_bus_skew_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_route_report_bus_skew_0 -report_type report_bus_skew:1.1 -steps route_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_route_report_bus_skew_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_post_route_phys_opt_report_timing_summary_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_timing_summary_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_post_route_phys_opt_report_timing_summary_0 -report_type report_timing_summary:1.0 -steps post_route_phys_opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_timing_summary_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "options.max_paths" -value "10" -objects $obj
|
||||||
|
set_property -name "options.report_unconstrained" -value "1" -objects $obj
|
||||||
|
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
# Create 'impl_1_post_route_phys_opt_report_bus_skew_0' report (if not found)
|
||||||
|
if { [ string equal [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_bus_skew_0] "" ] } {
|
||||||
|
create_report_config -report_name impl_1_post_route_phys_opt_report_bus_skew_0 -report_type report_bus_skew:1.1 -steps post_route_phys_opt_design -runs impl_1
|
||||||
|
}
|
||||||
|
set obj [get_report_configs -of_objects [get_runs impl_1] impl_1_post_route_phys_opt_report_bus_skew_0]
|
||||||
|
if { $obj != "" } {
|
||||||
|
set_property -name "options.warn_on_violation" -value "1" -objects $obj
|
||||||
|
|
||||||
|
}
|
||||||
|
set obj [get_runs impl_1]
|
||||||
|
set_property -name "needs_refresh" -value "1" -objects $obj
|
||||||
|
set_property -name "strategy" -value "Vivado Implementation Defaults" -objects $obj
|
||||||
|
set_property -name "steps.write_bitstream.args.readback_file" -value "0" -objects $obj
|
||||||
|
set_property -name "steps.write_bitstream.args.verbose" -value "0" -objects $obj
|
||||||
|
|
||||||
|
# set the current impl run
|
||||||
|
current_run -implementation [get_runs impl_1]
|
||||||
|
catch {
|
||||||
|
if { $idrFlowPropertiesConstraints != {} } {
|
||||||
|
set_param runs.disableIDRFlowPropertyConstraints $idrFlowPropertiesConstraints
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
puts "INFO: Project created:${_xil_proj_name_}"
|
||||||
|
# Create 'drc_1' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "drc_1" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {drc_1} -type drc
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "drc_1" ] ]
|
||||||
|
set_property -name "reports" -value "impl_1#impl_1_route_report_drc_0" -objects $obj
|
||||||
|
|
||||||
|
# Create 'methodology_1' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "methodology_1" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {methodology_1} -type methodology
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "methodology_1" ] ]
|
||||||
|
set_property -name "reports" -value "impl_1#impl_1_route_report_methodology_0" -objects $obj
|
||||||
|
|
||||||
|
# Create 'power_1' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "power_1" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {power_1} -type power
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "power_1" ] ]
|
||||||
|
set_property -name "reports" -value "impl_1#impl_1_route_report_power_0" -objects $obj
|
||||||
|
|
||||||
|
# Create 'timing_1' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "timing_1" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {timing_1} -type timing
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "timing_1" ] ]
|
||||||
|
set_property -name "reports" -value "impl_1#impl_1_route_report_timing_summary_0" -objects $obj
|
||||||
|
|
||||||
|
# Create 'utilization_1' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "utilization_1" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {utilization_1} -type utilization
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "utilization_1" ] ]
|
||||||
|
set_property -name "reports" -value "synth_1#synth_1_synth_report_utilization_0" -objects $obj
|
||||||
|
set_property -name "run.step" -value "synth_design" -objects $obj
|
||||||
|
set_property -name "run.type" -value "synthesis" -objects $obj
|
||||||
|
|
||||||
|
# Create 'utilization_2' gadget (if not found)
|
||||||
|
if {[string equal [get_dashboard_gadgets [ list "utilization_2" ] ] ""]} {
|
||||||
|
create_dashboard_gadget -name {utilization_2} -type utilization
|
||||||
|
}
|
||||||
|
set obj [get_dashboard_gadgets [ list "utilization_2" ] ]
|
||||||
|
set_property -name "reports" -value "impl_1#impl_1_place_report_utilization_0" -objects $obj
|
||||||
|
|
||||||
|
move_dashboard_gadget -name {utilization_1} -row 0 -col 0
|
||||||
|
move_dashboard_gadget -name {power_1} -row 1 -col 0
|
||||||
|
move_dashboard_gadget -name {drc_1} -row 2 -col 0
|
||||||
|
move_dashboard_gadget -name {timing_1} -row 0 -col 1
|
||||||
|
move_dashboard_gadget -name {utilization_2} -row 1 -col 1
|
||||||
|
move_dashboard_gadget -name {methodology_1} -row 2 -col 1
|
19
zynq7000-embassy/Cargo.toml
Normal file
19
zynq7000-embassy/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "zynq7000-embassy"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "Embassy-rs support for the Zynq7000 family of SoCs"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "embassy", "amd", "zynq7000"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
critical-section = "1"
|
||||||
|
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||||
|
zynq7000-hal = { path = "../zynq7000-hal" }
|
||||||
|
|
||||||
|
embassy-time-driver = { path = "/home/rmueller/Rust/embassy/embassy-time-driver", version = "0.2" }
|
||||||
|
embassy-time-queue-utils = { path = "/home/rmueller/Rust/embassy/embassy-time-queue-utils", version = "0.1" }
|
201
zynq7000-embassy/LICENSE-APACHE
Normal file
201
zynq7000-embassy/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
21
zynq7000-embassy/LICENSE-MIT
Normal file
21
zynq7000-embassy/LICENSE-MIT
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Robin A. Mueller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
8
zynq7000-embassy/README.md
Normal file
8
zynq7000-embassy/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Embassy-rs support for the AMD Zynq7000 SoC family
|
||||||
|
|
||||||
|
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
|
||||||
|
AMD Zynq7000 SoC family. Currently, it contains the time driver to allow using embassy-rs. It
|
||||||
|
currently provides one driver using the global timer peripheral provided by the Zynq7000 PS for
|
||||||
|
this purpose.
|
||||||
|
|
||||||
|
The documentation contains more information on how to use this crate.
|
176
zynq7000-embassy/src/lib.rs
Normal file
176
zynq7000-embassy/src/lib.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#![no_std]
|
||||||
|
use core::cell::{Cell, RefCell};
|
||||||
|
|
||||||
|
use critical_section::{CriticalSection, Mutex};
|
||||||
|
use embassy_time_driver::{Driver, TICK_HZ, time_driver_impl};
|
||||||
|
use embassy_time_queue_utils::Queue;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
use zynq7000_hal::{clocks::ArmClocks, gtc::Gtc, time::Hertz};
|
||||||
|
|
||||||
|
static SCALE: OnceCell<u64> = OnceCell::new();
|
||||||
|
static CPU_3X2X_CLK: OnceCell<Hertz> = OnceCell::new();
|
||||||
|
|
||||||
|
struct AlarmState {
|
||||||
|
timestamp: Cell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
/// This is the initialization method for the embassy time driver.
|
||||||
|
///
|
||||||
|
/// It should be called ONCE at system initialization.
|
||||||
|
pub fn init(arm_clocks: &ArmClocks, gtc: Gtc) {
|
||||||
|
if SCALE.get().is_some() || CPU_3X2X_CLK.get().is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsafe { GTC_TIME_DRIVER.init(arm_clocks, gtc) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This interrupt handler should be called ONCE in the interrupt handler on global timer
|
||||||
|
/// interrupts.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Needs to be called once in the global timer interrupt.
|
||||||
|
pub unsafe fn on_interrupt() {
|
||||||
|
unsafe { GTC_TIME_DRIVER.on_interrupt() };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GtcTimerDriver {
|
||||||
|
gtc: Mutex<RefCell<Gtc>>,
|
||||||
|
// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<AlarmState>,
|
||||||
|
queue: Mutex<RefCell<Queue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GtcTimerDriver {
|
||||||
|
/// This is the initialization method for the embassy time driver.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This has to be called ONCE at system initialization.
|
||||||
|
pub unsafe fn init(&'static self, arm_clock: &ArmClocks, mut gtc: Gtc) {
|
||||||
|
CPU_3X2X_CLK.set(arm_clock.cpu_3x2x_clk()).unwrap();
|
||||||
|
SCALE
|
||||||
|
.set(arm_clock.cpu_3x2x_clk().raw() as u64 / TICK_HZ)
|
||||||
|
.unwrap();
|
||||||
|
gtc.set_cpu_3x2x_clock(arm_clock.cpu_3x2x_clk());
|
||||||
|
gtc.set_prescaler(0);
|
||||||
|
gtc.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should be called inside the IRQ handler if the IRQ ID is equal to
|
||||||
|
/// [crate::hal::gic::PpiInterrupt::GlobalTimer].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function has to be called once for interrupt ID
|
||||||
|
/// [crate::hal::gic::PpiInterrupt::GlobalTimer].
|
||||||
|
pub unsafe fn on_interrupt(&self) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
self.trigger_alarm(cs);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut gtc = self.gtc.borrow(cs).borrow_mut();
|
||||||
|
let alarm = &self.alarms.borrow(cs);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
gtc.disable_interrupt();
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
|
||||||
|
// the interrupts are enabled or not. When they are enabled at a later point, the
|
||||||
|
// right value is already set.
|
||||||
|
|
||||||
|
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
|
||||||
|
// is not missed.
|
||||||
|
//
|
||||||
|
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||||
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
|
// and we don't do that here.
|
||||||
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
let opt_comparator = safe_timestamp.checked_mul(*SCALE.get().unwrap());
|
||||||
|
if opt_comparator.is_none() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
gtc.set_comparator(opt_comparator.unwrap());
|
||||||
|
gtc.enable_interrupt();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||||
|
let mut gtc = self.gtc.borrow(cs).borrow_mut();
|
||||||
|
gtc.disable_interrupt();
|
||||||
|
drop(gtc);
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs);
|
||||||
|
// Setting the maximum value disables the alarm.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
let mut next = self
|
||||||
|
.queue
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = self
|
||||||
|
.queue
|
||||||
|
.borrow(cs)
|
||||||
|
.borrow_mut()
|
||||||
|
.next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Driver for GtcTimerDriver {
|
||||||
|
#[inline]
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// This is okay, we only read the GTC and do not re-configure it, avoids the
|
||||||
|
// need for a lock.
|
||||||
|
let gtc = unsafe { Gtc::steal_fixed(Some(*CPU_3X2X_CLK.get().unwrap())) };
|
||||||
|
|
||||||
|
gtc.read_timer() / SCALE.get().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||||
|
|
||||||
|
if queue.schedule_wake(at, waker) {
|
||||||
|
let mut next = queue.next_expiration(self.now());
|
||||||
|
while !self.set_alarm(cs, next) {
|
||||||
|
next = queue.next_expiration(self.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_driver_impl!(
|
||||||
|
// We assume ownership of the GTC, so it is okay to steal here.
|
||||||
|
static GTC_TIME_DRIVER: GtcTimerDriver = GtcTimerDriver {
|
||||||
|
gtc: Mutex::new(RefCell::new(unsafe { Gtc::steal_fixed(None)})),
|
||||||
|
alarms: Mutex::new(AlarmState::new()),
|
||||||
|
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||||
|
});
|
45
zynq7000-hal/Cargo.toml
Normal file
45
zynq7000-hal/Cargo.toml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
[package]
|
||||||
|
name = "zynq7000-hal"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "HAL for the Zynq7000 family of SoCs"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "hal", "amd", "zynq7000", "xilinx", "bare-metal"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", features = ["critical-section-single-core"] }
|
||||||
|
zynq7000 = { path = "../zynq7000" }
|
||||||
|
|
||||||
|
arbitrary-int = "1.3"
|
||||||
|
thiserror = { version = "2", default-features = false }
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
ringbuf = { version = "0.4.8", default-features = false }
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
heapless = "0.8"
|
||||||
|
static_cell = "2"
|
||||||
|
delegate = "0.13"
|
||||||
|
paste = "1"
|
||||||
|
nb = "1"
|
||||||
|
fugit = "0.3"
|
||||||
|
critical-section = "1"
|
||||||
|
libm = "0.2"
|
||||||
|
log = "0.4"
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
raw-slicee = "0.1"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = ["thiserror/std", "alloc"]
|
||||||
|
alloc = []
|
||||||
|
# These devices have a lower pin count.
|
||||||
|
7z010-7z007s-clg225 = []
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
approx = "0.5"
|
201
zynq7000-hal/LICENSE-APACHE
Normal file
201
zynq7000-hal/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
21
zynq7000-hal/LICENSE-MIT
Normal file
21
zynq7000-hal/LICENSE-MIT
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Robin A. Mueller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
12
zynq7000-hal/README.md
Normal file
12
zynq7000-hal/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# HAL for the AMD Zynq 7000 SoC family
|
||||||
|
|
||||||
|
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
|
||||||
|
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000).
|
||||||
|
|
||||||
|
It is the result of reading the datasheet for the device and encoding a type-safe layer over the
|
||||||
|
raw PAC. This crate also implements traits specified by the
|
||||||
|
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||||
|
various drivers in the embedded rust ecosystem.
|
||||||
|
|
||||||
|
The [top-level README](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs) and the documentation
|
||||||
|
contain more information on how to use this crate.
|
466
zynq7000-hal/src/clocks.rs
Normal file
466
zynq7000-hal/src/clocks.rs
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
//! Clock module.
|
||||||
|
use arbitrary_int::Number;
|
||||||
|
|
||||||
|
use zynq7000::slcr::{
|
||||||
|
ClockControl,
|
||||||
|
clocks::{
|
||||||
|
ClockRatioSelect, DualCommonPeriphIoClkCtrl, FpgaClkControl, GigEthClkCtrl,
|
||||||
|
SingleCommonPeriphIoClkCtrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::time::Hertz;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ArmClocks {
|
||||||
|
ref_clk: Hertz,
|
||||||
|
cpu_1x_clk: Hertz,
|
||||||
|
cpu_2x_clk: Hertz,
|
||||||
|
cpu_3x2x_clk: Hertz,
|
||||||
|
cpu_6x4x_clk: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArmClocks {
|
||||||
|
/// Reference clock provided by ARM PLL which is used to calculate all other clock frequencies.
|
||||||
|
pub const fn ref_clk(&self) -> Hertz {
|
||||||
|
self.ref_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn cpu_1x_clk(&self) -> Hertz {
|
||||||
|
self.cpu_1x_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn cpu_2x_clk(&self) -> Hertz {
|
||||||
|
self.cpu_2x_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn cpu_3x2x_clk(&self) -> Hertz {
|
||||||
|
self.cpu_3x2x_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn cpu_6x4x_clk(&self) -> Hertz {
|
||||||
|
self.cpu_6x4x_clk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DdrClocks {
|
||||||
|
ref_clk: Hertz,
|
||||||
|
ddr_3x_clk: Hertz,
|
||||||
|
ddr_2x_clk: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DdrClocks {
|
||||||
|
/// Reference clock provided by DDR PLL which is used to calculate all other clock frequencies.
|
||||||
|
pub const fn ref_clk(&self) -> Hertz {
|
||||||
|
self.ref_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ddr_3x_clk(&self) -> Hertz {
|
||||||
|
self.ddr_3x_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ddr_2x_clk(&self) -> Hertz {
|
||||||
|
self.ddr_2x_clk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IoClocks {
|
||||||
|
/// Reference clock provided by IO PLL which is used to calculate all other clock frequencies.
|
||||||
|
ref_clk: Hertz,
|
||||||
|
smc_clk: Hertz,
|
||||||
|
qspi_clk: Hertz,
|
||||||
|
sdio_clk: Hertz,
|
||||||
|
uart_clk: Hertz,
|
||||||
|
spi_clk: Hertz,
|
||||||
|
can_clk: Hertz,
|
||||||
|
pcap_2x_clk: Hertz,
|
||||||
|
trace_clk: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoClocks {
|
||||||
|
pub const fn ref_clk(&self) -> Hertz {
|
||||||
|
self.ref_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn smc_clk(&self) -> Hertz {
|
||||||
|
self.smc_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_smc_clk(&mut self, clk: Hertz) {
|
||||||
|
self.smc_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn qspi_clk(&self) -> Hertz {
|
||||||
|
self.qspi_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_qspi_clk(&mut self, clk: Hertz) {
|
||||||
|
self.qspi_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn sdio_clk(&self) -> Hertz {
|
||||||
|
self.sdio_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_sdio_clk(&mut self, clk: Hertz) {
|
||||||
|
self.sdio_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn uart_clk(&self) -> Hertz {
|
||||||
|
self.uart_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_uart_clk(&mut self, clk: Hertz) {
|
||||||
|
self.uart_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn spi_clk(&self) -> Hertz {
|
||||||
|
self.spi_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_spi_clk(&mut self, clk: Hertz) {
|
||||||
|
self.spi_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_clk(&self) -> Hertz {
|
||||||
|
self.can_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_can_clk(&mut self, clk: Hertz) {
|
||||||
|
self.can_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pcap_2x_clk(&self) -> Hertz {
|
||||||
|
self.pcap_2x_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_pcap_2x_clk(&mut self, clk: Hertz) {
|
||||||
|
self.pcap_2x_clk = clk
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns [None] if the trace clock is configured to use the EMIO trace clock.
|
||||||
|
pub fn trace_clk(&self) -> Option<Hertz> {
|
||||||
|
self.trace_clk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Clocks {
|
||||||
|
ps_clk: Hertz,
|
||||||
|
arm_pll_out: Hertz,
|
||||||
|
io_pll_out: Hertz,
|
||||||
|
ddr_pll_out: Hertz,
|
||||||
|
arm: ArmClocks,
|
||||||
|
ddr: DdrClocks,
|
||||||
|
io: IoClocks,
|
||||||
|
pl: [Hertz; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ClockModuleId {
|
||||||
|
Ddr,
|
||||||
|
Arm,
|
||||||
|
Smc,
|
||||||
|
Qspi,
|
||||||
|
Sdio,
|
||||||
|
Uart,
|
||||||
|
Spi,
|
||||||
|
Pcap,
|
||||||
|
Can,
|
||||||
|
Fpga,
|
||||||
|
Trace,
|
||||||
|
Gem0,
|
||||||
|
Gem1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DivisorZero(pub ClockModuleId);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ClockReadError {
|
||||||
|
/// The feedback value for the PLL clock output calculation is zero.
|
||||||
|
PllFeedbackZero,
|
||||||
|
/// Detected a divisor of zero.
|
||||||
|
DivisorZero(DivisorZero),
|
||||||
|
/// Detected a divisor that is not even.
|
||||||
|
DivisorNotEven,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clocks {
|
||||||
|
/// Processing system clock, which is generally dependent on the board and the used crystal.
|
||||||
|
pub fn ps_clk(&self) -> Hertz {
|
||||||
|
self.ps_clk
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This generates the clock configuration by reading the SLCR clock registers.
|
||||||
|
///
|
||||||
|
/// It assumes that the clock already has been configured, for example by a first-stage
|
||||||
|
/// bootloader, or the PS7 initialization script.
|
||||||
|
pub fn new_from_regs(ps_clk_freq: Hertz) -> Result<Self, ClockReadError> {
|
||||||
|
let mut clk_regs = unsafe { ClockControl::new_mmio_fixed() };
|
||||||
|
|
||||||
|
let arm_pll_cfg = clk_regs.read_arm_pll();
|
||||||
|
let io_pll_cfg = clk_regs.read_io_pll();
|
||||||
|
let ddr_pll_cfg = clk_regs.read_ddr_pll();
|
||||||
|
|
||||||
|
if arm_pll_cfg.fdiv().as_u32() == 0
|
||||||
|
|| io_pll_cfg.fdiv().as_u32() == 0
|
||||||
|
|| ddr_pll_cfg.fdiv().as_u32() == 0
|
||||||
|
{
|
||||||
|
return Err(ClockReadError::PllFeedbackZero);
|
||||||
|
}
|
||||||
|
let arm_pll_out = ps_clk_freq * arm_pll_cfg.fdiv().into();
|
||||||
|
let io_pll_out = ps_clk_freq * io_pll_cfg.fdiv().into();
|
||||||
|
let ddr_pll_out = ps_clk_freq * ddr_pll_cfg.fdiv().into();
|
||||||
|
|
||||||
|
let arm_clk_ctrl = clk_regs.read_arm_clk_ctrl();
|
||||||
|
let arm_base_clk = match arm_clk_ctrl.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelArm::ArmPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelArm::ArmPllAlt => arm_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelArm::DdrPll => ddr_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelArm::IoPll => io_pll_out,
|
||||||
|
};
|
||||||
|
let clk_sel = clk_regs.read_clk_621_true();
|
||||||
|
if arm_clk_ctrl.divisor().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(ClockModuleId::Arm)));
|
||||||
|
}
|
||||||
|
let arm_clk_divided = arm_base_clk / arm_clk_ctrl.divisor().as_u32();
|
||||||
|
let arm_clks = match clk_sel.sel() {
|
||||||
|
ClockRatioSelect::FourToTwoToOne => ArmClocks {
|
||||||
|
ref_clk: arm_pll_out,
|
||||||
|
cpu_1x_clk: arm_clk_divided / 4,
|
||||||
|
cpu_2x_clk: arm_clk_divided / 2,
|
||||||
|
cpu_3x2x_clk: arm_clk_divided / 2,
|
||||||
|
cpu_6x4x_clk: arm_clk_divided,
|
||||||
|
},
|
||||||
|
ClockRatioSelect::SixToTwoToOne => ArmClocks {
|
||||||
|
ref_clk: arm_pll_out,
|
||||||
|
cpu_1x_clk: arm_clk_divided / 6,
|
||||||
|
cpu_2x_clk: arm_clk_divided / 3,
|
||||||
|
cpu_3x2x_clk: arm_clk_divided / 2,
|
||||||
|
cpu_6x4x_clk: arm_clk_divided,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let ddr_clk_ctrl = clk_regs.read_ddr_clk_ctrl();
|
||||||
|
if ddr_clk_ctrl.div_3x_clk().as_u32() == 0 || ddr_clk_ctrl.div_2x_clk().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(ClockModuleId::Ddr)));
|
||||||
|
}
|
||||||
|
let ddr_clks = DdrClocks {
|
||||||
|
ref_clk: ddr_pll_out,
|
||||||
|
ddr_3x_clk: ddr_pll_out / ddr_clk_ctrl.div_3x_clk().as_u32(),
|
||||||
|
ddr_2x_clk: ddr_pll_out / ddr_clk_ctrl.div_2x_clk().as_u32(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle_common_single_clock_config = |single_block: SingleCommonPeriphIoClkCtrl,
|
||||||
|
id: ClockModuleId|
|
||||||
|
-> Result<Hertz, ClockReadError> {
|
||||||
|
if single_block.divisor().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(id)));
|
||||||
|
}
|
||||||
|
Ok(match single_block.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||||
|
io_pll_out / single_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
|
||||||
|
arm_pll_out / single_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
|
||||||
|
ddr_pll_out / single_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let handle_common_dual_clock_config = |dual_block: DualCommonPeriphIoClkCtrl,
|
||||||
|
id: ClockModuleId|
|
||||||
|
-> Result<Hertz, ClockReadError> {
|
||||||
|
if dual_block.divisor().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(id)));
|
||||||
|
}
|
||||||
|
Ok(match dual_block.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||||
|
io_pll_out / dual_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
|
||||||
|
arm_pll_out / dual_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
|
||||||
|
ddr_pll_out / dual_block.divisor().as_u32()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let smc_clk =
|
||||||
|
handle_common_single_clock_config(clk_regs.read_smc_clk_ctrl(), ClockModuleId::Smc)?;
|
||||||
|
let qspi_clk =
|
||||||
|
handle_common_single_clock_config(clk_regs.read_lqspi_clk_ctrl(), ClockModuleId::Qspi)?;
|
||||||
|
let sdio_clk =
|
||||||
|
handle_common_dual_clock_config(clk_regs.read_sdio_clk_ctrl(), ClockModuleId::Sdio)?;
|
||||||
|
let uart_clk =
|
||||||
|
handle_common_dual_clock_config(clk_regs.read_uart_clk_ctrl(), ClockModuleId::Uart)?;
|
||||||
|
let spi_clk =
|
||||||
|
handle_common_dual_clock_config(clk_regs.read_spi_clk_ctrl(), ClockModuleId::Spi)?;
|
||||||
|
let pcap_2x_clk =
|
||||||
|
handle_common_single_clock_config(clk_regs.read_pcap_clk_ctrl(), ClockModuleId::Pcap)?;
|
||||||
|
let can_clk_ctrl = clk_regs.read_can_clk_ctrl();
|
||||||
|
let can_clk_ref_clk = match can_clk_ctrl.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => io_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::ArmPll => arm_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::DdrPll => ddr_pll_out,
|
||||||
|
};
|
||||||
|
if can_clk_ctrl.divisor_0().as_u32() == 0 || can_clk_ctrl.divisor_1().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(ClockModuleId::Can)));
|
||||||
|
}
|
||||||
|
let can_clk =
|
||||||
|
can_clk_ref_clk / can_clk_ctrl.divisor_0().as_u32() / can_clk_ctrl.divisor_1().as_u32();
|
||||||
|
|
||||||
|
let trace_clk_ctrl = clk_regs.read_dbg_clk_ctrl();
|
||||||
|
if trace_clk_ctrl.divisor().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(
|
||||||
|
ClockModuleId::Trace,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let trace_clk = match trace_clk_ctrl.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelTpiu::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelTpiu::IoPllAlt => {
|
||||||
|
Some(io_pll_out / trace_clk_ctrl.divisor().as_u32())
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelTpiu::ArmPll => {
|
||||||
|
Some(arm_pll_out / trace_clk_ctrl.divisor().as_u32())
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelTpiu::DdrPll => {
|
||||||
|
Some(ddr_pll_out / trace_clk_ctrl.divisor().as_u32())
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelTpiu::EmioTraceClk
|
||||||
|
| zynq7000::slcr::clocks::SrcSelTpiu::EmioTraceClkAlt0
|
||||||
|
| zynq7000::slcr::clocks::SrcSelTpiu::EmioTraceClkAlt1
|
||||||
|
| zynq7000::slcr::clocks::SrcSelTpiu::EmioTraceClkAlt2 => None,
|
||||||
|
};
|
||||||
|
let calculate_fpga_clk = |fpga_clk_ctrl: FpgaClkControl| -> Result<Hertz, ClockReadError> {
|
||||||
|
if fpga_clk_ctrl.divisor_0().as_u32() == 0 || fpga_clk_ctrl.divisor_1().as_u32() == 0 {
|
||||||
|
return Err(ClockReadError::DivisorZero(DivisorZero(
|
||||||
|
ClockModuleId::Fpga,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(match fpga_clk_ctrl.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
|
||||||
|
io_pll_out
|
||||||
|
/ fpga_clk_ctrl.divisor_0().as_u32()
|
||||||
|
/ fpga_clk_ctrl.divisor_1().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
|
||||||
|
arm_pll_out
|
||||||
|
/ fpga_clk_ctrl.divisor_0().as_u32()
|
||||||
|
/ fpga_clk_ctrl.divisor_1().as_u32()
|
||||||
|
}
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
|
||||||
|
ddr_pll_out
|
||||||
|
/ fpga_clk_ctrl.divisor_0().as_u32()
|
||||||
|
/ fpga_clk_ctrl.divisor_1().as_u32()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
ps_clk: ps_clk_freq,
|
||||||
|
io_pll_out,
|
||||||
|
ddr_pll_out,
|
||||||
|
arm_pll_out,
|
||||||
|
arm: arm_clks,
|
||||||
|
ddr: ddr_clks,
|
||||||
|
io: IoClocks {
|
||||||
|
ref_clk: io_pll_out,
|
||||||
|
smc_clk,
|
||||||
|
qspi_clk,
|
||||||
|
sdio_clk,
|
||||||
|
uart_clk,
|
||||||
|
spi_clk,
|
||||||
|
can_clk,
|
||||||
|
pcap_2x_clk,
|
||||||
|
trace_clk,
|
||||||
|
},
|
||||||
|
// TODO: There should be a mut and a non-mut getter for an inner block. We only do pure
|
||||||
|
// reads with the inner block here.
|
||||||
|
pl: [
|
||||||
|
calculate_fpga_clk(clk_regs.fpga_0_clk_ctrl().read_clk_ctrl())?,
|
||||||
|
calculate_fpga_clk(clk_regs.fpga_1_clk_ctrl().read_clk_ctrl())?,
|
||||||
|
calculate_fpga_clk(clk_regs.fpga_2_clk_ctrl().read_clk_ctrl())?,
|
||||||
|
calculate_fpga_clk(clk_regs.fpga_3_clk_ctrl().read_clk_ctrl())?,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arm_clocks(&self) -> &ArmClocks {
|
||||||
|
&self.arm
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ddr_clocks(&self) -> &DdrClocks {
|
||||||
|
&self.ddr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn io_clocks(&self) -> &IoClocks {
|
||||||
|
&self.io
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn io_clocks_mut(&mut self) -> &mut IoClocks {
|
||||||
|
&mut self.io
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Programmable Logic (PL) FCLK clocks.
|
||||||
|
pub fn pl_clocks(&self) -> &[Hertz; 4] {
|
||||||
|
&self.pl
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_gem_ref_clock(
|
||||||
|
&self,
|
||||||
|
reg: GigEthClkCtrl,
|
||||||
|
module: ClockModuleId,
|
||||||
|
) -> Result<Hertz, DivisorZero> {
|
||||||
|
let source_clk = match reg.srcsel() {
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::IoPll
|
||||||
|
| zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => self.io_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::ArmPll => self.arm_pll_out,
|
||||||
|
zynq7000::slcr::clocks::SrcSelIo::DdrPll => self.ddr_pll_out,
|
||||||
|
};
|
||||||
|
let div0 = reg.divisor_0().as_u32();
|
||||||
|
if div0 == 0 {
|
||||||
|
return Err(DivisorZero(module));
|
||||||
|
}
|
||||||
|
let div1 = reg.divisor_1().as_u32();
|
||||||
|
if div1 == 0 {
|
||||||
|
return Err(DivisorZero(module));
|
||||||
|
}
|
||||||
|
Ok(source_clk / reg.divisor_0().as_u32() / reg.divisor_1().as_u32())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the reference clock for GEM0.
|
||||||
|
///
|
||||||
|
/// The divisor 1 of the GEM is 0 on reset. You have to properly initialize the clock
|
||||||
|
/// configuration before calling this function.
|
||||||
|
///
|
||||||
|
/// It should be noted that the GEM has a separate TX and RX clock.
|
||||||
|
/// The reference clock will only be the RX clock in loopback mode. For the TX block,
|
||||||
|
/// the reference clock is used if the EMIO enable bit `GEM{0,1}_CLK_CTRL[6]` is set to 0.
|
||||||
|
pub fn calculate_gem_0_ref_clock(&self) -> Result<Hertz, DivisorZero> {
|
||||||
|
let clk_regs = unsafe { ClockControl::new_mmio_fixed() };
|
||||||
|
self.calculate_gem_ref_clock(clk_regs.read_gem_0_clk_ctrl(), ClockModuleId::Gem0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the reference clock for GEM1.
|
||||||
|
///
|
||||||
|
/// The divisor 1 of the GEM is 0 on reset. You have to properly initialize the clock
|
||||||
|
/// configuration before calling this function.
|
||||||
|
///
|
||||||
|
/// It should be noted that the GEM has a separate TX and RX clock.
|
||||||
|
/// The reference clock will only be the RX clock in loopback mode. For the TX block,
|
||||||
|
/// the reference clock is used if the EMIO enable bit `GEM{0,1}_CLK_CTRL[6]` is set to 0.
|
||||||
|
pub fn calculate_gem_1_ref_clock(&self) -> Result<Hertz, DivisorZero> {
|
||||||
|
let clk_regs = unsafe { ClockControl::new_mmio_fixed() };
|
||||||
|
self.calculate_gem_ref_clock(clk_regs.read_gem_0_clk_ctrl(), ClockModuleId::Gem1)
|
||||||
|
}
|
||||||
|
}
|
528
zynq7000-hal/src/gic.rs
Normal file
528
zynq7000-hal/src/gic.rs
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
//! Global Interrupt Controller (GIC) module.
|
||||||
|
//!
|
||||||
|
//! The primary interface to configure and allow handling the interrupts are the
|
||||||
|
//! [GicConfigurator] and the [GicInterruptHelper] structures.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [GTC ticks](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs)
|
||||||
|
use arbitrary_int::Number;
|
||||||
|
|
||||||
|
use cortex_ar::interrupt;
|
||||||
|
use zynq7000::gic::{
|
||||||
|
Dcr, Gicc, Gicd, Icr, InterruptSignalRegister, MmioGicc, MmioGicd, PriorityRegister,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SPURIOUS_INTERRUPT_ID: u32 = 1023;
|
||||||
|
|
||||||
|
pub const HIGHEST_PRIORITY: u8 = 0;
|
||||||
|
pub const LOWEST_PRIORITY: u8 = 31;
|
||||||
|
|
||||||
|
/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
|
||||||
|
/// Configures #32 to #47.
|
||||||
|
pub const ICFR_2_FIXED_VALUE: u32 = 0b01010101010111010101010001011111;
|
||||||
|
/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
|
||||||
|
/// This configures `PL[2:0]` to high-level sensitivity.
|
||||||
|
/// Configures #48 to #63.
|
||||||
|
pub const ICFR_3_FIXED_VALUE: u32 = 0b01010101010101011101010101010101;
|
||||||
|
/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
|
||||||
|
/// This configures `PL[7:3]` to high-level sensitivity.
|
||||||
|
/// Configures #64 to #79.
|
||||||
|
pub const ICFR_4_FIXED_VALUE: u32 = 0b01110101010101010101010101010101;
|
||||||
|
/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
|
||||||
|
/// This configures `PL[15:8]` to high-level sensitivity.
|
||||||
|
/// Configures #80 to #95.
|
||||||
|
pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101;
|
||||||
|
|
||||||
|
/// Helper value to target all interrupts which can be targetted to CPU 0
|
||||||
|
pub const TARGETS_ALL_CPU_0_IPTR_VAL: u32 = 0x01010101;
|
||||||
|
/// Helper value to target all interrupts which can be targetted to CPU 1
|
||||||
|
pub const TARGETS_ALL_CPU_1_IPTR_VAL: u32 = 0x02020202;
|
||||||
|
|
||||||
|
pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF;
|
||||||
|
pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000;
|
||||||
|
|
||||||
|
pub enum SpiSensitivity {
|
||||||
|
Level = 0b01,
|
||||||
|
Edge = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TargetCpu {
|
||||||
|
None = 0b00,
|
||||||
|
Cpu0 = 0b01,
|
||||||
|
Cpu1 = 0b10,
|
||||||
|
Both = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private Peripheral Interrupt (PPI) which are private to the CPU.
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum PpiInterrupt {
|
||||||
|
GlobalTimer = 27,
|
||||||
|
// Interrupt signal from the PL. CPU0: `IRQF2P[18]` and CPU1: `IRQF2P[19]`
|
||||||
|
NFiq = 28,
|
||||||
|
CpuPrivateTimer = 29,
|
||||||
|
/// AWDT0 and AWDT1 for each CPU.
|
||||||
|
Awdt = 30,
|
||||||
|
// Interrupt signal from the PL. CPU0: `IRQF2P[16]` and CPU1: `IRQF2P[17]`
|
||||||
|
NIrq = 31,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shared Peripheral Interrupt IDs.
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum SpiInterrupt {
|
||||||
|
Cpu0 = 32,
|
||||||
|
Cpu1 = 33,
|
||||||
|
L2Cache = 34,
|
||||||
|
Ocm = 35,
|
||||||
|
_Reserved0 = 36,
|
||||||
|
Pmu0 = 37,
|
||||||
|
Pmu1 = 38,
|
||||||
|
Xadc = 39,
|
||||||
|
DevC = 40,
|
||||||
|
Swdt = 41,
|
||||||
|
Ttc00 = 42,
|
||||||
|
Ttc01 = 43,
|
||||||
|
Ttc02 = 44,
|
||||||
|
DmacAbort = 45,
|
||||||
|
Dmac0 = 46,
|
||||||
|
Dmac1 = 47,
|
||||||
|
Dmac2 = 48,
|
||||||
|
Dmac3 = 49,
|
||||||
|
Smc = 50,
|
||||||
|
Qspi = 51,
|
||||||
|
Gpio = 52,
|
||||||
|
Usb0 = 53,
|
||||||
|
Eth0 = 54,
|
||||||
|
Eth0Wakeup = 55,
|
||||||
|
Sdio0 = 56,
|
||||||
|
I2c0 = 57,
|
||||||
|
Spi0 = 58,
|
||||||
|
Uart0 = 59,
|
||||||
|
Can0 = 60,
|
||||||
|
Pl0 = 61,
|
||||||
|
Pl1 = 62,
|
||||||
|
Pl2 = 63,
|
||||||
|
Pl3 = 64,
|
||||||
|
Pl4 = 65,
|
||||||
|
Pl5 = 66,
|
||||||
|
Pl6 = 67,
|
||||||
|
Pl7 = 68,
|
||||||
|
Ttc10 = 69,
|
||||||
|
Ttc11 = 70,
|
||||||
|
Ttc12 = 71,
|
||||||
|
Dmac4 = 72,
|
||||||
|
Dmac5 = 73,
|
||||||
|
Dmac6 = 74,
|
||||||
|
Dmac7 = 75,
|
||||||
|
Usb1 = 76,
|
||||||
|
Eth1 = 77,
|
||||||
|
Eth1Wakeup = 78,
|
||||||
|
Sdio1 = 79,
|
||||||
|
I2c1 = 80,
|
||||||
|
Spi1 = 81,
|
||||||
|
Uart1 = 82,
|
||||||
|
Can1 = 83,
|
||||||
|
Pl8 = 84,
|
||||||
|
Pl9 = 85,
|
||||||
|
Pl10 = 86,
|
||||||
|
Pl11 = 87,
|
||||||
|
Pl12 = 88,
|
||||||
|
Pl13 = 89,
|
||||||
|
Pl14 = 90,
|
||||||
|
Pl15 = 91,
|
||||||
|
ScuParity = 92,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt ID wrapper.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Interrupt {
|
||||||
|
Sgi(usize),
|
||||||
|
Ppi(PpiInterrupt),
|
||||||
|
Spi(SpiInterrupt),
|
||||||
|
/// Detects an invalid interrupt ID.
|
||||||
|
Invalid(usize),
|
||||||
|
/// Spurious interrupt (ID# 1023).
|
||||||
|
Spurious,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InterruptInfo {
|
||||||
|
raw_reg: InterruptSignalRegister,
|
||||||
|
interrupt: Interrupt,
|
||||||
|
cpu_id: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptInfo {
|
||||||
|
pub fn raw_reg(&self) -> InterruptSignalRegister {
|
||||||
|
self.raw_reg
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_id(&self) -> u8 {
|
||||||
|
self.cpu_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt(&self) -> Interrupt {
|
||||||
|
self.interrupt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid priority value {0}, range is [0, 31]")]
|
||||||
|
pub struct InvalidPriorityValue(pub u8);
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid PL interrupt ID {0}")]
|
||||||
|
pub struct InvalidPlInterruptId(pub usize);
|
||||||
|
|
||||||
|
/// Invalid Shared Peripheral Interrupt (SPI) ID.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid SPI interrupt ID {0}")]
|
||||||
|
pub struct InvalidSpiInterruptId(pub usize);
|
||||||
|
|
||||||
|
/// Invalid Software Generated Interrupt (SGI) ID.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("Invalid SGI interrupt ID {0}")]
|
||||||
|
pub struct InvalidSgiInterruptId(pub usize);
|
||||||
|
|
||||||
|
/// Higher-level GIC controller for the Zynq70000 SoC.
|
||||||
|
///
|
||||||
|
/// The flow of using this controller is as follows:
|
||||||
|
///
|
||||||
|
/// 1. Create the controller using [Self::new_with_init]. You can use the [zynq7000::PsPeripherals]
|
||||||
|
/// structure or the [zynq7000::gic::Gicc::new_mmio] and [zynq7000::gic::Gicd::new_mmio]
|
||||||
|
/// functions to create the MMIO instances. The constructor configures all PL interrupts
|
||||||
|
/// sensivities to high-level sensitivity and configures all sensitivities which are expected
|
||||||
|
/// to have a certain value. It also sets the priority mask to 0xff by calling
|
||||||
|
/// [Self::set_priority_mask] to prevent masking of the interrupts.
|
||||||
|
/// 2. Perform the configuration of the interrupt targets and the interrupt sensitivities.
|
||||||
|
/// The CPU targets are encoded with [TargetCpu] while the sensitivities are encoded by
|
||||||
|
/// the [SpiSensitivity] enum. You can use the following (helper) API to configure the
|
||||||
|
/// interrupts:
|
||||||
|
///
|
||||||
|
/// - [Self::set_spi_interrupt_cpu_target]
|
||||||
|
/// - [Self::set_all_spi_interrupt_targets_cpu0]
|
||||||
|
/// - [Self::set_pl_interrupt_sensitivity]
|
||||||
|
///
|
||||||
|
/// 3. Enable all required interrupts. The following API can be used for this:
|
||||||
|
///
|
||||||
|
/// - [Self::enable_sgi_interrupt]
|
||||||
|
/// - [Self::enable_ppi_interrupt]
|
||||||
|
/// - [Self::enable_spi_interrupt]
|
||||||
|
/// - [Self::enable_all_spi_interrupts]
|
||||||
|
/// - [Self::enable_all_ppi_interrupts]
|
||||||
|
/// - [Self::enable_all_sgi_interrupts]
|
||||||
|
/// - [Self::enable_all_interrupts]
|
||||||
|
///
|
||||||
|
/// You might also chose to enable these interrupts at run-time after the GIC was started.
|
||||||
|
/// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or
|
||||||
|
/// with [Self::enable] which assumes a certain configuration.
|
||||||
|
/// 5. Enable interrupts for the Cortex-AR core by calling [Self::enable_interrupts].
|
||||||
|
///
|
||||||
|
/// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a
|
||||||
|
/// properly configured GIC.
|
||||||
|
pub struct GicConfigurator {
|
||||||
|
pub gicc: MmioGicc<'static>,
|
||||||
|
pub gicd: MmioGicd<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GicConfigurator {
|
||||||
|
/// Create a new GIC controller instance and calls [Self::initialize] to perform
|
||||||
|
/// strongly recommended initialization routines for the GIC.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_with_init(gicc: MmioGicc<'static>, gicd: MmioGicd<'static>) -> Self {
|
||||||
|
let mut gic = GicConfigurator { gicc, gicd };
|
||||||
|
gic.initialize();
|
||||||
|
gic
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new GIC controller instance without performing any initialization routines.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This creates the GIC without performing any of the initialization routines necessary
|
||||||
|
/// for proper operation. It also circumvents ownership checks. It is mainly intended to be
|
||||||
|
/// used inside the interrupt handler.
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn steal() -> Self {
|
||||||
|
GicConfigurator {
|
||||||
|
gicc: unsafe { Gicc::new_mmio_fixed() },
|
||||||
|
gicd: unsafe { Gicd::new_mmio_fixed() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the GIC by configuring the required sensitivites for the shared peripheral
|
||||||
|
/// interrupts.
|
||||||
|
///
|
||||||
|
/// With a few exeception, the GIC expects software to set up the sensitivities
|
||||||
|
/// to fixed values. The only exceptions are the interupts coming from the programmable
|
||||||
|
/// logic. These are configured to high level sensitivity by this function.
|
||||||
|
/// If you need a different sensitivity, you need to update the bits using the
|
||||||
|
/// [Self::set_pl_interrupt_sensitivity] function.
|
||||||
|
#[inline]
|
||||||
|
pub fn initialize(&mut self) {
|
||||||
|
self.gicd.write_icfr_2_spi(ICFR_2_FIXED_VALUE);
|
||||||
|
self.gicd.write_icfr_3_spi(ICFR_3_FIXED_VALUE);
|
||||||
|
self.gicd.write_icfr_4_spi(ICFR_4_FIXED_VALUE);
|
||||||
|
self.gicd.write_icfr_5_spi(ICFR_5_FIXED_VALUE);
|
||||||
|
self.set_priority_mask(0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the priority mask for the CPU.
|
||||||
|
///
|
||||||
|
/// Only interrupts with a higher priority than the mask will be accepted.
|
||||||
|
/// A lower numerical number means a higher priority. This means that the reset value 0x0
|
||||||
|
/// will mask all interrupts to the CPU while 0xff will unmask all interrupts.
|
||||||
|
///
|
||||||
|
/// Please note that the highest priority mask will NOT be necessarily the number of priority
|
||||||
|
/// levels: The IPRn register always sets the priority level number to the upper bits of the
|
||||||
|
/// 8-bit bitfield. See p.83 of the ARM GICv1 architecture specification.
|
||||||
|
pub fn set_priority_mask(&mut self, mask: u8) {
|
||||||
|
self.gicc
|
||||||
|
.write_pmr(PriorityRegister::new_with_raw_value(mask as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the sensitivity of a the Programmable Logic SPI interrupts.
|
||||||
|
///
|
||||||
|
/// These are the only interrupt IDs which are configurable for SPI. They are set
|
||||||
|
/// to high-level sensitivity by default by the [Self::initialize] function. You can
|
||||||
|
/// use this method to override certain sensitivies.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_pl_interrupt_sensitivity(
|
||||||
|
&mut self,
|
||||||
|
pl_int_id: usize,
|
||||||
|
sensitivity: SpiSensitivity,
|
||||||
|
) -> Result<(), InvalidPlInterruptId> {
|
||||||
|
if pl_int_id >= 16 {
|
||||||
|
return Err(InvalidPlInterruptId(pl_int_id));
|
||||||
|
}
|
||||||
|
match pl_int_id {
|
||||||
|
0..=2 => {
|
||||||
|
let pos = 26 + (pl_int_id * 2);
|
||||||
|
let mask = 0b11 << pos;
|
||||||
|
self.gicd
|
||||||
|
.modify_icfr_3_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
|
||||||
|
}
|
||||||
|
3..=7 => {
|
||||||
|
let pos = pl_int_id * 2;
|
||||||
|
let mask = 0b11 << pos;
|
||||||
|
self.gicd
|
||||||
|
.modify_icfr_4_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
|
||||||
|
}
|
||||||
|
8..=15 => {
|
||||||
|
let pos = 8 + (pl_int_id * 2);
|
||||||
|
let mask = 0b11 << pos;
|
||||||
|
self.gicd
|
||||||
|
.modify_icfr_5_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the CPU target for a SPI interrupt.
|
||||||
|
///
|
||||||
|
/// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all
|
||||||
|
/// interrupts with one core.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_spi_interrupt_cpu_target(&mut self, spi_int: SpiInterrupt, target: TargetCpu) {
|
||||||
|
let spi_int_raw = spi_int as u32;
|
||||||
|
let spi_offset_to_0 = spi_int_raw as usize - 32;
|
||||||
|
// Unwrap okay, calculated index is always valid.
|
||||||
|
self.gicd
|
||||||
|
.write_iptr_spi(
|
||||||
|
spi_offset_to_0 / 4,
|
||||||
|
(target as u32) << ((spi_offset_to_0 % 4) * 8),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility function to set all SGI interrupt targets to CPU0.
|
||||||
|
///
|
||||||
|
/// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in
|
||||||
|
/// the system.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_all_spi_interrupt_targets_cpu0(&mut self) {
|
||||||
|
for i in 0..0x10 {
|
||||||
|
self.gicd
|
||||||
|
.write_iptr_spi(i, TARGETS_ALL_CPU_0_IPTR_VAL)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> {
|
||||||
|
if int_id >= 16 {
|
||||||
|
return Err(InvalidSpiInterruptId(int_id));
|
||||||
|
}
|
||||||
|
unsafe { self.gicd.write_iser_unchecked(0, 1 << int_id) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_all_sgi_interrupts(&mut self) {
|
||||||
|
// Unwrap okay, index is valid.
|
||||||
|
self.gicd
|
||||||
|
.modify_iser(0, |mut v| {
|
||||||
|
v |= ACTIVATE_ALL_SGIS_MASK_ISER;
|
||||||
|
v
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) {
|
||||||
|
// Unwrap okay, index is valid.
|
||||||
|
self.gicd
|
||||||
|
.modify_iser(0, |mut v| {
|
||||||
|
v |= 1 << (ppi_int as u32);
|
||||||
|
v
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_all_ppi_interrupts(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.gicd.modify_iser_unchecked(0, |mut v| {
|
||||||
|
v |= ACTIVATE_ALL_PPIS_MASK_ISER;
|
||||||
|
v
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) {
|
||||||
|
let spi_int_raw = spi_int as u32;
|
||||||
|
match spi_int_raw {
|
||||||
|
32..=63 => {
|
||||||
|
let bit_pos = spi_int_raw - 32;
|
||||||
|
// Unwrap okay, valid index.
|
||||||
|
self.gicd.write_iser(1, 1 << bit_pos).unwrap();
|
||||||
|
}
|
||||||
|
64..=92 => {
|
||||||
|
let bit_pos = spi_int_raw - 64;
|
||||||
|
// Unwrap okay, valid index.
|
||||||
|
self.gicd.write_iser(2, 1 << bit_pos).unwrap();
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_all_spi_interrupts(&mut self) {
|
||||||
|
self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap();
|
||||||
|
self.gicd.write_iser(2, 0xFFFF_FFFF).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables all interrupts by calling [Self::enable_all_sgi_interrupts],
|
||||||
|
/// [Self::enable_all_ppi_interrupts] and [Self::enable_all_spi_interrupts].
|
||||||
|
pub fn enable_all_interrupts(&mut self) {
|
||||||
|
self.enable_all_sgi_interrupts();
|
||||||
|
self.enable_all_ppi_interrupts();
|
||||||
|
self.enable_all_spi_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the GIC assuming a possibly non-secure configuration.
|
||||||
|
///
|
||||||
|
/// This function will NOT configure and enable the various interrupt sources. You need to
|
||||||
|
/// set the interrupt sensitivities and targets before calling this function.
|
||||||
|
/// This function configured the control registers with the following settings:
|
||||||
|
///
|
||||||
|
/// - CPU interface: Secure and non-secure interrupts are enabled. SBPR, FIQen and AckCtrl
|
||||||
|
/// fields set to default value 0.
|
||||||
|
/// - Distributor interface: Both non-secure and secure interrupt distribution enabled.
|
||||||
|
///
|
||||||
|
/// It calls [Self::update_ctrl_regs] to update the control registers.
|
||||||
|
/// If you need custom settings, you can call [Self::update_ctrl_regs] with your required
|
||||||
|
/// settings.
|
||||||
|
///
|
||||||
|
/// This will not enable the interrupt exception for the Cortex-AR core. You might also have
|
||||||
|
/// to call [Self::enable_interrupts] for interrupts to work.
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.update_ctrl_regs(
|
||||||
|
Icr::builder()
|
||||||
|
.with_sbpr(false)
|
||||||
|
.with_fiq_en(false)
|
||||||
|
.with_ack_ctrl(false)
|
||||||
|
.with_enable_non_secure(true)
|
||||||
|
.with_enable_secure(true)
|
||||||
|
.build(),
|
||||||
|
Dcr::builder()
|
||||||
|
.with_enable_non_secure(true)
|
||||||
|
.with_enable_secure(true)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the regular interrupt exceprion for the Cortex-A core by calling the
|
||||||
|
/// [interrupt::enable] function. You also need to [Self::enable] the GIC for interrupts to
|
||||||
|
/// work.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Do not call this in a critical section.
|
||||||
|
pub unsafe fn enable_interrupts(&self) {
|
||||||
|
unsafe {
|
||||||
|
interrupt::enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the interrupts for the Cortex-A core by calling the [interrupt::enable] module.
|
||||||
|
pub fn disable_interrupts(&self) {
|
||||||
|
interrupt::disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the control registers which control the safety configuration and which also enable
|
||||||
|
/// the GIC.
|
||||||
|
pub fn update_ctrl_regs(&mut self, icr: Icr, dcr: Dcr) {
|
||||||
|
self.gicc.write_icr(icr);
|
||||||
|
self.gicd.write_dcr(dcr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper structure which should only be used inside the interrupt handler once the GIC has
|
||||||
|
/// been configured with the [GicConfigurator].
|
||||||
|
pub struct GicInterruptHelper(MmioGicc<'static>);
|
||||||
|
|
||||||
|
impl GicInterruptHelper {
|
||||||
|
/// Create the interrupt helper with the fixed GICC MMIO instance.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
GicInterruptHelper(unsafe { Gicc::new_mmio_fixed() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acknowledges an interrupt by reading the IAR register and returning the interrupt context
|
||||||
|
/// information structure.
|
||||||
|
///
|
||||||
|
/// This should be called at the start of an interrupt handler.
|
||||||
|
pub fn acknowledge_interrupt(&mut self) -> InterruptInfo {
|
||||||
|
let iar = self.0.read_iar();
|
||||||
|
let int_id = iar.ack_int_id().as_u32();
|
||||||
|
let interrupt = match int_id {
|
||||||
|
0..=15 => Interrupt::Sgi(int_id as usize),
|
||||||
|
27..=31 => Interrupt::Ppi(PpiInterrupt::try_from(int_id as u8).unwrap()),
|
||||||
|
32..=92 => Interrupt::Spi(SpiInterrupt::try_from(int_id as u8).unwrap()),
|
||||||
|
SPURIOUS_INTERRUPT_ID => Interrupt::Spurious,
|
||||||
|
_ => Interrupt::Invalid(int_id as usize),
|
||||||
|
};
|
||||||
|
InterruptInfo {
|
||||||
|
interrupt,
|
||||||
|
cpu_id: iar.cpu_id().as_u8(),
|
||||||
|
raw_reg: iar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Acknowledges the end of an interrupt by writing the EOIR register of the GICC.
|
||||||
|
///
|
||||||
|
/// This should be called at the end of an interrupt handler.
|
||||||
|
pub fn end_of_interrupt(&mut self, irq_info: InterruptInfo) {
|
||||||
|
self.0.write_eoir(irq_info.raw_reg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GicInterruptHelper {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
46
zynq7000-hal/src/gpio/emio.rs
Normal file
46
zynq7000-hal/src/gpio/emio.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//! EMIO (Extended Multiplexed I/O) resource management module.
|
||||||
|
use zynq7000::gpio::MmioGpio;
|
||||||
|
|
||||||
|
pub use crate::gpio::PinState;
|
||||||
|
|
||||||
|
pub struct EmioPin {
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmioPin {
|
||||||
|
/// This offset ranges from 0 to 64.
|
||||||
|
pub fn offset(&self) -> usize {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pins {
|
||||||
|
emios: [Option<EmioPin>; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pins {
|
||||||
|
/// Create a new EMIO pin structure.
|
||||||
|
///
|
||||||
|
/// This structure is supposed to be used as a singleton. It will configure all
|
||||||
|
/// EMIO pins as inputs. If you want to retrieve individual pins without this structure,
|
||||||
|
/// use [EmioPin::steal] instead.
|
||||||
|
pub fn new(mut mmio: MmioGpio) -> Self {
|
||||||
|
let mut emios = [const { None }; 64];
|
||||||
|
// Configure all EMIO pins as inputs.
|
||||||
|
mmio.bank_2().write_dirm(0);
|
||||||
|
mmio.bank_3().write_dirm(0);
|
||||||
|
|
||||||
|
(0..64).for_each(|i| {
|
||||||
|
emios[i] = Some(EmioPin { offset: i });
|
||||||
|
});
|
||||||
|
Self { emios }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take(&mut self, offset: usize) -> Option<EmioPin> {
|
||||||
|
self.emios[offset].take()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn give(&mut self, emio: EmioPin) {
|
||||||
|
self.emios[emio.offset].replace(emio);
|
||||||
|
}
|
||||||
|
}
|
345
zynq7000-hal/src/gpio/ll.rs
Normal file
345
zynq7000-hal/src/gpio/ll.rs
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
//! Low-level GPIO access module.
|
||||||
|
use embedded_hal::digital::PinState;
|
||||||
|
use zynq7000::gpio::{Gpio, MaskedOutput, MmioGpio};
|
||||||
|
|
||||||
|
use crate::slcr::Slcr;
|
||||||
|
|
||||||
|
use super::{mio::MuxConf, PinIsOutputOnly};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum PinOffset {
|
||||||
|
Mio(usize),
|
||||||
|
Emio(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PinOffset {
|
||||||
|
/// Returs [None] if offset is larger than 53.
|
||||||
|
pub const fn new_for_mio(offset: usize) -> Option<Self> {
|
||||||
|
if offset > 53 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PinOffset::Mio(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returs [None] if offset is larger than 63.
|
||||||
|
pub const fn new_for_emio(offset: usize) -> Option<Self> {
|
||||||
|
if offset > 63 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PinOffset::Emio(offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mio(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
PinOffset::Mio(_) => true,
|
||||||
|
PinOffset::Emio(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PinOffset {
|
||||||
|
pub fn offset(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
PinOffset::Mio(offset) => *offset,
|
||||||
|
PinOffset::Emio(offset) => *offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LowLevelGpio {
|
||||||
|
offset: PinOffset,
|
||||||
|
regs: MmioGpio<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LowLevelGpio {
|
||||||
|
pub fn new(offset: PinOffset) -> Self {
|
||||||
|
Self {
|
||||||
|
offset,
|
||||||
|
regs: unsafe { Gpio::new_mmio_fixed() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> PinOffset {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an output pin.
|
||||||
|
pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
|
||||||
|
let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
|
||||||
|
if self.offset.is_mio() {
|
||||||
|
// Tri-state bit must be 0 for the output driver to work.
|
||||||
|
self.reconfigure_slcr_mio_cfg(false, None, Some(MuxConf::new_for_gpio()));
|
||||||
|
}
|
||||||
|
let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
|
||||||
|
curr_dirm |= 1 << offset;
|
||||||
|
unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
|
||||||
|
let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
|
||||||
|
curr_outen |= 1 << offset;
|
||||||
|
unsafe { core::ptr::write_volatile(outen, curr_outen) };
|
||||||
|
// Unwrap okay, just set mode.
|
||||||
|
self.write_state(init_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an output pin with open drain emulation.
|
||||||
|
///
|
||||||
|
/// This works by only enabling the output driver when the pin is driven low and letting
|
||||||
|
/// the pin float when it is driven high. A pin pull-up is used for MIO pins as well which
|
||||||
|
/// pulls the pin to a defined state if it is not driven. This allows something like 1-wire bus
|
||||||
|
/// operation because other devices can pull the pin low as well.
|
||||||
|
///
|
||||||
|
/// For EMIO pins, the pull-up and the IO buffer necessary for open-drain usage must be
|
||||||
|
/// provided by the FPGA design.
|
||||||
|
pub fn configure_as_output_open_drain(
|
||||||
|
&mut self,
|
||||||
|
init_level: PinState,
|
||||||
|
with_internal_pullup: bool,
|
||||||
|
) {
|
||||||
|
let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
|
||||||
|
if self.offset.is_mio() {
|
||||||
|
// Tri-state bit must be 0 for the output driver to work. Enable the pullup pin.
|
||||||
|
self.reconfigure_slcr_mio_cfg(
|
||||||
|
false,
|
||||||
|
Some(with_internal_pullup),
|
||||||
|
Some(MuxConf::new_for_gpio()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
|
||||||
|
curr_dirm |= 1 << offset;
|
||||||
|
unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
|
||||||
|
// Disable the output driver depending on initial level.
|
||||||
|
let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
|
||||||
|
if init_level == PinState::High {
|
||||||
|
curr_outen &= !(1 << offset);
|
||||||
|
} else {
|
||||||
|
curr_outen |= 1 << offset;
|
||||||
|
self.write_state(init_level);
|
||||||
|
}
|
||||||
|
unsafe { core::ptr::write_volatile(outen, curr_outen) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into a floating input pin.
|
||||||
|
pub fn configure_as_input_floating(&mut self) -> Result<(), PinIsOutputOnly> {
|
||||||
|
if self.offset.is_mio() {
|
||||||
|
let offset_raw = self.offset.offset();
|
||||||
|
if offset_raw == 7 || offset_raw == 8 {
|
||||||
|
return Err(PinIsOutputOnly);
|
||||||
|
}
|
||||||
|
self.reconfigure_slcr_mio_cfg(true, Some(false), Some(MuxConf::new_for_gpio()));
|
||||||
|
}
|
||||||
|
self.configure_input_pin();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an input pin with a pull up.
|
||||||
|
pub fn configure_as_input_with_pull_up(&mut self) -> Result<(), PinIsOutputOnly> {
|
||||||
|
if self.offset.is_mio() {
|
||||||
|
let offset_raw = self.offset.offset();
|
||||||
|
if offset_raw == 7 || offset_raw == 8 {
|
||||||
|
return Err(PinIsOutputOnly);
|
||||||
|
}
|
||||||
|
self.reconfigure_slcr_mio_cfg(true, Some(true), Some(MuxConf::new_for_gpio()));
|
||||||
|
}
|
||||||
|
self.configure_input_pin();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin into an IO peripheral pin.
|
||||||
|
pub fn configure_as_io_periph_pin(&mut self, mux_conf: MuxConf, pullup: Option<bool>) {
|
||||||
|
self.reconfigure_slcr_mio_cfg(false, pullup, Some(mux_conf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low(&self) -> bool {
|
||||||
|
let (offset, in_reg) = self.get_data_in_reg_and_local_offset();
|
||||||
|
let in_val = unsafe { core::ptr::read_volatile(in_reg) };
|
||||||
|
((in_val >> offset) & 0b1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high(&self) -> bool {
|
||||||
|
!self.is_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_set_low(&self) -> bool {
|
||||||
|
let (offset, out_reg) = self.get_data_out_reg_and_local_offset();
|
||||||
|
let out_val = unsafe { core::ptr::read_volatile(out_reg) };
|
||||||
|
((out_val >> offset) & 0b1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_set_high(&self) -> bool {
|
||||||
|
!self.is_set_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_output_driver(&mut self) {
|
||||||
|
let (offset, _dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
|
||||||
|
let mut outen_reg = unsafe { core::ptr::read_volatile(outen) };
|
||||||
|
outen_reg |= 1 << offset;
|
||||||
|
unsafe { core::ptr::write_volatile(outen, outen_reg) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_output_driver(&mut self) {
|
||||||
|
let (offset, _dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
|
||||||
|
let mut outen_reg = unsafe { core::ptr::read_volatile(outen) };
|
||||||
|
outen_reg &= !(1 << offset);
|
||||||
|
unsafe { core::ptr::write_volatile(outen, outen_reg) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low(&mut self) {
|
||||||
|
self.write_state(PinState::Low)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high(&mut self) {
|
||||||
|
self.write_state(PinState::High)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_state(&mut self, level: PinState) {
|
||||||
|
let (offset_in_reg, masked_out_ptr) = self.get_masked_out_reg_and_local_offset();
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_volatile(
|
||||||
|
masked_out_ptr,
|
||||||
|
MaskedOutput::builder()
|
||||||
|
.with_mask(!(1 << offset_in_reg))
|
||||||
|
.with_output((level as u16) << offset_in_reg)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconfigure_slcr_mio_cfg(
|
||||||
|
&mut self,
|
||||||
|
tristate: bool,
|
||||||
|
pullup: Option<bool>,
|
||||||
|
mux_conf: Option<MuxConf>,
|
||||||
|
) {
|
||||||
|
let raw_offset = self.offset.offset();
|
||||||
|
// Safety: We only modify the MIO config of the pin.
|
||||||
|
let mut slcr_wrapper = unsafe { Slcr::steal() };
|
||||||
|
// We read first, because writing also required unlocking the SLCR.
|
||||||
|
// This allows the user to configure the SLCR themselves to avoid unnecessary
|
||||||
|
// re-configuration which might also be potentially unsafe at run-time.
|
||||||
|
let mio_cfg = slcr_wrapper.regs().read_mio_pins(raw_offset).unwrap();
|
||||||
|
if (pullup.is_some() && mio_cfg.pullup() != pullup.unwrap())
|
||||||
|
|| (mux_conf.is_some() && MuxConf::from(mio_cfg) != mux_conf.unwrap())
|
||||||
|
|| tristate != mio_cfg.tri_enable()
|
||||||
|
{
|
||||||
|
slcr_wrapper.modify(|mut_slcr| {
|
||||||
|
mut_slcr
|
||||||
|
.modify_mio_pins(raw_offset, |mut val| {
|
||||||
|
if let Some(pullup) = pullup {
|
||||||
|
val.set_pullup(pullup);
|
||||||
|
}
|
||||||
|
if let Some(mux_conf) = mux_conf {
|
||||||
|
val.set_l0_sel(mux_conf.l0_sel());
|
||||||
|
val.set_l1_sel(mux_conf.l1_sel());
|
||||||
|
val.set_l2_sel(mux_conf.l2_sel());
|
||||||
|
val.set_l3_sel(mux_conf.l3_sel());
|
||||||
|
}
|
||||||
|
val.set_tri_enable(tristate);
|
||||||
|
val
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure_input_pin(&mut self) {
|
||||||
|
let (offset, dirm, outen) = self.get_dirm_outen_regs_and_local_offset();
|
||||||
|
let mut curr_dirm = unsafe { core::ptr::read_volatile(dirm) };
|
||||||
|
curr_dirm &= !(1 << offset);
|
||||||
|
unsafe { core::ptr::write_volatile(dirm, curr_dirm) };
|
||||||
|
let mut curr_outen = unsafe { core::ptr::read_volatile(outen) };
|
||||||
|
curr_outen &= !(1 << offset);
|
||||||
|
unsafe { core::ptr::write_volatile(outen, curr_outen) };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_data_in_reg_and_local_offset(&self) -> (usize, *mut u32) {
|
||||||
|
match self.offset {
|
||||||
|
PinOffset::Mio(offset) => match offset {
|
||||||
|
0..=31 => (offset, self.regs.pointer_to_in_0()),
|
||||||
|
32..=53 => (offset - 32, self.regs.pointer_to_in_1()),
|
||||||
|
_ => panic!("invalid MIO pin offset"),
|
||||||
|
},
|
||||||
|
PinOffset::Emio(offset) => match offset {
|
||||||
|
0..=31 => (offset, self.regs.pointer_to_in_2()),
|
||||||
|
32..=63 => (offset - 32, self.regs.pointer_to_in_3()),
|
||||||
|
_ => panic!("invalid EMIO pin offset"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_data_out_reg_and_local_offset(&self) -> (usize, *mut u32) {
|
||||||
|
match self.offset {
|
||||||
|
PinOffset::Mio(offset) => match offset {
|
||||||
|
0..=31 => (offset, self.regs.pointer_to_out_0()),
|
||||||
|
32..=53 => (offset - 32, self.regs.pointer_to_out_1()),
|
||||||
|
_ => panic!("invalid MIO pin offset"),
|
||||||
|
},
|
||||||
|
PinOffset::Emio(offset) => match offset {
|
||||||
|
0..=31 => (offset, self.regs.pointer_to_out_2()),
|
||||||
|
32..=63 => (offset - 32, self.regs.pointer_to_out_3()),
|
||||||
|
_ => panic!("invalid EMIO pin offset"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_dirm_outen_regs_and_local_offset(&self) -> (usize, *mut u32, *mut u32) {
|
||||||
|
match self.offset {
|
||||||
|
PinOffset::Mio(offset) => match offset {
|
||||||
|
0..=31 => (
|
||||||
|
offset,
|
||||||
|
self.regs.bank_0_shared().pointer_to_dirm(),
|
||||||
|
self.regs.bank_0_shared().pointer_to_out_en(),
|
||||||
|
),
|
||||||
|
32..=53 => (
|
||||||
|
offset - 32,
|
||||||
|
self.regs.bank_1_shared().pointer_to_dirm(),
|
||||||
|
self.regs.bank_1_shared().pointer_to_out_en(),
|
||||||
|
),
|
||||||
|
_ => panic!("invalid MIO pin offset"),
|
||||||
|
},
|
||||||
|
PinOffset::Emio(offset) => match offset {
|
||||||
|
0..=31 => (
|
||||||
|
offset,
|
||||||
|
self.regs.bank_2_shared().pointer_to_dirm(),
|
||||||
|
self.regs.bank_2_shared().pointer_to_out_en(),
|
||||||
|
),
|
||||||
|
32..=63 => (
|
||||||
|
offset - 32,
|
||||||
|
self.regs.bank_3_shared().pointer_to_dirm(),
|
||||||
|
self.regs.bank_3_shared().pointer_to_out_en(),
|
||||||
|
),
|
||||||
|
_ => panic!("invalid EMIO pin offset"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_masked_out_reg_and_local_offset(&mut self) -> (usize, *mut MaskedOutput) {
|
||||||
|
match self.offset {
|
||||||
|
PinOffset::Mio(offset) => match offset {
|
||||||
|
0..=15 => (offset, self.regs.pointer_to_masked_out_0_lsw()),
|
||||||
|
16..=31 => (offset - 16, self.regs.pointer_to_masked_out_0_msw()),
|
||||||
|
32..=47 => (offset - 32, self.regs.pointer_to_masked_out_1_lsw()),
|
||||||
|
48..=53 => (offset - 48, self.regs.pointer_to_masked_out_1_msw()),
|
||||||
|
_ => panic!("invalid MIO pin offset"),
|
||||||
|
},
|
||||||
|
PinOffset::Emio(offset) => match offset {
|
||||||
|
0..=15 => (offset, self.regs.pointer_to_masked_out_2_lsw()),
|
||||||
|
16..=31 => (offset - 16, self.regs.pointer_to_masked_out_2_msw()),
|
||||||
|
32..=47 => (offset - 32, self.regs.pointer_to_masked_out_3_lsw()),
|
||||||
|
48..=63 => (offset - 48, self.regs.pointer_to_masked_out_3_msw()),
|
||||||
|
_ => panic!("invalid EMIO pin offset"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
364
zynq7000-hal/src/gpio/mio.rs
Normal file
364
zynq7000-hal/src/gpio/mio.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
//! Multiplexed I/O (MIO) module.
|
||||||
|
//!
|
||||||
|
//! This module provides a [singleton][Pins] for the resource management of all MIO pins. This
|
||||||
|
//! also allows associating the pins, their modes and their IDs to the peripherals they are able to
|
||||||
|
//! serve.
|
||||||
|
use arbitrary_int::{u2, u3};
|
||||||
|
use zynq7000::gpio::MmioGpio;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct MuxConf {
|
||||||
|
l3: u3,
|
||||||
|
l2: u2,
|
||||||
|
l1: bool,
|
||||||
|
l0: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<zynq7000::slcr::mio::Config> for MuxConf {
|
||||||
|
fn from(value: zynq7000::slcr::mio::Config) -> Self {
|
||||||
|
Self::new(
|
||||||
|
value.l0_sel(),
|
||||||
|
value.l1_sel(),
|
||||||
|
value.l2_sel(),
|
||||||
|
value.l3_sel(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MuxConf {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(l0: bool, l1: bool, l2: u2, l3: u3) -> Self {
|
||||||
|
Self { l3, l2, l1, l0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_with_l3(l3: u3) -> Self {
|
||||||
|
Self::new(false, false, u2::new(0b00), l3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_for_gpio() -> Self {
|
||||||
|
Self::new(false, false, u2::new(0), u3::new(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn l0_sel(&self) -> bool {
|
||||||
|
self.l0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn l1_sel(&self) -> bool {
|
||||||
|
self.l1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn l2_sel(&self) -> u2 {
|
||||||
|
self.l2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn l3_sel(&self) -> u3 {
|
||||||
|
self.l3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PinId {
|
||||||
|
const OFFSET: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pin_id {
|
||||||
|
($Id:ident, $num:literal) => {
|
||||||
|
// Need paste macro to use ident in doc attribute
|
||||||
|
paste::paste! {
|
||||||
|
#[doc = "Pin ID representing pin " $Id]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum $Id {}
|
||||||
|
impl $crate::sealed::Sealed for $Id {}
|
||||||
|
impl PinId for $Id {
|
||||||
|
const OFFSET: usize = $num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_id!(Mio0, 0);
|
||||||
|
pin_id!(Mio1, 1);
|
||||||
|
pin_id!(Mio2, 2);
|
||||||
|
pin_id!(Mio3, 3);
|
||||||
|
pin_id!(Mio4, 4);
|
||||||
|
pin_id!(Mio5, 5);
|
||||||
|
pin_id!(Mio6, 6);
|
||||||
|
pin_id!(Mio7, 7);
|
||||||
|
pin_id!(Mio8, 8);
|
||||||
|
pin_id!(Mio9, 9);
|
||||||
|
pin_id!(Mio10, 10);
|
||||||
|
pin_id!(Mio11, 11);
|
||||||
|
pin_id!(Mio12, 12);
|
||||||
|
pin_id!(Mio13, 13);
|
||||||
|
pin_id!(Mio14, 14);
|
||||||
|
pin_id!(Mio15, 15);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio16, 16);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio17, 17);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio18, 18);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio19, 19);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio20, 20);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio21, 21);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio22, 22);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio23, 23);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio24, 24);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio25, 25);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio26, 26);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio27, 27);
|
||||||
|
pin_id!(Mio28, 28);
|
||||||
|
pin_id!(Mio29, 29);
|
||||||
|
pin_id!(Mio30, 30);
|
||||||
|
pin_id!(Mio31, 31);
|
||||||
|
|
||||||
|
pin_id!(Mio32, 32);
|
||||||
|
pin_id!(Mio33, 33);
|
||||||
|
pin_id!(Mio34, 34);
|
||||||
|
pin_id!(Mio35, 35);
|
||||||
|
pin_id!(Mio36, 36);
|
||||||
|
pin_id!(Mio37, 37);
|
||||||
|
pin_id!(Mio38, 38);
|
||||||
|
pin_id!(Mio39, 39);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio40, 40);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio41, 41);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio42, 42);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio43, 43);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio44, 44);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio45, 45);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio46, 46);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio47, 47);
|
||||||
|
pin_id!(Mio48, 48);
|
||||||
|
pin_id!(Mio49, 49);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio50, 50);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pin_id!(Mio51, 51);
|
||||||
|
pin_id!(Mio52, 52);
|
||||||
|
pin_id!(Mio53, 53);
|
||||||
|
|
||||||
|
pub trait MioPinMarker {
|
||||||
|
fn offset(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pin<I: PinId> {
|
||||||
|
phantom: core::marker::PhantomData<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> Pin<I> {
|
||||||
|
#[inline]
|
||||||
|
const unsafe fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
//pin: LowLevelPin::new(I::OFFSET),
|
||||||
|
phantom: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steal a typed MIO pin.
|
||||||
|
///
|
||||||
|
/// Usually, you can just use the MIO pin members of the [MioPins] structure.
|
||||||
|
/// However, if you pass the pins into a consuming peripheral driver which performs
|
||||||
|
/// immediate type erasure, and you require the pins for/after a re-configuration
|
||||||
|
/// of the system, you can unsafely steal the pin. This function will NOT perform any
|
||||||
|
/// re-configuration.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This allows to create multiple instances of the same pin, which can lead to
|
||||||
|
/// data races on concurrent access.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn steal() -> Self {
|
||||||
|
unsafe { Self::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pins {
|
||||||
|
pub mio0: Pin<Mio0>,
|
||||||
|
pub mio1: Pin<Mio1>,
|
||||||
|
pub mio2: Pin<Mio2>,
|
||||||
|
pub mio3: Pin<Mio3>,
|
||||||
|
pub mio4: Pin<Mio4>,
|
||||||
|
pub mio5: Pin<Mio5>,
|
||||||
|
pub mio6: Pin<Mio6>,
|
||||||
|
pub mio7: Pin<Mio7>,
|
||||||
|
pub mio8: Pin<Mio8>,
|
||||||
|
pub mio9: Pin<Mio9>,
|
||||||
|
pub mio10: Pin<Mio10>,
|
||||||
|
pub mio11: Pin<Mio11>,
|
||||||
|
pub mio12: Pin<Mio12>,
|
||||||
|
pub mio13: Pin<Mio13>,
|
||||||
|
pub mio14: Pin<Mio14>,
|
||||||
|
pub mio15: Pin<Mio15>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio16: Pin<Mio16>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio17: Pin<Mio17>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio18: Pin<Mio18>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio19: Pin<Mio19>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio20: Pin<Mio20>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio21: Pin<Mio21>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio22: Pin<Mio22>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio23: Pin<Mio23>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio24: Pin<Mio24>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio25: Pin<Mio25>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio26: Pin<Mio26>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio27: Pin<Mio27>,
|
||||||
|
pub mio28: Pin<Mio28>,
|
||||||
|
pub mio29: Pin<Mio29>,
|
||||||
|
pub mio30: Pin<Mio30>,
|
||||||
|
pub mio31: Pin<Mio31>,
|
||||||
|
|
||||||
|
pub mio32: Pin<Mio32>,
|
||||||
|
pub mio33: Pin<Mio33>,
|
||||||
|
pub mio34: Pin<Mio34>,
|
||||||
|
pub mio35: Pin<Mio35>,
|
||||||
|
pub mio36: Pin<Mio36>,
|
||||||
|
pub mio37: Pin<Mio37>,
|
||||||
|
pub mio38: Pin<Mio38>,
|
||||||
|
pub mio39: Pin<Mio39>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio40: Pin<Mio40>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio41: Pin<Mio41>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio42: Pin<Mio42>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio43: Pin<Mio43>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio44: Pin<Mio44>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio45: Pin<Mio45>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio46: Pin<Mio46>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio47: Pin<Mio47>,
|
||||||
|
pub mio48: Pin<Mio48>,
|
||||||
|
pub mio49: Pin<Mio49>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio50: Pin<Mio50>,
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
pub mio51: Pin<Mio51>,
|
||||||
|
pub mio52: Pin<Mio52>,
|
||||||
|
pub mio53: Pin<Mio53>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pins {
|
||||||
|
pub const fn new(_mmio: MmioGpio) -> Self {
|
||||||
|
Self {
|
||||||
|
mio0: unsafe { Pin::new() },
|
||||||
|
mio1: unsafe { Pin::new() },
|
||||||
|
mio2: unsafe { Pin::new() },
|
||||||
|
mio3: unsafe { Pin::new() },
|
||||||
|
mio4: unsafe { Pin::new() },
|
||||||
|
mio5: unsafe { Pin::new() },
|
||||||
|
mio6: unsafe { Pin::new() },
|
||||||
|
mio7: unsafe { Pin::new() },
|
||||||
|
mio8: unsafe { Pin::new() },
|
||||||
|
mio9: unsafe { Pin::new() },
|
||||||
|
mio10: unsafe { Pin::new() },
|
||||||
|
mio11: unsafe { Pin::new() },
|
||||||
|
mio12: unsafe { Pin::new() },
|
||||||
|
mio13: unsafe { Pin::new() },
|
||||||
|
mio14: unsafe { Pin::new() },
|
||||||
|
mio15: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio16: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio17: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio18: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio19: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio20: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio21: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio22: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio23: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio24: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio25: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio26: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio27: unsafe { Pin::new() },
|
||||||
|
mio28: unsafe { Pin::new() },
|
||||||
|
mio29: unsafe { Pin::new() },
|
||||||
|
mio30: unsafe { Pin::new() },
|
||||||
|
mio31: unsafe { Pin::new() },
|
||||||
|
|
||||||
|
mio32: unsafe { Pin::new() },
|
||||||
|
mio33: unsafe { Pin::new() },
|
||||||
|
mio34: unsafe { Pin::new() },
|
||||||
|
mio35: unsafe { Pin::new() },
|
||||||
|
mio36: unsafe { Pin::new() },
|
||||||
|
mio37: unsafe { Pin::new() },
|
||||||
|
mio38: unsafe { Pin::new() },
|
||||||
|
mio39: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio40: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio41: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio42: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio43: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio44: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio45: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio46: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio47: unsafe { Pin::new() },
|
||||||
|
mio48: unsafe { Pin::new() },
|
||||||
|
mio49: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio50: unsafe { Pin::new() },
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
mio51: unsafe { Pin::new() },
|
||||||
|
mio52: unsafe { Pin::new() },
|
||||||
|
mio53: unsafe { Pin::new() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> MioPinMarker for Pin<I> {
|
||||||
|
fn offset(&self) -> usize {
|
||||||
|
I::OFFSET
|
||||||
|
}
|
||||||
|
}
|
406
zynq7000-hal/src/gpio/mod.rs
Normal file
406
zynq7000-hal/src/gpio/mod.rs
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
//! GPIO support module for the Zynq7000 SoC.
|
||||||
|
//!
|
||||||
|
//! This module contains a MIO and EMIO pin resource managements singleton as well as abstractions
|
||||||
|
//! to use these pins as GPIOs.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/main.rs)
|
||||||
|
//! - [Logger example](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/logger.rs)
|
||||||
|
//! which uses MIO pins for the UART.
|
||||||
|
pub mod emio;
|
||||||
|
pub mod ll;
|
||||||
|
pub mod mio;
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use ll::PinOffset;
|
||||||
|
use mio::{MioPinMarker, MuxConf};
|
||||||
|
|
||||||
|
use crate::gpio::ll::LowLevelGpio;
|
||||||
|
use crate::{enable_amba_peripheral_clock, slcr::Slcr};
|
||||||
|
pub use embedded_hal::digital::PinState;
|
||||||
|
use zynq7000::{gpio::MmioGpio, slcr::reset::GpioClockReset};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("MIO pins 7 and 8 can only be output pins")]
|
||||||
|
pub struct PinIsOutputOnly;
|
||||||
|
|
||||||
|
/// GPIO pin singleton to allow resource management of both MIO and EMIO pins.
|
||||||
|
pub struct GpioPins {
|
||||||
|
pub mio: mio::Pins,
|
||||||
|
pub emio: emio::Pins,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpioPins {
|
||||||
|
pub fn new(gpio: MmioGpio) -> Self {
|
||||||
|
enable_amba_peripheral_clock(crate::PeripheralSelect::Gpio);
|
||||||
|
Self {
|
||||||
|
mio: mio::Pins::new(unsafe { gpio.clone() }),
|
||||||
|
emio: emio::Pins::new(gpio),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the GPIO peripheral using the SLCR reset register for GPIO.
|
||||||
|
#[inline]
|
||||||
|
pub fn reset() {
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.reset_ctrl()
|
||||||
|
.write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(true).build());
|
||||||
|
// Keep it in reset for one cycle.. not sure if this is necessary.
|
||||||
|
cortex_ar::asm::nop();
|
||||||
|
regs.reset_ctrl()
|
||||||
|
.write_gpio(GpioClockReset::builder().with_gpio_cpu1x_rst(false).build());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enumeration of all pin modes. Some of the modes are only valid for MIO pins.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PinMode {
|
||||||
|
OutputPushPull,
|
||||||
|
/// See [super::gpio] documentation for more information on running an output pin in
|
||||||
|
/// open-drain configuration.
|
||||||
|
OutputOpenDrain,
|
||||||
|
InputFloating,
|
||||||
|
InputPullUp,
|
||||||
|
/// MIO-only peripheral pin configuration
|
||||||
|
MioIoPeriph(MuxConf),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("invalid pin mode for MIO pin: {0:?}")]
|
||||||
|
pub struct InvalidPinMode(pub PinMode);
|
||||||
|
|
||||||
|
impl embedded_hal::digital::Error for InvalidPinMode {
|
||||||
|
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||||
|
embedded_hal::digital::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IoPinProvider {
|
||||||
|
fn mode(&self) -> PinMode;
|
||||||
|
|
||||||
|
fn offset(&self) -> PinOffset;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_input(&self) -> bool {
|
||||||
|
matches!(self.mode(), PinMode::InputFloating | PinMode::InputPullUp)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_output(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.mode(),
|
||||||
|
PinMode::OutputPushPull | PinMode::OutputOpenDrain
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_io_periph(&self) -> bool {
|
||||||
|
matches!(self.mode(), PinMode::MioIoPeriph(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flex pin abstraction which can be dynamically re-configured.
|
||||||
|
///
|
||||||
|
/// The following functions can be configured at run-time:
|
||||||
|
///
|
||||||
|
/// - Input Floating
|
||||||
|
/// - Input with Pull-Up
|
||||||
|
/// - Output Push-Pull
|
||||||
|
/// - Output Open-Drain.
|
||||||
|
///
|
||||||
|
/// Flex pins are always floating input pins after construction except for MIO7 and MIO8,
|
||||||
|
/// which are Push-Pull Output pins with initial low-level.
|
||||||
|
///
|
||||||
|
/// ## Notes on [PinMode::OutputOpenDrain] configuration
|
||||||
|
///
|
||||||
|
/// For MIO, the open-drain functionality is simulated by only enabling the output driver
|
||||||
|
/// when driving the pin low, and leaving the pin floating when the pin is driven high.
|
||||||
|
/// The internal pull-up will also be enabled to have a high state if the pin is not driven.
|
||||||
|
///
|
||||||
|
/// For EMIO, the pull-up and the IO buffer needs to be provided in the FPGA design for the
|
||||||
|
/// used EMIO pins because the EMIO pins are just wires going out to the FPGA design.
|
||||||
|
/// The software will still perform the necessary logic when driving the pin low or high.
|
||||||
|
///
|
||||||
|
/// ## Notes on [PinMode::InputPullUp] configuration
|
||||||
|
///
|
||||||
|
/// For EMIO, the pull-up wiring needs to be provided by the FPGA design.
|
||||||
|
pub struct Flex {
|
||||||
|
ll: LowLevelGpio,
|
||||||
|
mode: PinMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Flex {
|
||||||
|
pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Self {
|
||||||
|
let mut ll = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
|
||||||
|
if I::OFFSET == 7 || I::OFFSET == 8 {
|
||||||
|
ll.configure_as_output_push_pull(PinState::Low);
|
||||||
|
} else {
|
||||||
|
ll.configure_as_input_floating().unwrap();
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
ll,
|
||||||
|
mode: PinMode::InputFloating,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_for_emio(pin: emio::EmioPin) -> Self {
|
||||||
|
let mut ll = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
|
||||||
|
ll.configure_as_input_floating().unwrap();
|
||||||
|
Self {
|
||||||
|
ll,
|
||||||
|
mode: PinMode::InputFloating,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_as_input_floating(&mut self) -> Result<(), PinIsOutputOnly> {
|
||||||
|
self.mode = PinMode::InputFloating;
|
||||||
|
self.ll.configure_as_input_floating()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_as_input_with_pull_up(&mut self) -> Result<(), PinIsOutputOnly> {
|
||||||
|
self.mode = PinMode::InputPullUp;
|
||||||
|
self.ll.configure_as_input_with_pull_up()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_as_output_push_pull(&mut self, level: PinState) {
|
||||||
|
self.mode = PinMode::OutputPushPull;
|
||||||
|
self.ll.configure_as_output_push_pull(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_as_output_open_drain(&mut self, level: PinState, with_internal_pullup: bool) {
|
||||||
|
self.mode = PinMode::OutputOpenDrain;
|
||||||
|
self.ll.configure_as_output_open_drain(level, with_internal_pullup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is configured as an input pin, this function does nothing.
|
||||||
|
pub fn set_high(&mut self) {
|
||||||
|
if self.is_input() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.mode == PinMode::OutputOpenDrain {
|
||||||
|
self.ll.disable_output_driver();
|
||||||
|
} else {
|
||||||
|
self.ll.set_high();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is configured as an input pin, this function does nothing.
|
||||||
|
pub fn set_low(&mut self) {
|
||||||
|
if self.is_input() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.ll.set_low();
|
||||||
|
if self.mode == PinMode::OutputOpenDrain {
|
||||||
|
self.ll.enable_output_driver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the input state of the pin, regardless of configured mode.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high(&self) -> bool {
|
||||||
|
self.ll.is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the input state of the pin, regardless of configured mode.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low(&self) -> bool {
|
||||||
|
!self.ll.is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||||
|
/// of this function is undefined.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_set_low(&self) -> bool {
|
||||||
|
self.ll.is_set_low()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||||
|
/// of this function is undefined.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_set_high(&self) -> bool {
|
||||||
|
!self.is_set_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoPinProvider for Flex {
|
||||||
|
fn mode(&self) -> PinMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset(&self) -> PinOffset {
|
||||||
|
self.ll.offset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for Flex {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::InputPin for Flex {
|
||||||
|
/// Reads the input state of the pin, regardless of configured mode.
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.ll.is_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the input state of the pin, regardless of configured mode.
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.ll.is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::OutputPin for Flex {
|
||||||
|
/// If the pin is configured as an input pin, this function does nothing.
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_low();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is configured as an input pin, this function does nothing.
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.set_high();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::StatefulOutputPin for Flex {
|
||||||
|
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||||
|
/// of this function is undefined.
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.ll.is_set_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||||
|
/// of this function is undefined.
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.ll.is_set_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push-Pull output pin.
|
||||||
|
pub struct Output(LowLevelGpio);
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>, init_level: PinState) -> Self {
|
||||||
|
let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
|
||||||
|
low_level.configure_as_output_push_pull(init_level);
|
||||||
|
Self(low_level)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_for_emio(pin: emio::EmioPin, init_level: PinState) -> Self {
|
||||||
|
let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
|
||||||
|
low_level.configure_as_output_push_pull(init_level);
|
||||||
|
Self(low_level)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low(&mut self) {
|
||||||
|
self.0.set_low();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high(&mut self) {
|
||||||
|
self.0.set_high();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for Output {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::OutputPin for Output {
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.0.set_low();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.0.set_high();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::StatefulOutputPin for Output {
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.0.is_set_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.0.is_set_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input pin.
|
||||||
|
pub struct Input(LowLevelGpio);
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn new_for_mio<I: mio::PinId>(_pin: mio::Pin<I>) -> Result<Self, PinIsOutputOnly> {
|
||||||
|
let mut low_level = LowLevelGpio::new(PinOffset::Mio(I::OFFSET));
|
||||||
|
low_level.configure_as_input_floating()?;
|
||||||
|
Ok(Self(low_level))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_for_emio(pin: emio::EmioPin) -> Result<Self, PinIsOutputOnly> {
|
||||||
|
let mut low_level = LowLevelGpio::new(PinOffset::new_for_emio(pin.offset()).unwrap());
|
||||||
|
low_level.configure_as_input_floating()?;
|
||||||
|
Ok(Self(low_level))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_high(&self) -> bool {
|
||||||
|
self.0.is_high()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_low(&self) -> bool {
|
||||||
|
self.0.is_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for Input {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::InputPin for Input {
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.0.is_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self.0.is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IO peripheral pin.
|
||||||
|
pub struct IoPeriphPin {
|
||||||
|
pin: LowLevelGpio,
|
||||||
|
mux_conf: MuxConf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoPeriphPin {
|
||||||
|
pub fn new(pin: impl MioPinMarker, mux_conf: MuxConf, pullup: Option<bool>) -> Self {
|
||||||
|
let mut low_level = LowLevelGpio::new(PinOffset::Mio(pin.offset()));
|
||||||
|
low_level.configure_as_io_periph_pin(mux_conf, pullup);
|
||||||
|
Self {
|
||||||
|
pin: low_level,
|
||||||
|
mux_conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoPinProvider for IoPeriphPin {
|
||||||
|
#[inline]
|
||||||
|
fn mode(&self) -> PinMode {
|
||||||
|
PinMode::MioIoPeriph(self.mux_conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn offset(&self) -> PinOffset {
|
||||||
|
self.pin.offset()
|
||||||
|
}
|
||||||
|
}
|
169
zynq7000-hal/src/gtc.rs
Normal file
169
zynq7000-hal/src/gtc.rs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
//! Global timer counter driver module.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [GTC ticks example](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs)
|
||||||
|
//! - [Embassy Timer Driver](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000-embassy/src/lib.rs)
|
||||||
|
use zynq7000::gtc::MmioGtc;
|
||||||
|
|
||||||
|
use crate::{clocks::ArmClocks, time::Hertz};
|
||||||
|
|
||||||
|
/// High level GTC driver.
|
||||||
|
///
|
||||||
|
/// This structure also allows an optional clock member, which is required for the
|
||||||
|
/// [frequency_to_ticks] function and the [embedded_hal::delay::DelayNs] implementation
|
||||||
|
/// to work.
|
||||||
|
pub struct Gtc {
|
||||||
|
regs: MmioGtc<'static>,
|
||||||
|
cpu_3x2x_clock: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Gtc {}
|
||||||
|
|
||||||
|
pub const fn frequency_to_ticks(clock: Hertz, frequency: Hertz) -> u32 {
|
||||||
|
clock.raw().div_ceil(frequency.raw())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gtc {
|
||||||
|
/// Create a peripheral driver from a MMIO GTC block.
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(_regs: MmioGtc<'static>, clocks: &ArmClocks) -> Self {
|
||||||
|
unsafe { Self::steal_fixed(Some(clocks.cpu_3x2x_clk())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steal the GTC from the PAC.
|
||||||
|
///
|
||||||
|
/// This function still expect the GTC clock, which is the CPU 3x2x clock frequency.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function allows creating an arbitrary amount of memory-mapped peripheral drivers.
|
||||||
|
/// See the [zynq7000::gtc::Gtc::new_mmio] docs for more safety information.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn steal_fixed(cpu_3x2x_clk: Option<Hertz>) -> Self {
|
||||||
|
Self {
|
||||||
|
regs: unsafe { zynq7000::gtc::Gtc::new_mmio_fixed() },
|
||||||
|
cpu_3x2x_clock: cpu_3x2x_clk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cpu_3x2x_clock(&mut self, clock: Hertz) {
|
||||||
|
self.cpu_3x2x_clock = Some(clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Change this API once pure-reads work.
|
||||||
|
/// Read the 64-bit timer.
|
||||||
|
#[inline]
|
||||||
|
pub fn read_timer(&self) -> u64 {
|
||||||
|
// Safety: We require interior mutability here because even reads are unsafe.
|
||||||
|
// But we want to avoid a RefCell which would incur a run-time cost solely to make this
|
||||||
|
// function non-mut, so we steal the GTC here. Ownership is guaranteed or mandated
|
||||||
|
// by constructor.
|
||||||
|
let upper = self.regs.read_count_upper();
|
||||||
|
loop {
|
||||||
|
let lower = self.regs.read_count_lower();
|
||||||
|
if self.regs.read_count_upper() == upper {
|
||||||
|
return ((upper as u64) << 32) | (lower as u64);
|
||||||
|
}
|
||||||
|
// Overflow, read upper again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the comparator which can be used to trigger an interrupt in the future.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_comparator(&mut self, comparator: u64) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_comparator_enable(false);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
self.regs.write_comparator_upper((comparator >> 32) as u32);
|
||||||
|
self.regs.write_comparator_lower(comparator as u32);
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_comparator_enable(true);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frequency_to_ticks(&self, frequency: Hertz) -> u32 {
|
||||||
|
if self.cpu_3x2x_clock.is_none() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
frequency_to_ticks(self.cpu_3x2x_clock.unwrap(), frequency)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the auto-increment value which will be used by the hardware to automatically
|
||||||
|
/// increment the comparator value on a comparator interrupt, if the auto-increment is enabled.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_auto_increment_value(&mut self, value: u32) {
|
||||||
|
self.regs.write_auto_increment(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_auto_increment_value_for_frequency(&mut self, frequency: Hertz) {
|
||||||
|
self.regs
|
||||||
|
.write_auto_increment(self.frequency_to_ticks(frequency));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_enable(true);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_auto_increment(&mut self) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_auto_increment(true);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_prescaler(&mut self, prescaler: u8) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_prescaler(prescaler);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_enable(false);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the comparator interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_interrupt(&mut self) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_irq_enable(true);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the comparator interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_interrupt(&mut self) {
|
||||||
|
self.regs.modify_ctrl(|mut ctrl| {
|
||||||
|
ctrl.set_irq_enable(false);
|
||||||
|
ctrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GTC can be used for blocking delays.
|
||||||
|
impl embedded_hal::delay::DelayNs for Gtc {
|
||||||
|
fn delay_ns(&mut self, ns: u32) {
|
||||||
|
if self.cpu_3x2x_clock.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let end_of_delay = self.read_timer()
|
||||||
|
+ (((ns as u64) * self.cpu_3x2x_clock.unwrap().raw() as u64) / 1_000_000_000);
|
||||||
|
while self.read_timer() < end_of_delay {}
|
||||||
|
}
|
||||||
|
}
|
711
zynq7000-hal/src/i2c.rs
Normal file
711
zynq7000-hal/src/i2c.rs
Normal file
@ -0,0 +1,711 @@
|
|||||||
|
use arbitrary_int::{u2, u3, u6};
|
||||||
|
use embedded_hal::i2c::NoAcknowledgeSource;
|
||||||
|
use zynq7000::{
|
||||||
|
i2c::{Control, I2C_0_BASE_ADDR, I2C_1_BASE_ADDR, InterruptStatus, MmioI2c, TransferSize},
|
||||||
|
slcr::reset::DualClockReset,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
use crate::gpio::mio::{
|
||||||
|
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
|
||||||
|
Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
enable_amba_peripheral_clock,
|
||||||
|
gpio::{
|
||||||
|
IoPeriphPin,
|
||||||
|
mio::{
|
||||||
|
Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33,
|
||||||
|
Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Mio52, Mio53, MioPinMarker,
|
||||||
|
MuxConf, Pin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slcr::Slcr,
|
||||||
|
time::Hertz,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const I2C_MUX_CONF: MuxConf = MuxConf::new_with_l3(u3::new(0b010));
|
||||||
|
pub const FIFO_DEPTH: usize = 16;
|
||||||
|
/// Maximum read size in one read operation.
|
||||||
|
pub const MAX_READ_SIZE: usize = 255;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum I2cId {
|
||||||
|
I2c0 = 0,
|
||||||
|
I2c1 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PsI2c {
|
||||||
|
fn reg_block(&self) -> MmioI2c<'static>;
|
||||||
|
fn id(&self) -> Option<I2cId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PsI2c for MmioI2c<'static> {
|
||||||
|
#[inline]
|
||||||
|
fn reg_block(&self) -> MmioI2c<'static> {
|
||||||
|
unsafe { self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> Option<I2cId> {
|
||||||
|
let base_addr = unsafe { self.ptr() } as usize;
|
||||||
|
if base_addr == I2C_0_BASE_ADDR {
|
||||||
|
return Some(I2cId::I2c0);
|
||||||
|
} else if base_addr == I2C_1_BASE_ADDR {
|
||||||
|
return Some(I2cId::I2c1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SdaPin: MioPinMarker {
|
||||||
|
const ID: I2cId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SckPin: MioPinMarker {
|
||||||
|
const ID: I2cId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait I2cPins {}
|
||||||
|
|
||||||
|
macro_rules! i2c_pin_impls {
|
||||||
|
($Id: path, $SckMio:ident, $SdaMio:ident) => {
|
||||||
|
impl SckPin for Pin<$SckMio> {
|
||||||
|
const ID: I2cId = $Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdaPin for Pin<$SdaMio> {
|
||||||
|
const ID: I2cId = $Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2cPins for (Pin<$SckMio>, Pin<$SdaMio>) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
macro_rules! into_i2c {
|
||||||
|
($($Mio:ident),+) => {
|
||||||
|
$(
|
||||||
|
impl <M: PinMode> MioPin<$Mio, M> {
|
||||||
|
/// Convert the pin into I2C pins by configuring the pin routing via the
|
||||||
|
/// MIO multiplexer bits. Also enables pull-ups for the pins.
|
||||||
|
pub fn into_i2c(self) -> MioPin<$Mio, IoPeriph> {
|
||||||
|
// Enable pull-ups for the I2C pins.
|
||||||
|
self.into_io_periph(I2C_MUX_CONF, Some(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
into_i2c!(
|
||||||
|
Mio10, Mio11, Mio14, Mio15, Mio30, Mio31, Mio34, Mio35, Mio38, Mio39, Mio12, Mio13, Mio28,
|
||||||
|
Mio29, Mio32, Mio33, Mio36, Mio37, Mio48, Mio49, Mio52, Mio53
|
||||||
|
);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
into_i2c!(
|
||||||
|
Mio18, Mio19, Mio22, Mio23, Mio26, Mio27, Mio42, Mio43, Mio46, Mio47, Mio50, Mio51, Mio16,
|
||||||
|
Mio17, Mio20, Mio21, Mio24, Mio25, Mio40, Mio41, Mio44, Mio45
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio10, Mio11);
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio14, Mio15);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio18, Mio19);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio22, Mio23);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio26, Mio27);
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio30, Mio31);
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio34, Mio35);
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio38, Mio39);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio42, Mio43);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio46, Mio47);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c0, Mio50, Mio51);
|
||||||
|
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio12, Mio13);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio16, Mio17);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio20, Mio21);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio24, Mio25);
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio28, Mio29);
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio32, Mio33);
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio36, Mio37);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio40, Mio41);
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio44, Mio45);
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio48, Mio49);
|
||||||
|
i2c_pin_impls!(I2cId::I2c1, Mio52, Mio53);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum I2cSpeed {
|
||||||
|
Normal100kHz,
|
||||||
|
HighSpeed400KHz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2cSpeed {
|
||||||
|
pub fn frequency_full_number(&self) -> Hertz {
|
||||||
|
Hertz::from_raw(match self {
|
||||||
|
I2cSpeed::Normal100kHz => 100_000,
|
||||||
|
I2cSpeed::HighSpeed400KHz => 400_000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// From Xilinx embeddedsw
|
||||||
|
/// If frequency 400KHz is selected, 384.6KHz should be set.
|
||||||
|
/// If frequency 100KHz is selected, 90KHz should be set.
|
||||||
|
/// This is due to a hardware limitation.
|
||||||
|
pub fn frequency_for_calculation(&self) -> Hertz {
|
||||||
|
Hertz::from_raw(match self {
|
||||||
|
I2cSpeed::Normal100kHz => 90_000,
|
||||||
|
I2cSpeed::HighSpeed400KHz => 384_600,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("I2C speed not attainable")]
|
||||||
|
pub struct I2cSpeedNotAttainable;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum I2cTxError {
|
||||||
|
#[error("arbitration lost")]
|
||||||
|
ArbitrationLoss,
|
||||||
|
#[error("transfer not acknowledged: {0}")]
|
||||||
|
Nack(NoAcknowledgeSource),
|
||||||
|
#[error("TX overflow")]
|
||||||
|
TxOverflow,
|
||||||
|
#[error("timeout of transfer")]
|
||||||
|
Timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum I2cRxError {
|
||||||
|
#[error("arbitration lost")]
|
||||||
|
ArbitrationLoss,
|
||||||
|
#[error("transfer not acknowledged")]
|
||||||
|
Nack(NoAcknowledgeSource),
|
||||||
|
#[error("RX underflow")]
|
||||||
|
RxUnderflow,
|
||||||
|
#[error("RX overflow")]
|
||||||
|
RxOverflow,
|
||||||
|
#[error("timeout of transfer")]
|
||||||
|
Timeout,
|
||||||
|
#[error("read data exceeds maximum allowed 255 bytes per transfer")]
|
||||||
|
ReadDataLenTooLarge,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum I2cError {
|
||||||
|
#[error("arbitration lost")]
|
||||||
|
ArbitrationLoss,
|
||||||
|
#[error("transfer not acknowledged: {0}")]
|
||||||
|
Nack(NoAcknowledgeSource),
|
||||||
|
#[error("TX overflow")]
|
||||||
|
TxOverflow,
|
||||||
|
#[error("RX underflow")]
|
||||||
|
RxUnderflow,
|
||||||
|
#[error("RX overflow")]
|
||||||
|
RxOverflow,
|
||||||
|
#[error("timeout of transfer")]
|
||||||
|
Timeout,
|
||||||
|
#[error("read data exceeds maximum allowed 255 bytes per transfer")]
|
||||||
|
ReadDataLenTooLarge,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<I2cRxError> for I2cError {
|
||||||
|
fn from(err: I2cRxError) -> Self {
|
||||||
|
match err {
|
||||||
|
I2cRxError::ArbitrationLoss => I2cError::ArbitrationLoss,
|
||||||
|
I2cRxError::Nack(nack) => I2cError::Nack(nack),
|
||||||
|
I2cRxError::RxUnderflow => I2cError::RxUnderflow,
|
||||||
|
I2cRxError::RxOverflow => I2cError::RxOverflow,
|
||||||
|
I2cRxError::Timeout => I2cError::Timeout,
|
||||||
|
I2cRxError::ReadDataLenTooLarge => I2cError::ReadDataLenTooLarge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<I2cTxError> for I2cError {
|
||||||
|
fn from(err: I2cTxError) -> Self {
|
||||||
|
match err {
|
||||||
|
I2cTxError::ArbitrationLoss => I2cError::ArbitrationLoss,
|
||||||
|
I2cTxError::Nack(nack) => I2cError::Nack(nack),
|
||||||
|
I2cTxError::TxOverflow => I2cError::TxOverflow,
|
||||||
|
I2cTxError::Timeout => I2cError::Timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn calculate_i2c_speed(cpu_1x_clk: Hertz, clk_config: ClockConfig) -> Hertz {
|
||||||
|
cpu_1x_clk / (22 * (clk_config.div_a as u32 + 1) * (clk_config.div_b as u32 + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_divisors(
|
||||||
|
cpu_1x_clk: Hertz,
|
||||||
|
speed: I2cSpeed,
|
||||||
|
) -> Result<ClockConfig, I2cSpeedNotAttainable> {
|
||||||
|
let target_speed = speed.frequency_for_calculation();
|
||||||
|
if cpu_1x_clk > 22 * 64 * 4 * target_speed {
|
||||||
|
return Err(I2cSpeedNotAttainable);
|
||||||
|
}
|
||||||
|
let mut smallest_deviation = u32::MAX;
|
||||||
|
let mut best_div_a = 1;
|
||||||
|
let mut best_div_b = 1;
|
||||||
|
for divisor_a in 1..=4 {
|
||||||
|
for divisor_b in 1..=64 {
|
||||||
|
let i2c_clock = cpu_1x_clk / (22 * divisor_a * divisor_b);
|
||||||
|
let deviation = (target_speed.raw() as i32 - i2c_clock.raw() as i32).unsigned_abs();
|
||||||
|
if deviation < smallest_deviation {
|
||||||
|
smallest_deviation = deviation;
|
||||||
|
best_div_a = divisor_a;
|
||||||
|
best_div_b = divisor_b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ClockConfig::new(best_div_a as u8 - 1, best_div_b as u8 - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct ClockConfig {
|
||||||
|
div_a: u8,
|
||||||
|
div_b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockConfig {
|
||||||
|
pub fn new(div_a: u8, div_b: u8) -> Self {
|
||||||
|
Self { div_a, div_b }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div_a(&self) -> u8 {
|
||||||
|
self.div_a
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn div_b(&self) -> u8 {
|
||||||
|
self.div_b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("invalid I2C ID")]
|
||||||
|
pub struct InvalidPsI2cError;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum I2cConstructionError {
|
||||||
|
#[error("invalid I2C ID {0}")]
|
||||||
|
InvalidPsI2c(#[from] InvalidPsI2cError),
|
||||||
|
#[error("pin invalid for I2C ID")]
|
||||||
|
PinInvalidForI2cId,
|
||||||
|
#[error("invalid pin configuration for I2C")]
|
||||||
|
InvalidPinConf,
|
||||||
|
}
|
||||||
|
pub struct I2c {
|
||||||
|
regs: MmioI2c<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2c {
|
||||||
|
pub fn new_with_mio<Sck: SckPin, Sda: SdaPin>(
|
||||||
|
i2c: impl PsI2c,
|
||||||
|
clk_cfg: ClockConfig,
|
||||||
|
i2c_pins: (Sck, Sda),
|
||||||
|
) -> Result<Self, I2cConstructionError> {
|
||||||
|
if i2c.id().is_none() {
|
||||||
|
return Err(InvalidPsI2cError.into());
|
||||||
|
}
|
||||||
|
if Sck::ID != Sda::ID {
|
||||||
|
return Err(I2cConstructionError::PinInvalidForI2cId);
|
||||||
|
}
|
||||||
|
IoPeriphPin::new(i2c_pins.0, I2C_MUX_CONF, Some(true));
|
||||||
|
IoPeriphPin::new(i2c_pins.1, I2C_MUX_CONF, Some(true));
|
||||||
|
Ok(Self::new_generic(
|
||||||
|
i2c.id().unwrap(),
|
||||||
|
i2c.reg_block(),
|
||||||
|
clk_cfg,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_emio(i2c: impl PsI2c, clk_cfg: ClockConfig) -> Result<Self, InvalidPsI2cError> {
|
||||||
|
if i2c.id().is_none() {
|
||||||
|
return Err(InvalidPsI2cError);
|
||||||
|
}
|
||||||
|
Ok(Self::new_generic(
|
||||||
|
i2c.id().unwrap(),
|
||||||
|
i2c.reg_block(),
|
||||||
|
clk_cfg,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_generic(id: I2cId, mut regs: MmioI2c<'static>, clk_cfg: ClockConfig) -> Self {
|
||||||
|
let periph_sel = match id {
|
||||||
|
I2cId::I2c0 => crate::PeripheralSelect::I2c0,
|
||||||
|
I2cId::I2c1 => crate::PeripheralSelect::I2c1,
|
||||||
|
};
|
||||||
|
enable_amba_peripheral_clock(periph_sel);
|
||||||
|
//reset(id);
|
||||||
|
regs.write_cr(
|
||||||
|
Control::builder()
|
||||||
|
.with_div_a(u2::new(clk_cfg.div_a()))
|
||||||
|
.with_div_b(u6::new(clk_cfg.div_b()))
|
||||||
|
.with_clear_fifo(true)
|
||||||
|
.with_slv_mon(false)
|
||||||
|
.with_hold_bus(false)
|
||||||
|
.with_acken(false)
|
||||||
|
.with_addressing(true)
|
||||||
|
.with_mode(zynq7000::i2c::Mode::Master)
|
||||||
|
.with_dir(zynq7000::i2c::Direction::Transmitter)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
Self { regs }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start the transfer by writing the I2C address.
|
||||||
|
#[inline]
|
||||||
|
fn start_transfer(&mut self, address: u8) {
|
||||||
|
self.regs
|
||||||
|
.write_addr(zynq7000::i2c::Addr::new_with_raw_value(address as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_hold_bit(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_hold_bus(true);
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_hold_bit(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_hold_bus(false);
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_transfer_blocking(
|
||||||
|
&mut self,
|
||||||
|
addr: u8,
|
||||||
|
data: &[u8],
|
||||||
|
generate_stop: bool,
|
||||||
|
) -> Result<(), I2cTxError> {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_acken(true);
|
||||||
|
cr.set_mode(zynq7000::i2c::Mode::Master);
|
||||||
|
cr.set_clear_fifo(true);
|
||||||
|
cr.set_dir(zynq7000::i2c::Direction::Transmitter);
|
||||||
|
if !generate_stop {
|
||||||
|
cr.set_hold_bus(true);
|
||||||
|
}
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
let mut first_write_cycle = true;
|
||||||
|
let mut addr_set = false;
|
||||||
|
let mut written = 0;
|
||||||
|
// Clear the interrupt status register before using it to monitor the transfer.
|
||||||
|
self.regs.modify_isr(|isr| isr);
|
||||||
|
loop {
|
||||||
|
let bytes_to_write = core::cmp::min(
|
||||||
|
FIFO_DEPTH - self.regs.read_transfer_size().size() as usize,
|
||||||
|
data.len() - written,
|
||||||
|
);
|
||||||
|
(0..bytes_to_write).for_each(|_| {
|
||||||
|
self.regs
|
||||||
|
.write_data(zynq7000::i2c::Fifo::new_with_raw_value(
|
||||||
|
data[written] as u32,
|
||||||
|
));
|
||||||
|
written += 1;
|
||||||
|
});
|
||||||
|
if !addr_set {
|
||||||
|
self.start_transfer(addr);
|
||||||
|
addr_set = true;
|
||||||
|
}
|
||||||
|
let mut status = self.regs.read_sr();
|
||||||
|
// While the hardware is busy sending out data, we poll for errors.
|
||||||
|
while status.tx_busy() {
|
||||||
|
let isr = self.regs.read_isr();
|
||||||
|
self.check_and_handle_tx_errors(isr, first_write_cycle, bytes_to_write)?;
|
||||||
|
// Re-read for next check.
|
||||||
|
status = self.regs.read_sr();
|
||||||
|
}
|
||||||
|
first_write_cycle = false;
|
||||||
|
// Just need to poll to completion now.
|
||||||
|
if written == data.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Poll to completion.
|
||||||
|
while !self.regs.read_isr().complete() {
|
||||||
|
let isr = self.regs.read_isr();
|
||||||
|
self.check_and_handle_tx_errors(isr, first_write_cycle, data.len())?;
|
||||||
|
}
|
||||||
|
if generate_stop {
|
||||||
|
self.clear_hold_bit();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_and_handle_tx_errors(
|
||||||
|
&mut self,
|
||||||
|
isr: InterruptStatus,
|
||||||
|
first_write_cycle: bool,
|
||||||
|
first_chunk_len: usize,
|
||||||
|
) -> Result<(), I2cTxError> {
|
||||||
|
if isr.tx_overflow() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cTxError::TxOverflow);
|
||||||
|
}
|
||||||
|
if isr.arbitration_lost() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cTxError::ArbitrationLoss);
|
||||||
|
}
|
||||||
|
if isr.nack() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
// I have no tested this yet, but if no data was sent yet, this is probably
|
||||||
|
// an address NACK.
|
||||||
|
if first_write_cycle
|
||||||
|
&& self.regs.read_transfer_size().size() as usize + 1 == first_chunk_len
|
||||||
|
{
|
||||||
|
return Err(I2cTxError::Nack(NoAcknowledgeSource::Address));
|
||||||
|
} else {
|
||||||
|
return Err(I2cTxError::Nack(NoAcknowledgeSource::Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isr.timeout() {
|
||||||
|
// Timeout / Stall condition.
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cTxError::Timeout);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean_up_after_transfer_or_on_error(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_acken(false);
|
||||||
|
cr.set_clear_fifo(true);
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_transfer_blocking(&mut self, addr: u8, data: &mut [u8]) -> Result<(), I2cRxError> {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_acken(true);
|
||||||
|
cr.set_mode(zynq7000::i2c::Mode::Master);
|
||||||
|
cr.set_clear_fifo(true);
|
||||||
|
cr.set_dir(zynq7000::i2c::Direction::Receiver);
|
||||||
|
if data.len() > FIFO_DEPTH {
|
||||||
|
cr.set_hold_bus(true);
|
||||||
|
}
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
let mut read = 0;
|
||||||
|
if data.len() > MAX_READ_SIZE {
|
||||||
|
return Err(I2cRxError::ReadDataLenTooLarge);
|
||||||
|
}
|
||||||
|
// Clear the interrupt status register before using it to monitor the transfer.
|
||||||
|
self.regs.modify_isr(|isr| isr);
|
||||||
|
self.regs
|
||||||
|
.write_transfer_size(TransferSize::new_with_raw_value(data.len() as u32));
|
||||||
|
self.start_transfer(addr);
|
||||||
|
loop {
|
||||||
|
let mut status = self.regs.read_sr();
|
||||||
|
loop {
|
||||||
|
let isr = self.regs.read_isr();
|
||||||
|
self.check_and_handle_rx_errors(read, isr)?;
|
||||||
|
if status.rx_valid() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Re-read for next check.
|
||||||
|
status = self.regs.read_sr();
|
||||||
|
}
|
||||||
|
// Data to be read.
|
||||||
|
while self.regs.read_sr().rx_valid() {
|
||||||
|
data[read] = self.regs.read_data().data();
|
||||||
|
read += 1;
|
||||||
|
}
|
||||||
|
// The outstading read size is smaller than the FIFO. Clear the HOLD register as
|
||||||
|
// specified in TRM p.649 polled read step 6.
|
||||||
|
if self.regs.read_transfer_size().size() as usize <= FIFO_DEPTH {
|
||||||
|
self.clear_hold_bit();
|
||||||
|
}
|
||||||
|
// Read everything, just need to poll to completion now.
|
||||||
|
if read == data.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll to completion.
|
||||||
|
while !self.regs.read_isr().complete() {
|
||||||
|
let isr = self.regs.read_isr();
|
||||||
|
self.check_and_handle_rx_errors(read, isr)?
|
||||||
|
}
|
||||||
|
self.clear_hold_bit();
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_and_handle_rx_errors(
|
||||||
|
&mut self,
|
||||||
|
read_count: usize,
|
||||||
|
isr: InterruptStatus,
|
||||||
|
) -> Result<(), I2cRxError> {
|
||||||
|
if isr.rx_overflow() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cRxError::RxOverflow);
|
||||||
|
}
|
||||||
|
if isr.rx_underflow() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cRxError::RxUnderflow);
|
||||||
|
}
|
||||||
|
if isr.nack() {
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
// I have no tested this yet, but if no data was sent yet, this is probably
|
||||||
|
// an address NACK.
|
||||||
|
if read_count == 0 {
|
||||||
|
return Err(I2cRxError::Nack(NoAcknowledgeSource::Address));
|
||||||
|
} else {
|
||||||
|
return Err(I2cRxError::Nack(NoAcknowledgeSource::Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isr.timeout() {
|
||||||
|
// Timeout / Stall condition.
|
||||||
|
self.clean_up_after_transfer_or_on_error();
|
||||||
|
return Err(I2cRxError::Timeout);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::ErrorType for I2c {
|
||||||
|
type Error = I2cError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::Error for I2cError {
|
||||||
|
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||||
|
match self {
|
||||||
|
I2cError::ArbitrationLoss => embedded_hal::i2c::ErrorKind::ArbitrationLoss,
|
||||||
|
I2cError::Nack(nack_kind) => embedded_hal::i2c::ErrorKind::NoAcknowledge(*nack_kind),
|
||||||
|
I2cError::RxOverflow => embedded_hal::i2c::ErrorKind::Overrun,
|
||||||
|
I2cError::TxOverflow => embedded_hal::i2c::ErrorKind::Other,
|
||||||
|
I2cError::RxUnderflow => embedded_hal::i2c::ErrorKind::Other,
|
||||||
|
I2cError::Timeout | I2cError::ReadDataLenTooLarge => {
|
||||||
|
embedded_hal::i2c::ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::i2c::I2c for I2c {
|
||||||
|
fn transaction(
|
||||||
|
&mut self,
|
||||||
|
address: u8,
|
||||||
|
operations: &mut [embedded_hal::i2c::Operation<'_>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
for op in operations {
|
||||||
|
match op {
|
||||||
|
embedded_hal::i2c::Operation::Read(items) => {
|
||||||
|
self.read_transfer_blocking(address, items)?
|
||||||
|
}
|
||||||
|
embedded_hal::i2c::Operation::Write(items) => {
|
||||||
|
self.write_transfer_blocking(address, items, true)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_read(
|
||||||
|
&mut self,
|
||||||
|
address: u8,
|
||||||
|
write: &[u8],
|
||||||
|
read: &mut [u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
// I have never tested this, so I am not sure whether the master still generates a stop
|
||||||
|
// condition somehow.. which might break the trait contract.
|
||||||
|
self.write_transfer_blocking(address, write, false)?;
|
||||||
|
Ok(self.read_transfer_blocking(address, read)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the SPI peripheral using the SLCR reset register for SPI.
|
||||||
|
///
|
||||||
|
/// Please note that this function will interfere with an already configured
|
||||||
|
/// SPI instance.
|
||||||
|
#[inline]
|
||||||
|
pub fn reset(id: I2cId) {
|
||||||
|
let assert_reset = match id {
|
||||||
|
I2cId::I2c0 => DualClockReset::builder()
|
||||||
|
.with_periph1_cpu1x_rst(false)
|
||||||
|
.with_periph0_cpu1x_rst(true)
|
||||||
|
.build(),
|
||||||
|
I2cId::I2c1 => DualClockReset::builder()
|
||||||
|
.with_periph1_cpu1x_rst(true)
|
||||||
|
.with_periph0_cpu1x_rst(false)
|
||||||
|
.build(),
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.reset_ctrl().write_i2c(assert_reset);
|
||||||
|
// Keep it in reset for some cycles.. The TMR just mentions some small delay,
|
||||||
|
// no idea what is meant with that.
|
||||||
|
for _ in 0..3 {
|
||||||
|
cortex_ar::asm::nop();
|
||||||
|
}
|
||||||
|
regs.reset_ctrl().write_i2c(DualClockReset::DEFAULT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
extern crate std;
|
||||||
|
use super::*;
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
use std::println;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_test() {
|
||||||
|
let clk_cfg = calculate_divisors(111.MHz(), I2cSpeed::Normal100kHz).unwrap();
|
||||||
|
assert_eq!(clk_cfg.div_a(), 0);
|
||||||
|
assert_eq!(clk_cfg.div_b(), 55);
|
||||||
|
let speed = calculate_i2c_speed(111.MHz(), clk_cfg);
|
||||||
|
assert!(speed.raw() < 100_000);
|
||||||
|
assert!(speed.raw() > 85_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_test_2() {
|
||||||
|
let clk_cfg = calculate_divisors(111.MHz(), I2cSpeed::HighSpeed400KHz).unwrap();
|
||||||
|
assert_eq!(clk_cfg.div_a(), 0);
|
||||||
|
assert_eq!(clk_cfg.div_b(), 12);
|
||||||
|
let speed = calculate_i2c_speed(111.MHz(), clk_cfg);
|
||||||
|
assert!(speed.raw() < 400_000);
|
||||||
|
assert!(speed.raw() > 360_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_test_3() {
|
||||||
|
let clk_cfg = calculate_divisors(133.MHz(), I2cSpeed::Normal100kHz).unwrap();
|
||||||
|
assert_eq!(clk_cfg.div_a(), 1);
|
||||||
|
assert_eq!(clk_cfg.div_b(), 33);
|
||||||
|
let speed = calculate_i2c_speed(133.MHz(), clk_cfg);
|
||||||
|
assert!(speed.raw() < 100_000);
|
||||||
|
assert!(speed.raw() > 85_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_test_4() {
|
||||||
|
let clk_cfg = calculate_divisors(133.MHz(), I2cSpeed::HighSpeed400KHz).unwrap();
|
||||||
|
assert_eq!(clk_cfg.div_a(), 0);
|
||||||
|
assert_eq!(clk_cfg.div_b(), 15);
|
||||||
|
let speed = calculate_i2c_speed(133.MHz(), clk_cfg);
|
||||||
|
assert!(speed.raw() < 400_000);
|
||||||
|
assert!(speed.raw() > 360_000);
|
||||||
|
}
|
||||||
|
}
|
204
zynq7000-hal/src/lib.rs
Normal file
204
zynq7000-hal/src/lib.rs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
//! # HAL for the AMD Zynq 7000 SoC family
|
||||||
|
//!
|
||||||
|
//! This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
|
||||||
|
//! hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq7000).
|
||||||
|
//!
|
||||||
|
//! It is the result of reading the datasheet for the device and encoding a type-safe layer over the
|
||||||
|
//! raw PAC. This crate also implements traits specified by the
|
||||||
|
//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||||
|
//! various drivers in the embedded rust ecosystem.
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use slcr::Slcr;
|
||||||
|
use zynq7000::slcr::LevelShifterReg;
|
||||||
|
|
||||||
|
pub mod clocks;
|
||||||
|
pub mod gic;
|
||||||
|
pub mod gpio;
|
||||||
|
pub mod gtc;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod log;
|
||||||
|
pub mod prelude;
|
||||||
|
pub mod slcr;
|
||||||
|
pub mod spi;
|
||||||
|
pub mod time;
|
||||||
|
pub mod ttc;
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
|
/// This enumeration encodes the various boot sources.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum BootDevice {
|
||||||
|
JtagCascaded,
|
||||||
|
JtagIndependent,
|
||||||
|
Nor,
|
||||||
|
Nand,
|
||||||
|
Qspi,
|
||||||
|
SdCard,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum BootPllConfig {
|
||||||
|
Enabled,
|
||||||
|
Bypassed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BootMode {
|
||||||
|
boot_mode: Option<BootDevice>,
|
||||||
|
pll_config: BootPllConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootMode {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
/// Create a new boot mode information structure by reading the boot mode register from the
|
||||||
|
/// fixed SLCR block.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
// Safety: Only read a read-only register here.
|
||||||
|
Self::new_with_raw_reg(
|
||||||
|
unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() }
|
||||||
|
.read_boot_mode()
|
||||||
|
.raw_value(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_with_raw_reg(raw_register: u32) -> Self {
|
||||||
|
let msb_three_bits = (raw_register >> 1) & 0b111;
|
||||||
|
|
||||||
|
let boot_mode = match msb_three_bits {
|
||||||
|
0b000 => {
|
||||||
|
if raw_register & 0b1 == 0 {
|
||||||
|
Some(BootDevice::JtagCascaded)
|
||||||
|
} else {
|
||||||
|
Some(BootDevice::JtagIndependent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0b001 => Some(BootDevice::Nor),
|
||||||
|
0b010 => Some(BootDevice::Nand),
|
||||||
|
0b100 => Some(BootDevice::Qspi),
|
||||||
|
0b110 => Some(BootDevice::SdCard),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let pll_config = if (raw_register >> 4) & 0b1 == 0 {
|
||||||
|
BootPllConfig::Enabled
|
||||||
|
} else {
|
||||||
|
BootPllConfig::Bypassed
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
boot_mode,
|
||||||
|
pll_config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn boot_device(&self) -> Option<BootDevice> {
|
||||||
|
self.boot_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn pll_enable(&self) -> BootPllConfig {
|
||||||
|
self.pll_config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This configures the level shifters between the programmable logic (PL) and the processing
|
||||||
|
/// system (PS).
|
||||||
|
///
|
||||||
|
/// The Zynq-7000 TRM p.32 specifies more information about this register and how to use it.
|
||||||
|
pub fn configure_level_shifter(config: zynq7000::slcr::LevelShifterConfig) {
|
||||||
|
// Safety: We only manipulate the level shift registers.
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|slcr_unlocked| {
|
||||||
|
slcr_unlocked.write_lvl_shftr_en(LevelShifterReg::new_with_raw_value(config as u32));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum PeripheralSelect {
|
||||||
|
Smc = 24,
|
||||||
|
Lqspi = 23,
|
||||||
|
Gpio = 22,
|
||||||
|
Uart1 = 21,
|
||||||
|
Uart0 = 20,
|
||||||
|
I2c1 = 19,
|
||||||
|
I2c0 = 18,
|
||||||
|
Can1 = 17,
|
||||||
|
Can0 = 16,
|
||||||
|
Spi1 = 15,
|
||||||
|
Spi0 = 14,
|
||||||
|
Sdio1 = 11,
|
||||||
|
Sdio0 = 10,
|
||||||
|
Gem1 = 7,
|
||||||
|
Gem0 = 6,
|
||||||
|
Usb1 = 3,
|
||||||
|
Usb0 = 2,
|
||||||
|
Dma = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the AMBA peripheral clock, which is required to read the registers of a peripheral
|
||||||
|
/// block.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_amba_peripheral_clock(select: PeripheralSelect) {
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.clk_ctrl().modify_aper_clk_ctrl(|mut val| {
|
||||||
|
match select {
|
||||||
|
PeripheralSelect::Smc => val.set_smc_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Lqspi => val.set_lqspi_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Gpio => val.set_gpio_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Uart1 => val.set_uart_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Uart0 => val.set_uart_0_1x_clk_act(true),
|
||||||
|
PeripheralSelect::I2c1 => val.set_i2c_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::I2c0 => val.set_i2c_0_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Can1 => val.set_can_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Can0 => val.set_can_0_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Spi1 => val.set_spi_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Spi0 => val.set_spi_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Sdio1 => val.set_sdio_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Sdio0 => val.set_sdio_0_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Gem1 => val.set_gem_1_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Gem0 => val.set_gem_0_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Usb1 => val.set_usb_1_cpu_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Usb0 => val.set_usb_0_cpu_1x_clk_act(true),
|
||||||
|
PeripheralSelect::Dma => val.set_dma_cpu_2x_clk_act(true),
|
||||||
|
}
|
||||||
|
val
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the AMBA peripheral clock, which is required to read the registers of a peripheral
|
||||||
|
/// block.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_amba_peripheral_clock(select: PeripheralSelect) {
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.clk_ctrl().modify_aper_clk_ctrl(|mut val| {
|
||||||
|
match select {
|
||||||
|
PeripheralSelect::Smc => val.set_smc_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Lqspi => val.set_lqspi_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Gpio => val.set_gpio_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Uart1 => val.set_uart_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Uart0 => val.set_uart_0_1x_clk_act(false),
|
||||||
|
PeripheralSelect::I2c1 => val.set_i2c_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::I2c0 => val.set_i2c_0_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Can1 => val.set_can_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Can0 => val.set_can_0_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Spi1 => val.set_spi_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Spi0 => val.set_spi_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Sdio1 => val.set_sdio_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Sdio0 => val.set_sdio_0_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Gem1 => val.set_gem_1_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Gem0 => val.set_gem_0_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Usb1 => val.set_usb_1_cpu_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Usb0 => val.set_usb_0_cpu_1x_clk_act(false),
|
||||||
|
PeripheralSelect::Dma => val.set_dma_cpu_2x_clk_act(false),
|
||||||
|
}
|
||||||
|
val
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
230
zynq7000-hal/src/log.rs
Normal file
230
zynq7000-hal/src/log.rs
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
//! # Simple logging providers.
|
||||||
|
|
||||||
|
/// Blocking UART loggers.
|
||||||
|
pub mod uart_blocking {
|
||||||
|
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||||
|
use embedded_io::Write as _;
|
||||||
|
|
||||||
|
use cortex_ar::register::Cpsr;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use log::{LevelFilter, set_logger, set_max_level};
|
||||||
|
|
||||||
|
use crate::uart::Uart;
|
||||||
|
|
||||||
|
pub struct UartLoggerBlocking(Mutex<RefCell<Option<Uart>>>);
|
||||||
|
|
||||||
|
unsafe impl Send for UartLoggerBlocking {}
|
||||||
|
unsafe impl Sync for UartLoggerBlocking {}
|
||||||
|
|
||||||
|
static UART_LOGGER_BLOCKING: UartLoggerBlocking =
|
||||||
|
UartLoggerBlocking(Mutex::new(RefCell::new(None)));
|
||||||
|
|
||||||
|
/// Initialize the logger with a blocking UART instance.
|
||||||
|
///
|
||||||
|
/// This is a blocking logger which performs a write inside a critical section. This logger is
|
||||||
|
/// thread-safe, but interrupts will be disabled while the logger is writing to the UART.
|
||||||
|
///
|
||||||
|
/// For async applications, it is strongly recommended to use the asynchronous ring buffer
|
||||||
|
/// logger instead.
|
||||||
|
pub fn init_with_locks(uart: Uart, level: LevelFilter) {
|
||||||
|
// TODO: Impl debug for Uart
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let inner = UART_LOGGER_BLOCKING.0.borrow(cs);
|
||||||
|
inner.replace(Some(uart));
|
||||||
|
});
|
||||||
|
set_logger(&UART_LOGGER_BLOCKING).unwrap();
|
||||||
|
// Adjust as needed
|
||||||
|
set_max_level(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl log::Log for UartLoggerBlocking {
|
||||||
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut opt_logger = self.0.borrow(cs).borrow_mut();
|
||||||
|
if opt_logger.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let logger = opt_logger.as_mut().unwrap();
|
||||||
|
writeln!(logger, "{} - {}\r", record.level(), record.args()).unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UartLoggerUnsafeSingleThread {
|
||||||
|
skip_in_isr: Cell<bool>,
|
||||||
|
uart: UnsafeCell<Option<Uart>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for UartLoggerUnsafeSingleThread {}
|
||||||
|
unsafe impl Sync for UartLoggerUnsafeSingleThread {}
|
||||||
|
|
||||||
|
static UART_LOGGER_UNSAFE_SINGLE_THREAD: UartLoggerUnsafeSingleThread =
|
||||||
|
UartLoggerUnsafeSingleThread {
|
||||||
|
skip_in_isr: Cell::new(false),
|
||||||
|
uart: UnsafeCell::new(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Initialize the logger with a blocking UART instance.
|
||||||
|
///
|
||||||
|
/// For async applications, it is strongly recommended to use the asynchronous ring buffer
|
||||||
|
/// logger instead.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This is a blocking logger which performs a write WITHOUT a critical section. This logger is
|
||||||
|
/// NOT thread-safe. Users must ensure that this logger is not used inside a pre-emptive
|
||||||
|
/// multi-threading context and interrupt handlers.
|
||||||
|
pub unsafe fn create_unsafe_single_thread_logger(uart: Uart) -> UartLoggerUnsafeSingleThread {
|
||||||
|
UartLoggerUnsafeSingleThread {
|
||||||
|
skip_in_isr: Cell::new(false),
|
||||||
|
uart: UnsafeCell::new(Some(uart)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the logger with a blocking UART instance which does not use locks.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This is a blocking logger which performs a write WITHOUT a critical section. This logger is
|
||||||
|
/// NOT thread-safe, which might lead to garbled output. Log output in ISRs can optionally be
|
||||||
|
/// surpressed.
|
||||||
|
pub unsafe fn init_unsafe_single_core(uart: Uart, level: LevelFilter, skip_in_isr: bool) {
|
||||||
|
let opt_uart = unsafe { &mut *UART_LOGGER_UNSAFE_SINGLE_THREAD.uart.get() };
|
||||||
|
opt_uart.replace(uart);
|
||||||
|
UART_LOGGER_UNSAFE_SINGLE_THREAD
|
||||||
|
.skip_in_isr
|
||||||
|
.set(skip_in_isr);
|
||||||
|
|
||||||
|
set_logger(&UART_LOGGER_UNSAFE_SINGLE_THREAD).unwrap();
|
||||||
|
set_max_level(level); // Adjust as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
impl log::Log for UartLoggerUnsafeSingleThread {
|
||||||
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
if self.skip_in_isr.get() {
|
||||||
|
match Cpsr::read().mode().unwrap() {
|
||||||
|
cortex_ar::register::cpsr::ProcessorMode::Fiq
|
||||||
|
| cortex_ar::register::cpsr::ProcessorMode::Irq => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uart_mut = unsafe { &mut *self.uart.get() }.as_mut();
|
||||||
|
if uart_mut.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeln!(
|
||||||
|
uart_mut.unwrap(),
|
||||||
|
"{} - {}\r",
|
||||||
|
record.level(),
|
||||||
|
record.args()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logger module which logs into a ring buffer to allow asynchronous logging handling.
|
||||||
|
pub mod rb {
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use core::fmt::Write as _;
|
||||||
|
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use log::{LevelFilter, set_logger, set_max_level};
|
||||||
|
use ringbuf::{
|
||||||
|
StaticRb,
|
||||||
|
traits::{Consumer, Producer},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Logger implementation which logs frames via a ring buffer and sends the frame sizes
|
||||||
|
/// as messages.
|
||||||
|
///
|
||||||
|
/// The logger does not require allocation and reserved a generous amount of 4096 bytes for
|
||||||
|
/// both data buffer and ring buffer. This should be sufficient for most logging needs.
|
||||||
|
pub struct Logger {
|
||||||
|
frame_queue: embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32>,
|
||||||
|
data_buf: critical_section::Mutex<RefCell<heapless::String<4096>>>,
|
||||||
|
ring_buf: critical_section::Mutex<RefCell<Option<StaticRb<u8, 4096>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Logger {}
|
||||||
|
unsafe impl Sync for Logger {}
|
||||||
|
|
||||||
|
static LOGGER_RB: Logger = Logger {
|
||||||
|
frame_queue: embassy_sync::channel::Channel::new(),
|
||||||
|
data_buf: critical_section::Mutex::new(RefCell::new(heapless::String::new())),
|
||||||
|
ring_buf: critical_section::Mutex::new(RefCell::new(None)),
|
||||||
|
};
|
||||||
|
|
||||||
|
impl log::Log for Logger {
|
||||||
|
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let ref_buf = self.data_buf.borrow(cs);
|
||||||
|
let mut buf = ref_buf.borrow_mut();
|
||||||
|
buf.clear();
|
||||||
|
let _ = writeln!(buf, "{} - {}\r", record.level(), record.args());
|
||||||
|
let rb_ref = self.ring_buf.borrow(cs);
|
||||||
|
let mut rb_opt = rb_ref.borrow_mut();
|
||||||
|
if rb_opt.is_none() {
|
||||||
|
panic!("log call on uninitialized logger");
|
||||||
|
}
|
||||||
|
rb_opt.as_mut().unwrap().push_slice(buf.as_bytes());
|
||||||
|
let _ = self.frame_queue.try_send(buf.len());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {
|
||||||
|
while !self.frame_queue().is_empty() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger {
|
||||||
|
pub fn frame_queue(
|
||||||
|
&self,
|
||||||
|
) -> &embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
|
||||||
|
&self.frame_queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(level: LevelFilter) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let rb = StaticRb::<u8, 4096>::default();
|
||||||
|
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
|
||||||
|
rb_ref.borrow_mut().replace(rb);
|
||||||
|
});
|
||||||
|
set_logger(&LOGGER_RB).unwrap();
|
||||||
|
set_max_level(level); // Adjust as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_next_frame(frame_len: usize, buf: &mut [u8]) {
|
||||||
|
let read_len = core::cmp::min(frame_len, buf.len());
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
|
||||||
|
let mut rb = rb_ref.borrow_mut();
|
||||||
|
rb.as_mut().unwrap().pop_slice(&mut buf[0..read_len]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_frame_queue()
|
||||||
|
-> &'static embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
|
||||||
|
LOGGER_RB.frame_queue()
|
||||||
|
}
|
||||||
|
}
|
3
zynq7000-hal/src/prelude.rs
Normal file
3
zynq7000-hal/src/prelude.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Prelude
|
||||||
|
pub use fugit::ExtU32 as _;
|
||||||
|
pub use fugit::RateExtU32 as _;
|
67
zynq7000-hal/src/slcr.rs
Normal file
67
zynq7000-hal/src/slcr.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
//! # System Level Control Register (SLCR) module.
|
||||||
|
use zynq7000::slcr::MmioSlcr;
|
||||||
|
|
||||||
|
pub const LOCK_KEY: u32 = 0x767B;
|
||||||
|
pub const UNLOCK_KEY: u32 = 0xDF0D;
|
||||||
|
|
||||||
|
pub struct Slcr(zynq7000::slcr::MmioSlcr<'static>);
|
||||||
|
|
||||||
|
impl Slcr {
|
||||||
|
/// Modify the SLCR register.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This method unsafely steals the SLCR MMIO block and then calls a user provided function
|
||||||
|
/// with the [SLCR MMIO][MmioSlcr] block as an input argument. It is the user's responsibility
|
||||||
|
/// that the SLCR is not used concurrently in a way which leads to data races.
|
||||||
|
pub unsafe fn with<F: FnMut(&mut MmioSlcr)>(mut f: F) {
|
||||||
|
let mut slcr = unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() };
|
||||||
|
slcr.write_unlock(UNLOCK_KEY);
|
||||||
|
f(&mut slcr);
|
||||||
|
slcr.write_lock(LOCK_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new SLCR peripheral wrapper.
|
||||||
|
pub fn new(slcr: zynq7000::slcr::MmioSlcr<'static>) -> Self {
|
||||||
|
Self(slcr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafely create a new SLCR peripheral wrapper.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This allows to create an arbitrary number of SLCR peripheral wrappers. It is the user's
|
||||||
|
/// responsibility that these wrappers are not used concurrently in a way which leads to
|
||||||
|
/// data races.
|
||||||
|
pub unsafe fn steal() -> Self {
|
||||||
|
Self::new(unsafe { zynq7000::slcr::Slcr::new_mmio_fixed() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the SLCR MMIO block.
|
||||||
|
///
|
||||||
|
/// The MMIO block will not be unlocked. However, the registers can still be read.
|
||||||
|
pub fn regs(&mut self) -> &mut MmioSlcr<'static> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modify the SLCR register.
|
||||||
|
///
|
||||||
|
/// This method unlocks the SLCR registers and then calls a user provided function
|
||||||
|
/// with the [SLCR MMIO][MmioSlcr] block as an input argument. This allows the user
|
||||||
|
/// to safely modify the SLCR registers. The SLCR will be locked afte the operation.
|
||||||
|
pub fn modify<F: FnMut(&mut MmioSlcr)>(&mut self, mut f: F) {
|
||||||
|
self.0.write_unlock(UNLOCK_KEY);
|
||||||
|
f(&mut self.0);
|
||||||
|
self.0.write_lock(LOCK_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manually unlock the SLCR registers.
|
||||||
|
pub fn unlock(&mut self) {
|
||||||
|
self.0.write_unlock(UNLOCK_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manually lock the SLCR registers.
|
||||||
|
pub fn lock(&mut self) {
|
||||||
|
self.0.write_lock(LOCK_KEY);
|
||||||
|
}
|
||||||
|
}
|
584
zynq7000-hal/src/spi/asynch.rs
Normal file
584
zynq7000-hal/src/spi/asynch.rs
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
//! Asynchronous PS SPI driver.
|
||||||
|
use core::{cell::RefCell, convert::Infallible, sync::atomic::AtomicBool};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_hal_async::spi::SpiBus;
|
||||||
|
use raw_slice::{RawBufSlice, RawBufSliceMut};
|
||||||
|
use zynq7000::spi::InterruptStatus;
|
||||||
|
|
||||||
|
use super::{ChipSelect, FIFO_DEPTH, Spi, SpiId, SpiLowLevel};
|
||||||
|
|
||||||
|
static WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
|
static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; 2] =
|
||||||
|
[const { Mutex::new(RefCell::new(TransferContext::new())) }; 2];
|
||||||
|
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
||||||
|
// critical section.
|
||||||
|
static DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
|
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
||||||
|
/// SPI peripheral.
|
||||||
|
///
|
||||||
|
/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
|
||||||
|
/// the given SPI bank.
|
||||||
|
pub fn on_interrupt(peripheral: SpiId) {
|
||||||
|
let mut spi = unsafe { SpiLowLevel::steal(peripheral) };
|
||||||
|
let idx = peripheral as usize;
|
||||||
|
let imr = spi.read_imr();
|
||||||
|
// IRQ is not related.
|
||||||
|
if !imr.tx_trig() && !imr.tx_full() && !imr.tx_underflow() && !imr.rx_ovr() && !imr.rx_full() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Prevent spurious interrupts from messing with out logic here.
|
||||||
|
spi.disable_interrupts();
|
||||||
|
let isr = spi.read_isr();
|
||||||
|
spi.clear_interrupts();
|
||||||
|
let mut context = critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow()
|
||||||
|
});
|
||||||
|
// No transfer active.
|
||||||
|
if context.transfer_type.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let transfer_type = context.transfer_type.unwrap();
|
||||||
|
match transfer_type {
|
||||||
|
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
|
||||||
|
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
|
||||||
|
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
|
||||||
|
TransferType::TransferInPlace => {
|
||||||
|
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_read(
|
||||||
|
idx: usize,
|
||||||
|
context: &mut TransferContext,
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
mut isr: InterruptStatus,
|
||||||
|
) {
|
||||||
|
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
|
let transfer_len = read_slice.len();
|
||||||
|
|
||||||
|
// Read data from RX FIFO first.
|
||||||
|
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||||
|
(0..read_len).for_each(|_| {
|
||||||
|
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
|
||||||
|
context.rx_progress += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// The FIFO still needs to be pumped.
|
||||||
|
while context.tx_progress < read_slice.len() && !isr.tx_full() {
|
||||||
|
spi.write_fifo_unchecked(0);
|
||||||
|
context.tx_progress += 1;
|
||||||
|
isr = spi.read_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
isr_finish_handler(idx, spi, context, transfer_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_write(
|
||||||
|
idx: usize,
|
||||||
|
context: &mut TransferContext,
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
mut isr: InterruptStatus,
|
||||||
|
) {
|
||||||
|
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||||
|
let transfer_len = write_slice.len();
|
||||||
|
|
||||||
|
// Read data from RX FIFO first.
|
||||||
|
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||||
|
(0..read_len).for_each(|_| {
|
||||||
|
spi.read_fifo_unchecked();
|
||||||
|
context.rx_progress += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Data still needs to be sent
|
||||||
|
while context.tx_progress < transfer_len && !isr.tx_full() {
|
||||||
|
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
|
||||||
|
context.tx_progress += 1;
|
||||||
|
isr = spi.read_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
isr_finish_handler(idx, spi, context, transfer_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_transfer(
|
||||||
|
idx: usize,
|
||||||
|
context: &mut TransferContext,
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
mut isr: InterruptStatus,
|
||||||
|
) {
|
||||||
|
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
|
let read_len = read_slice.len();
|
||||||
|
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||||
|
let write_len = write_slice.len();
|
||||||
|
let transfer_len = core::cmp::max(read_len, write_len);
|
||||||
|
|
||||||
|
// Read data from RX FIFO first.
|
||||||
|
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||||
|
(0..read_len).for_each(|_| {
|
||||||
|
if context.rx_progress < read_len {
|
||||||
|
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
|
||||||
|
} else {
|
||||||
|
spi.read_fifo_unchecked();
|
||||||
|
}
|
||||||
|
context.rx_progress += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Data still needs to be sent
|
||||||
|
while context.tx_progress < transfer_len && !isr.tx_full() {
|
||||||
|
if context.tx_progress < write_len {
|
||||||
|
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
|
||||||
|
} else {
|
||||||
|
// Dummy write.
|
||||||
|
spi.write_fifo_unchecked(0);
|
||||||
|
}
|
||||||
|
context.tx_progress += 1;
|
||||||
|
isr = spi.read_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
isr_finish_handler(idx, spi, context, transfer_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_transfer_in_place(
|
||||||
|
idx: usize,
|
||||||
|
context: &mut TransferContext,
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
mut isr: InterruptStatus,
|
||||||
|
) {
|
||||||
|
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||||
|
let transfer_len = transfer_slice.len();
|
||||||
|
// Read data from RX FIFO first.
|
||||||
|
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
|
||||||
|
(0..read_len).for_each(|_| {
|
||||||
|
transfer_slice[context.rx_progress] = spi.read_fifo_unchecked();
|
||||||
|
context.rx_progress += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Data still needs to be sent
|
||||||
|
while context.tx_progress < transfer_len && !isr.tx_full() {
|
||||||
|
spi.write_fifo_unchecked(transfer_slice[context.tx_progress]);
|
||||||
|
context.tx_progress += 1;
|
||||||
|
isr = spi.read_isr();
|
||||||
|
}
|
||||||
|
|
||||||
|
isr_finish_handler(idx, spi, context, transfer_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_read_len(
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
isr: InterruptStatus,
|
||||||
|
total_read_len: usize,
|
||||||
|
rx_progress: usize,
|
||||||
|
) -> usize {
|
||||||
|
if isr.rx_full() {
|
||||||
|
core::cmp::min(FIFO_DEPTH, total_read_len - rx_progress)
|
||||||
|
} else if isr.rx_not_empty() {
|
||||||
|
let trigger = spi.read_rx_not_empty_threshold();
|
||||||
|
core::cmp::min(total_read_len - rx_progress, trigger as usize)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
||||||
|
/// and unfinished conditions.
|
||||||
|
fn isr_finish_handler(
|
||||||
|
idx: usize,
|
||||||
|
spi: &mut SpiLowLevel,
|
||||||
|
context: &mut TransferContext,
|
||||||
|
transfer_len: usize,
|
||||||
|
) {
|
||||||
|
// Transfer finish condition.
|
||||||
|
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
||||||
|
finish_transfer(idx, context, spi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unfinished_transfer(spi, transfer_len, context.rx_progress);
|
||||||
|
|
||||||
|
// If the transfer is done, the context structure was already written back.
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = *context;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = *context;
|
||||||
|
});
|
||||||
|
spi.set_rx_fifo_trigger(1).unwrap();
|
||||||
|
spi.set_tx_fifo_trigger(1).unwrap();
|
||||||
|
// Interrupts were already disabled and cleared.
|
||||||
|
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
WAKERS[idx].wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, rx_progress: usize) {
|
||||||
|
let new_trig_level = core::cmp::min(FIFO_DEPTH, transfer_len - rx_progress);
|
||||||
|
spi.set_rx_fifo_trigger(new_trig_level as u32).unwrap();
|
||||||
|
// Re-enable interrupts with the new RX FIFO trigger level.
|
||||||
|
spi.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum TransferType {
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
Transfer,
|
||||||
|
TransferInPlace,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
|
pub struct TransferContext {
|
||||||
|
transfer_type: Option<TransferType>,
|
||||||
|
tx_progress: usize,
|
||||||
|
rx_progress: usize,
|
||||||
|
tx_slice: RawBufSlice,
|
||||||
|
rx_slice: RawBufSliceMut,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl TransferContext {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
transfer_type: None,
|
||||||
|
tx_progress: 0,
|
||||||
|
rx_progress: 0,
|
||||||
|
tx_slice: RawBufSlice::new_nulled(),
|
||||||
|
rx_slice: RawBufSliceMut::new_nulled(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SpiFuture {
|
||||||
|
id: super::SpiId,
|
||||||
|
spi: super::SpiLowLevel,
|
||||||
|
config: super::Config,
|
||||||
|
finished_regularly: core::cell::Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpiFuture {
|
||||||
|
fn new_for_read(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
|
||||||
|
if words.is_empty() {
|
||||||
|
panic!("words length unexpectedly 0");
|
||||||
|
}
|
||||||
|
let idx = spi_id as usize;
|
||||||
|
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
spi.inner.disable_interrupts();
|
||||||
|
|
||||||
|
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
|
||||||
|
// Send dummy bytes.
|
||||||
|
(0..write_idx).for_each(|_| {
|
||||||
|
spi.inner.write_fifo_unchecked(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::set_triggers(spi, write_idx, words.len());
|
||||||
|
// We assume that the slave select configuration was already performed, but we take
|
||||||
|
// care of issuing a start if necessary.
|
||||||
|
spi.issue_manual_start_for_manual_cfg();
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
context.transfer_type = Some(TransferType::Read);
|
||||||
|
unsafe {
|
||||||
|
context.rx_slice.set(words);
|
||||||
|
}
|
||||||
|
context.tx_slice.set_null();
|
||||||
|
context.tx_progress = write_idx;
|
||||||
|
context.rx_progress = 0;
|
||||||
|
spi.inner.clear_interrupts();
|
||||||
|
spi.inner.enable_interrupts();
|
||||||
|
spi.inner.enable();
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
id: spi_id,
|
||||||
|
config: spi.config,
|
||||||
|
spi: unsafe { spi.inner.clone() },
|
||||||
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_for_write(spi: &mut Spi, spi_id: SpiId, words: &[u8]) -> Self {
|
||||||
|
if words.is_empty() {
|
||||||
|
panic!("words length unexpectedly 0");
|
||||||
|
}
|
||||||
|
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
context.transfer_type = Some(TransferType::Write);
|
||||||
|
unsafe {
|
||||||
|
context.tx_slice.set(words);
|
||||||
|
}
|
||||||
|
context.rx_slice.set_null();
|
||||||
|
context.tx_progress = write_idx;
|
||||||
|
context.rx_progress = 0;
|
||||||
|
spi.inner.clear_interrupts();
|
||||||
|
spi.inner.enable_interrupts();
|
||||||
|
spi.inner.enable();
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
id: spi_id,
|
||||||
|
config: spi.config,
|
||||||
|
spi: unsafe { spi.inner.clone() },
|
||||||
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_for_transfer(spi: &mut Spi, spi_id: SpiId, read: &mut [u8], write: &[u8]) -> Self {
|
||||||
|
if read.is_empty() || write.is_empty() {
|
||||||
|
panic!("read or write buffer unexpectedly empty");
|
||||||
|
}
|
||||||
|
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
context.transfer_type = Some(TransferType::Transfer);
|
||||||
|
unsafe {
|
||||||
|
context.tx_slice.set(write);
|
||||||
|
context.rx_slice.set(read);
|
||||||
|
}
|
||||||
|
context.tx_progress = write_idx;
|
||||||
|
context.rx_progress = 0;
|
||||||
|
spi.inner.clear_interrupts();
|
||||||
|
spi.inner.enable_interrupts();
|
||||||
|
spi.inner.enable();
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
id: spi_id,
|
||||||
|
config: spi.config,
|
||||||
|
spi: unsafe { spi.inner.clone() },
|
||||||
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_for_transfer_in_place(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
|
||||||
|
if words.is_empty() {
|
||||||
|
panic!("read and write buffer unexpectedly empty");
|
||||||
|
}
|
||||||
|
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
context.transfer_type = Some(TransferType::TransferInPlace);
|
||||||
|
unsafe {
|
||||||
|
context.rx_slice.set(words);
|
||||||
|
}
|
||||||
|
context.tx_slice.set_null();
|
||||||
|
context.tx_progress = write_idx;
|
||||||
|
context.rx_progress = 0;
|
||||||
|
spi.inner.clear_interrupts();
|
||||||
|
spi.inner.enable_interrupts();
|
||||||
|
spi.inner.enable();
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
id: spi_id,
|
||||||
|
config: spi.config,
|
||||||
|
spi: unsafe { spi.inner.clone() },
|
||||||
|
finished_regularly: core::cell::Cell::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generic_init_transfer(spi: &mut Spi, spi_id: SpiId, write: &[u8]) -> (usize, usize) {
|
||||||
|
let idx = spi_id as usize;
|
||||||
|
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
spi.inner.disable();
|
||||||
|
spi.inner.disable_interrupts();
|
||||||
|
|
||||||
|
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
||||||
|
(0..write_idx).for_each(|idx| {
|
||||||
|
spi.inner.write_fifo_unchecked(write[idx]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self::set_triggers(spi, write_idx, write.len());
|
||||||
|
// We assume that the slave select configuration was already performed, but we take
|
||||||
|
// care of issuing a start if necessary.
|
||||||
|
spi.issue_manual_start_for_manual_cfg();
|
||||||
|
(idx, write_idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_triggers(spi: &mut Spi, write_idx: usize, write_len: usize) {
|
||||||
|
// This should never fail because it is never larger than the FIFO depth.
|
||||||
|
spi.inner.set_rx_fifo_trigger(write_idx as u32).unwrap();
|
||||||
|
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
||||||
|
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
|
||||||
|
// this because the TMR says that this interrupt is triggered when the FIFO has less than
|
||||||
|
// threshold entries.
|
||||||
|
if write_len > super::FIFO_DEPTH {
|
||||||
|
spi.inner.set_tx_fifo_trigger(2).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for SpiFuture {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
WAKERS[self.id as usize].register(cx.waker());
|
||||||
|
if DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut ctx = TRANSFER_CONTEXTS[self.id as usize].borrow(cs).borrow_mut();
|
||||||
|
*ctx = TransferContext::default();
|
||||||
|
});
|
||||||
|
self.finished_regularly.set(true);
|
||||||
|
return core::task::Poll::Ready(());
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SpiFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.finished_regularly.get() {
|
||||||
|
// It might be sufficient to disable and enable the SPI.. But this definitely
|
||||||
|
// ensures the SPI is fully reset.
|
||||||
|
self.spi.reset_and_reconfigure(self.config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronous SPI driver.
|
||||||
|
///
|
||||||
|
/// This is the primary data structure used to perform non-blocking SPI operations.
|
||||||
|
/// It implements the [embedded_hal_async::spi::SpiBus] as well.
|
||||||
|
pub struct SpiAsync(pub Spi);
|
||||||
|
|
||||||
|
impl SpiAsync {
|
||||||
|
pub fn new(spi: Spi) -> Self {
|
||||||
|
Self(spi)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self, words: &mut [u8]) {
|
||||||
|
if words.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let id = self.0.inner.id;
|
||||||
|
let spi_fut = SpiFuture::new_for_read(&mut self.0, id, words);
|
||||||
|
spi_fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write(&mut self, words: &[u8]) {
|
||||||
|
if words.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let id = self.0.inner.id;
|
||||||
|
let spi_fut = SpiFuture::new_for_write(&mut self.0, id, words);
|
||||||
|
spi_fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
|
||||||
|
if read.is_empty() || write.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let id = self.0.inner.id;
|
||||||
|
let spi_fut = SpiFuture::new_for_transfer(&mut self.0, id, read, write);
|
||||||
|
spi_fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transfer_in_place(&mut self, words: &mut [u8]) {
|
||||||
|
if words.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let id = self.0.inner.id;
|
||||||
|
let spi_fut = SpiFuture::new_for_transfer_in_place(&mut self.0, id, words);
|
||||||
|
spi_fut.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
||||||
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.read(words).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.write(words).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.transfer(read, write).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.transfer_in_place(words).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This structure is a wrapper for [SpiAsync] which implements the
|
||||||
|
/// [embedded_hal_async::spi::SpiDevice] trait as well.
|
||||||
|
pub struct SpiWithHwCsAsync<Delay: embedded_hal_async::delay::DelayNs> {
|
||||||
|
pub spi: SpiAsync,
|
||||||
|
pub cs: ChipSelect,
|
||||||
|
pub delay: Delay,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Delay: embedded_hal_async::delay::DelayNs> SpiWithHwCsAsync<Delay> {
|
||||||
|
pub fn new(spi: SpiAsync, cs: ChipSelect, delay: Delay) -> Self {
|
||||||
|
Self { spi, cs, delay }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> SpiAsync {
|
||||||
|
self.spi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::ErrorType
|
||||||
|
for SpiWithHwCsAsync<Delay>
|
||||||
|
{
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::SpiDevice
|
||||||
|
for SpiWithHwCsAsync<Delay>
|
||||||
|
{
|
||||||
|
async fn transaction(
|
||||||
|
&mut self,
|
||||||
|
operations: &mut [embedded_hal::spi::Operation<'_, u8>],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.spi.0.inner.select_hw_cs(self.cs);
|
||||||
|
for op in operations {
|
||||||
|
match op {
|
||||||
|
embedded_hal::spi::Operation::Read(items) => {
|
||||||
|
self.spi.read(items).await;
|
||||||
|
}
|
||||||
|
embedded_hal::spi::Operation::Write(items) => {
|
||||||
|
self.spi.write(items).await;
|
||||||
|
}
|
||||||
|
embedded_hal::spi::Operation::Transfer(read, write) => {
|
||||||
|
self.spi.transfer(read, write).await;
|
||||||
|
}
|
||||||
|
embedded_hal::spi::Operation::TransferInPlace(items) => {
|
||||||
|
self.spi.transfer_in_place(items).await;
|
||||||
|
}
|
||||||
|
embedded_hal::spi::Operation::DelayNs(delay) => {
|
||||||
|
self.delay.delay_ns(*delay).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.spi.flush().await?;
|
||||||
|
self.spi.0.inner.no_hw_cs();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
1213
zynq7000-hal/src/spi/mod.rs
Normal file
1213
zynq7000-hal/src/spi/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
26
zynq7000-hal/src/time.rs
Normal file
26
zynq7000-hal/src/time.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//! Time units
|
||||||
|
|
||||||
|
// Frequency based
|
||||||
|
|
||||||
|
/// Hertz
|
||||||
|
pub type Hertz = fugit::HertzU32;
|
||||||
|
|
||||||
|
/// KiloHertz
|
||||||
|
pub type KiloHertz = fugit::KilohertzU32;
|
||||||
|
|
||||||
|
/// MegaHertz
|
||||||
|
pub type MegaHertz = fugit::MegahertzU32;
|
||||||
|
|
||||||
|
// Period based
|
||||||
|
|
||||||
|
/// Seconds
|
||||||
|
pub type Seconds = fugit::SecsDurationU32;
|
||||||
|
|
||||||
|
/// Milliseconds
|
||||||
|
pub type Milliseconds = fugit::MillisDurationU32;
|
||||||
|
|
||||||
|
/// Microseconds
|
||||||
|
pub type Microseconds = fugit::MicrosDurationU32;
|
||||||
|
|
||||||
|
/// Nanoseconds
|
||||||
|
pub type Nanoseconds = fugit::NanosDurationU32;
|
376
zynq7000-hal/src/ttc.rs
Normal file
376
zynq7000-hal/src/ttc.rs
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
//! Triple-timer counter (TTC) high-level driver.
|
||||||
|
//!
|
||||||
|
//! This module also contains support for PWM and output waveform generation.
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
use arbitrary_int::{Number, u3, u4};
|
||||||
|
use zynq7000::ttc::{MmioTtc, TTC_0_BASE_ADDR, TTC_1_BASE_ADDR};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
use crate::gpio::mio::{Mio16, Mio17, Mio18, Mio19, Mio40, Mio41, Mio42, Mio43};
|
||||||
|
use crate::{
|
||||||
|
clocks::ArmClocks,
|
||||||
|
gpio::{
|
||||||
|
IoPeriphPin,
|
||||||
|
mio::{Mio28, Mio29, Mio30, Mio31, MioPinMarker, MuxConf, Pin},
|
||||||
|
},
|
||||||
|
time::Hertz,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Each TTC consists of three independent timers/counters.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum TtcId {
|
||||||
|
Ttc0 = 0,
|
||||||
|
Ttc1 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum ChannelId {
|
||||||
|
Ch0 = 0,
|
||||||
|
Ch1 = 1,
|
||||||
|
Ch2 = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PsTtc {
|
||||||
|
fn reg_block(&self) -> MmioTtc<'static>;
|
||||||
|
fn id(&self) -> Option<TtcId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PsTtc for MmioTtc<'static> {
|
||||||
|
#[inline]
|
||||||
|
fn reg_block(&self) -> MmioTtc<'static> {
|
||||||
|
unsafe { self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> Option<TtcId> {
|
||||||
|
let base_addr = unsafe { self.ptr() } as usize;
|
||||||
|
if base_addr == TTC_0_BASE_ADDR {
|
||||||
|
return Some(TtcId::Ttc0);
|
||||||
|
} else if base_addr == TTC_1_BASE_ADDR {
|
||||||
|
return Some(TtcId::Ttc1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TTC_MUX_CONF: MuxConf = MuxConf::new_with_l3(u3::new(0b110));
|
||||||
|
|
||||||
|
pub trait ClockInPin: MioPinMarker {
|
||||||
|
const ID: TtcId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WaveOutPin: MioPinMarker {
|
||||||
|
const ID: TtcId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTC0 pin trait implementations.
|
||||||
|
|
||||||
|
impl ClockInPin for Pin<Mio19> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl ClockInPin for Pin<Mio31> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl ClockInPin for Pin<Mio43> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaveOutPin for Pin<Mio18> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl WaveOutPin for Pin<Mio30> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl WaveOutPin for Pin<Mio42> {
|
||||||
|
const ID: TtcId = TtcId::Ttc0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTC1 pin trait implementations.
|
||||||
|
|
||||||
|
impl ClockInPin for Pin<Mio17> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl ClockInPin for Pin<Mio29> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl ClockInPin for Pin<Mio41> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaveOutPin for Pin<Mio16> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl WaveOutPin for Pin<Mio28> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl WaveOutPin for Pin<Mio40> {
|
||||||
|
const ID: TtcId = TtcId::Ttc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ttc {
|
||||||
|
pub ch0: TtcChannel,
|
||||||
|
pub ch1: TtcChannel,
|
||||||
|
pub ch2: TtcChannel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ttc {
|
||||||
|
/// Create a new TTC instance. The passed TTC peripheral instance MUST point to a valid
|
||||||
|
/// processing system TTC peripheral.
|
||||||
|
///
|
||||||
|
/// Returns [None] if the passed peripheral block does not have a valid PS TTC address.
|
||||||
|
pub fn new(ps_ttc: impl PsTtc) -> Option<Self> {
|
||||||
|
ps_ttc.id()?;
|
||||||
|
let regs = ps_ttc.reg_block();
|
||||||
|
let ch0 = TtcChannel {
|
||||||
|
regs: unsafe { regs.clone() },
|
||||||
|
id: ChannelId::Ch0,
|
||||||
|
};
|
||||||
|
let ch1 = TtcChannel {
|
||||||
|
regs: unsafe { regs.clone() },
|
||||||
|
id: ChannelId::Ch1,
|
||||||
|
};
|
||||||
|
let ch2 = TtcChannel {
|
||||||
|
regs,
|
||||||
|
id: ChannelId::Ch2,
|
||||||
|
};
|
||||||
|
Some(Self { ch0, ch1, ch2 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TtcChannel {
|
||||||
|
regs: MmioTtc<'static>,
|
||||||
|
id: ChannelId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TtcChannel {
|
||||||
|
pub fn regs_mut(&mut self) -> &mut MmioTtc<'static> {
|
||||||
|
&mut self.regs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read_counter(&self) -> u16 {
|
||||||
|
self.regs
|
||||||
|
.read_current_counter(self.id as usize)
|
||||||
|
.unwrap()
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> ChannelId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("invalid TTC pin configuration")]
|
||||||
|
pub struct InvalidTtcPinConfigError(pub MuxConf);
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("frequency is zero")]
|
||||||
|
pub struct FrequencyIsZeroError;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum TtcConstructionError {
|
||||||
|
#[error("invalid TTC pin configuration")]
|
||||||
|
InvalidTtcPinConfig(#[from] InvalidTtcPinConfigError),
|
||||||
|
#[error("frequency is zero")]
|
||||||
|
FrequencyIsZero(#[from] FrequencyIsZeroError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_prescaler_reg_and_interval_ticks(
|
||||||
|
mut ref_clk: Hertz,
|
||||||
|
freq: Hertz,
|
||||||
|
) -> (Option<u4>, u16) {
|
||||||
|
// TODO: Can this be optimized?
|
||||||
|
let mut prescaler_reg: Option<u4> = None;
|
||||||
|
let mut tick_val = ref_clk / freq;
|
||||||
|
while tick_val > u16::MAX as u32 {
|
||||||
|
ref_clk /= 2;
|
||||||
|
if let Some(prescaler_reg) = prescaler_reg {
|
||||||
|
// TODO: Better error handling for this case? Can this even happen?
|
||||||
|
if prescaler_reg.value() == u4::MAX.value() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
prescaler_reg.checked_add(u4::new(1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
prescaler_reg = Some(u4::new(0));
|
||||||
|
}
|
||||||
|
tick_val = ref_clk / freq;
|
||||||
|
}
|
||||||
|
(prescaler_reg, tick_val as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pwm {
|
||||||
|
channel: TtcChannel,
|
||||||
|
ref_clk: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pwm {
|
||||||
|
/// Create a new PWM instance which uses the CPU 1x clock as the clock source and also uses
|
||||||
|
/// a MIO output pin for the waveform output.
|
||||||
|
pub fn new_with_cpu_clk_and_mio_waveout(
|
||||||
|
channel: TtcChannel,
|
||||||
|
arm_clocks: &ArmClocks,
|
||||||
|
freq: Hertz,
|
||||||
|
wave_out: impl WaveOutPin,
|
||||||
|
) -> Result<Self, TtcConstructionError> {
|
||||||
|
IoPeriphPin::new(wave_out, TTC_MUX_CONF, None);
|
||||||
|
Ok(Self::new_with_cpu_clk(channel, arm_clocks, freq)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new PWM instance which uses the CPU 1x clock as the clock source.
|
||||||
|
pub fn new_with_cpu_clk(
|
||||||
|
channel: TtcChannel,
|
||||||
|
arm_clocks: &ArmClocks,
|
||||||
|
freq: Hertz,
|
||||||
|
) -> Result<Self, FrequencyIsZeroError> {
|
||||||
|
Self::new_generic(channel, arm_clocks.cpu_1x_clk(), freq)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new PWM instance based on a reference clock source.
|
||||||
|
pub fn new_generic(
|
||||||
|
channel: TtcChannel,
|
||||||
|
ref_clk: Hertz,
|
||||||
|
freq: Hertz,
|
||||||
|
) -> Result<Self, FrequencyIsZeroError> {
|
||||||
|
if freq.raw() == 0 {
|
||||||
|
return Err(FrequencyIsZeroError);
|
||||||
|
}
|
||||||
|
let (prescaler_reg, tick_val) = calculate_prescaler_reg_and_interval_ticks(ref_clk, freq);
|
||||||
|
let id = channel.id() as usize;
|
||||||
|
let mut pwm = Self { channel, ref_clk };
|
||||||
|
pwm.set_up_and_configure_pwm(id, prescaler_reg, tick_val);
|
||||||
|
Ok(pwm)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a new frequency for the PWM cycle.
|
||||||
|
///
|
||||||
|
/// This resets the duty cycle to 0%.
|
||||||
|
pub fn set_frequency(&mut self, freq: Hertz) -> Result<(), FrequencyIsZeroError> {
|
||||||
|
if freq.raw() == 0 {
|
||||||
|
return Err(FrequencyIsZeroError);
|
||||||
|
}
|
||||||
|
let id = self.channel.id() as usize;
|
||||||
|
let (prescaler_reg, tick_val) =
|
||||||
|
calculate_prescaler_reg_and_interval_ticks(self.ref_clk, freq);
|
||||||
|
self.set_up_and_configure_pwm(id, prescaler_reg, tick_val);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ttc_channel_mut(&mut self) -> &mut TtcChannel {
|
||||||
|
&mut self.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn max_duty_cycle(&self) -> u16 {
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.read_interval_value(self.channel.id() as usize)
|
||||||
|
.unwrap()
|
||||||
|
.value()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_duty_cycle(&mut self, duty: u16) {
|
||||||
|
let id = self.channel.id() as usize;
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.modify_cnt_ctrl(id, |mut val| {
|
||||||
|
val.set_disable(true);
|
||||||
|
val
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_match_value_0(
|
||||||
|
self.channel.id() as usize,
|
||||||
|
zynq7000::ttc::RwValue::new_with_raw_value(duty as u32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.modify_cnt_ctrl(id, |mut val| {
|
||||||
|
val.set_disable(false);
|
||||||
|
val.set_reset(true);
|
||||||
|
val
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_up_and_configure_pwm(&mut self, id: usize, prescaler_reg: Option<u4>, tick_val: u16) {
|
||||||
|
// Disable the counter first.
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_cnt_ctrl(id, zynq7000::ttc::CounterControl::new_with_raw_value(1))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Clock configuration
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_clk_cntr(
|
||||||
|
id,
|
||||||
|
zynq7000::ttc::ClockControl::builder()
|
||||||
|
.with_ext_clk_edge(false)
|
||||||
|
.with_clk_src(zynq7000::ttc::ClockSource::Pclk)
|
||||||
|
.with_prescaler(prescaler_reg.unwrap_or(u4::new(0)))
|
||||||
|
.with_prescale_enable(prescaler_reg.is_some())
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_interval_value(
|
||||||
|
id,
|
||||||
|
zynq7000::ttc::RwValue::new_with_raw_value(tick_val as u32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
// Corresponds to duty cycle 0.
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_match_value_0(id, zynq7000::ttc::RwValue::new_with_raw_value(0))
|
||||||
|
.unwrap();
|
||||||
|
self.channel
|
||||||
|
.regs
|
||||||
|
.write_cnt_ctrl(
|
||||||
|
id,
|
||||||
|
zynq7000::ttc::CounterControl::builder()
|
||||||
|
.with_wave_polarity(zynq7000::ttc::WavePolarity::LowToHighOnMatch1)
|
||||||
|
.with_wave_enable_n(zynq7000::ttc::WaveEnable::Enable)
|
||||||
|
.with_reset(true)
|
||||||
|
.with_match_enable(true)
|
||||||
|
.with_decrementing(false)
|
||||||
|
.with_mode(zynq7000::ttc::Mode::Interval)
|
||||||
|
.with_disable(false)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::ErrorType for Pwm {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::pwm::SetDutyCycle for Pwm {
|
||||||
|
#[inline]
|
||||||
|
fn max_duty_cycle(&self) -> u16 {
|
||||||
|
self.max_duty_cycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||||
|
self.set_duty_cycle(duty);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
739
zynq7000-hal/src/uart/mod.rs
Normal file
739
zynq7000-hal/src/uart/mod.rs
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
//! # UART module.
|
||||||
|
//!
|
||||||
|
//! Support for the processing system UARTs.
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
use arbitrary_int::u3;
|
||||||
|
use libm::round;
|
||||||
|
use zynq7000::{
|
||||||
|
slcr::reset::DualRefAndClockReset,
|
||||||
|
uart::{
|
||||||
|
BaudRateDiv, Baudgen, ChMode, ClkSel, FifoTrigger, InterruptControl, MmioUart, Mode,
|
||||||
|
UART_0_BASE, UART_1_BASE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
enable_amba_peripheral_clock,
|
||||||
|
gpio::{
|
||||||
|
IoPeriphPin,
|
||||||
|
mio::{
|
||||||
|
Mio8, Mio9, Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31,
|
||||||
|
Mio32, Mio33, Mio34, Mio35, Mio36, Mio37, Mio38, Mio39, Mio48, Mio49, Mio52, Mio53,
|
||||||
|
MioPinMarker, MuxConf, Pin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slcr::Slcr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
use crate::gpio::mio::{
|
||||||
|
Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
|
||||||
|
Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio50, Mio51,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{clocks::IoClocks, time::Hertz};
|
||||||
|
|
||||||
|
pub mod tx;
|
||||||
|
pub use tx::*;
|
||||||
|
|
||||||
|
pub mod tx_async;
|
||||||
|
pub use tx_async::*;
|
||||||
|
|
||||||
|
pub mod rx;
|
||||||
|
pub use rx::*;
|
||||||
|
|
||||||
|
pub const FIFO_DEPTH: usize = 64;
|
||||||
|
pub const DEFAULT_RX_TRIGGER_LEVEL: u8 = 32;
|
||||||
|
pub const UART_MUX_CONF: MuxConf = MuxConf::new_with_l3(u3::new(0b111));
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum UartId {
|
||||||
|
Uart0 = 0,
|
||||||
|
Uart1 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PsUart {
|
||||||
|
fn reg_block(&self) -> MmioUart<'static>;
|
||||||
|
fn uart_id(&self) -> Option<UartId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PsUart for MmioUart<'static> {
|
||||||
|
#[inline]
|
||||||
|
fn reg_block(&self) -> MmioUart<'static> {
|
||||||
|
unsafe { self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uart_id(&self) -> Option<UartId> {
|
||||||
|
let base_addr = unsafe { self.ptr() } as usize;
|
||||||
|
if base_addr == UART_0_BASE {
|
||||||
|
return Some(UartId::Uart0);
|
||||||
|
} else if base_addr == UART_1_BASE {
|
||||||
|
return Some(UartId::Uart1);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UartId {
|
||||||
|
/// Unsafely steal a peripheral MMIO block for the given UART.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents ownership and safety guarantees by the HAL.
|
||||||
|
pub const unsafe fn regs(&self) -> MmioUart<'static> {
|
||||||
|
match self {
|
||||||
|
UartId::Uart0 => unsafe { zynq7000::uart::Uart::new_mmio_fixed_0() },
|
||||||
|
UartId::Uart1 => unsafe { zynq7000::uart::Uart::new_mmio_fixed_1() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RxPin: MioPinMarker {
|
||||||
|
const UART_IDX: UartId;
|
||||||
|
}
|
||||||
|
pub trait TxPin: MioPinMarker {
|
||||||
|
const UART_IDX: UartId;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UartPins {}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("divisor is zero")]
|
||||||
|
pub struct DivisorZero;
|
||||||
|
|
||||||
|
macro_rules! pin_pairs {
|
||||||
|
($UartPeriph:path, ($( [$(#[$meta:meta], )? $TxMio:ident, $RxMio:ident] ),+ $(,)? )) => {
|
||||||
|
$(
|
||||||
|
$( #[$meta] )?
|
||||||
|
impl TxPin for Pin<$TxMio> {
|
||||||
|
const UART_IDX: UartId = $UartPeriph;
|
||||||
|
}
|
||||||
|
|
||||||
|
$( #[$meta] )?
|
||||||
|
impl RxPin for Pin<$RxMio> {
|
||||||
|
const UART_IDX: UartId = $UartPeriph;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UartPins for (Pin<$TxMio>, Pin<$RxMio>) {}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
macro_rules! impl_into_uart {
|
||||||
|
(($($Mio:ident),+)) => {
|
||||||
|
$(
|
||||||
|
impl From<Pin<$Mio>> for IoPeriphPin {
|
||||||
|
fn from(pin: Pin<$Mio>) -> Self {
|
||||||
|
IoPeriphPin::new(pin, UART_MUX_CONF, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_into_uart!((
|
||||||
|
Mio10, Mio11, Mio15, Mio14, Mio31, Mio30, Mio35, Mio34, Mio39, Mio38, Mio8, Mio9, Mio12, Mio13,
|
||||||
|
Mio28, Mio29, Mio32, Mio33, Mio36, Mio37, Mio48, Mio49, Mio52, Mio53
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "7z010-7z007s-clg225"))]
|
||||||
|
impl_into_uart!((
|
||||||
|
Mio19, Mio18, Mio23, Mio22, Mio43, Mio42, Mio47, Mio46, Mio51, Mio50, Mio16, Mio17, Mio20,
|
||||||
|
Mio21, Mio24, Mio25, Mio40, Mio41, Mio44, Mio45
|
||||||
|
));
|
||||||
|
*/
|
||||||
|
|
||||||
|
pin_pairs!(
|
||||||
|
UartId::Uart0,
|
||||||
|
(
|
||||||
|
[Mio11, Mio10],
|
||||||
|
[Mio15, Mio14],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio19, Mio18],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio23, Mio22],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio27, Mio26],
|
||||||
|
[Mio31, Mio30],
|
||||||
|
[Mio35, Mio34],
|
||||||
|
[Mio39, Mio38],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio43, Mio42],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio47, Mio46],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio51, Mio50],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
pin_pairs!(
|
||||||
|
UartId::Uart1,
|
||||||
|
(
|
||||||
|
[Mio8, Mio9],
|
||||||
|
[Mio12, Mio13],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio16, Mio17],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio20, Mio21],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio24, Mio25],
|
||||||
|
[Mio28, Mio29],
|
||||||
|
[Mio32, Mio33],
|
||||||
|
[Mio36, Mio37],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio40, Mio41],
|
||||||
|
[#[cfg(not(feature ="7z010-7z007s-clg225"))], Mio44, Mio45],
|
||||||
|
[Mio48, Mio49],
|
||||||
|
[Mio52, Mio53],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Based on values provided by the vendor library.
|
||||||
|
pub const MAX_BAUD_RATE: u32 = 6240000;
|
||||||
|
/// Based on values provided by the vendor library.
|
||||||
|
pub const MIN_BAUD_RATE: u32 = 110;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub enum Parity {
|
||||||
|
Even,
|
||||||
|
Odd,
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub enum Stopbits {
|
||||||
|
#[default]
|
||||||
|
One,
|
||||||
|
OnePointFive,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub enum CharLen {
|
||||||
|
SixBits,
|
||||||
|
SevenBits,
|
||||||
|
#[default]
|
||||||
|
EightBits,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ClkConfigRaw {
|
||||||
|
cd: u16,
|
||||||
|
bdiv: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub fn calculate_viable_configs(
|
||||||
|
mut uart_clk: Hertz,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
target_baud: u32,
|
||||||
|
) -> alloc::vec::Vec<(ClkConfigRaw, f64)> {
|
||||||
|
let mut viable_cfgs = alloc::vec::Vec::new();
|
||||||
|
if clk_sel == ClkSel::UartRefClkDiv8 {
|
||||||
|
uart_clk /= 8;
|
||||||
|
}
|
||||||
|
let mut current_clk_config = ClkConfigRaw::new(0, 0);
|
||||||
|
for bdiv in 4..u8::MAX {
|
||||||
|
let cd =
|
||||||
|
round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
|
||||||
|
if cd > u16::MAX as u64 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current_clk_config.cd = cd as u16;
|
||||||
|
current_clk_config.bdiv = bdiv;
|
||||||
|
let baud = current_clk_config.actual_baud(uart_clk);
|
||||||
|
let error = ((baud - target_baud as f64).abs() / target_baud as f64) * 100.0;
|
||||||
|
if error < MAX_BAUDERROR_RATE as f64 {
|
||||||
|
viable_cfgs.push((current_clk_config, error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viable_cfgs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the clock configuration for the smallest error to reach the desired target
|
||||||
|
/// baud rate.
|
||||||
|
///
|
||||||
|
/// You can also use [calculate_viable_configs] to get a list of all viable configurations.
|
||||||
|
pub fn calculate_raw_baud_cfg_smallest_error(
|
||||||
|
mut uart_clk: Hertz,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
target_baud: u32,
|
||||||
|
) -> Result<(ClkConfigRaw, f64), DivisorZero> {
|
||||||
|
if target_baud == 0 {
|
||||||
|
return Err(DivisorZero);
|
||||||
|
}
|
||||||
|
if clk_sel == ClkSel::UartRefClkDiv8 {
|
||||||
|
uart_clk /= 8;
|
||||||
|
}
|
||||||
|
let mut current_clk_config = ClkConfigRaw::default();
|
||||||
|
let mut best_clk_config = ClkConfigRaw::default();
|
||||||
|
let mut smallest_error: f64 = 100.0;
|
||||||
|
for bdiv in 4..u8::MAX {
|
||||||
|
let cd =
|
||||||
|
round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
|
||||||
|
if cd > u16::MAX as u64 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current_clk_config.cd = cd as u16;
|
||||||
|
current_clk_config.bdiv = bdiv;
|
||||||
|
let baud = current_clk_config.actual_baud(uart_clk);
|
||||||
|
let error = ((baud - target_baud as f64).abs() / target_baud as f64) * 100.0;
|
||||||
|
if error < smallest_error {
|
||||||
|
best_clk_config = current_clk_config;
|
||||||
|
smallest_error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((best_clk_config, smallest_error))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClkConfigRaw {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(cd: u16, bdiv: u8) -> Result<Self, DivisorZero> {
|
||||||
|
if cd == 0 {
|
||||||
|
return Err(DivisorZero);
|
||||||
|
}
|
||||||
|
Ok(ClkConfigRaw { cd, bdiv })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auto-calculates the best clock configuration settings for the target baudrate.
|
||||||
|
///
|
||||||
|
/// This function assumes [ClkSel::UartRefClk] as the clock source. It returns a tuple
|
||||||
|
/// where the first entry is the clock configuration while the second entry is the associated
|
||||||
|
/// baud error from 0.0 to 1.0. It is recommended to keep this error below 2-3 %.
|
||||||
|
pub fn new_autocalc_with_error(
|
||||||
|
io_clks: &IoClocks,
|
||||||
|
target_baud: u32,
|
||||||
|
) -> Result<(Self, f64), DivisorZero> {
|
||||||
|
Self::new_autocalc_generic(io_clks, ClkSel::UartRefClk, target_baud)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_autocalc_generic(
|
||||||
|
io_clks: &IoClocks,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
target_baud: u32,
|
||||||
|
) -> Result<(Self, f64), DivisorZero> {
|
||||||
|
Self::new_autocalc_with_raw_clk(io_clks.uart_clk(), clk_sel, target_baud)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_autocalc_with_raw_clk(
|
||||||
|
uart_clk: Hertz,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
target_baud: u32,
|
||||||
|
) -> Result<(Self, f64), DivisorZero> {
|
||||||
|
calculate_raw_baud_cfg_smallest_error(uart_clk, clk_sel, target_baud)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn cd(&self) -> u16 {
|
||||||
|
self.cd
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn bdiv(&self) -> u8 {
|
||||||
|
self.bdiv
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn rounded_baud(&self, sel_clk: Hertz) -> u32 {
|
||||||
|
round(self.actual_baud(sel_clk)) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn actual_baud(&self, sel_clk: Hertz) -> f64 {
|
||||||
|
sel_clk.raw() as f64 / (self.cd as f64 * (self.bdiv + 1) as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClkConfigRaw {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
ClkConfigRaw::new(1, 0).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UartConfig {
|
||||||
|
clk_config: ClkConfigRaw,
|
||||||
|
chmode: ChMode,
|
||||||
|
parity: Parity,
|
||||||
|
stopbits: Stopbits,
|
||||||
|
chrl: CharLen,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UartConfig {
|
||||||
|
pub fn new_with_clk_config(clk_config: ClkConfigRaw) -> Self {
|
||||||
|
Self::new(
|
||||||
|
clk_config,
|
||||||
|
ChMode::default(),
|
||||||
|
Parity::default(),
|
||||||
|
Stopbits::default(),
|
||||||
|
CharLen::default(),
|
||||||
|
ClkSel::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(
|
||||||
|
clk_config: ClkConfigRaw,
|
||||||
|
chmode: ChMode,
|
||||||
|
parity: Parity,
|
||||||
|
stopbits: Stopbits,
|
||||||
|
chrl: CharLen,
|
||||||
|
clk_sel: ClkSel,
|
||||||
|
) -> Self {
|
||||||
|
UartConfig {
|
||||||
|
clk_config,
|
||||||
|
chmode,
|
||||||
|
parity,
|
||||||
|
stopbits,
|
||||||
|
chrl,
|
||||||
|
clk_sel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn raw_clk_config(&self) -> ClkConfigRaw {
|
||||||
|
self.clk_config
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn chmode(&self) -> ChMode {
|
||||||
|
self.chmode
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn parity(&self) -> Parity {
|
||||||
|
self.parity
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn stopbits(&self) -> Stopbits {
|
||||||
|
self.stopbits
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn chrl(&self) -> CharLen {
|
||||||
|
self.chrl
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn clksel(&self) -> ClkSel {
|
||||||
|
self.clk_sel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Impl Debug
|
||||||
|
pub struct Uart {
|
||||||
|
rx: Rx,
|
||||||
|
tx: Tx,
|
||||||
|
cfg: UartConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("invalid UART ID")]
|
||||||
|
pub struct InvalidPsUart;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum UartConstructionError {
|
||||||
|
#[error("invalid UART ID")]
|
||||||
|
InvalidPsUart(#[from] InvalidPsUart),
|
||||||
|
#[error("missmatch between pins index and passed index")]
|
||||||
|
IdxMissmatch,
|
||||||
|
#[error("invalid pin mux conf for UART")]
|
||||||
|
InvalidMuxConf(MuxConf),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Uart {
|
||||||
|
/// This is the constructor to use the PS UART with EMIO pins to route the UART into the PL
|
||||||
|
/// or expose them via the PL package pins.
|
||||||
|
///
|
||||||
|
/// A valid PL design which routes the UART pins through into the PL must be used for this to
|
||||||
|
/// work.
|
||||||
|
pub fn new_with_emio(uart: impl PsUart, cfg: UartConfig) -> Result<Uart, InvalidPsUart> {
|
||||||
|
if uart.uart_id().is_none() {
|
||||||
|
return Err(InvalidPsUart);
|
||||||
|
}
|
||||||
|
Ok(Self::new_generic_unchecked(
|
||||||
|
uart.reg_block(),
|
||||||
|
uart.uart_id().unwrap(),
|
||||||
|
cfg,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the constructor to use the PS UART with MIO pins.
|
||||||
|
pub fn new_with_mio<TxPinI: TxPin, RxPinI: RxPin>(
|
||||||
|
uart: impl PsUart,
|
||||||
|
cfg: UartConfig,
|
||||||
|
pins: (TxPinI, RxPinI),
|
||||||
|
) -> Result<Self, UartConstructionError>
|
||||||
|
where
|
||||||
|
(TxPinI, RxPinI): UartPins,
|
||||||
|
{
|
||||||
|
let id = uart.uart_id();
|
||||||
|
if id.is_none() {
|
||||||
|
return Err(InvalidPsUart.into());
|
||||||
|
}
|
||||||
|
if id.unwrap() != TxPinI::UART_IDX || id.unwrap() != RxPinI::UART_IDX {
|
||||||
|
return Err(UartConstructionError::IdxMissmatch);
|
||||||
|
}
|
||||||
|
IoPeriphPin::new(pins.0, UART_MUX_CONF, None);
|
||||||
|
IoPeriphPin::new(pins.1, UART_MUX_CONF, None);
|
||||||
|
Ok(Self::new_generic_unchecked(
|
||||||
|
uart.reg_block(),
|
||||||
|
id.unwrap(),
|
||||||
|
cfg,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the generic constructor used by all other constructors.
|
||||||
|
///
|
||||||
|
/// It does not do any pin checks and resource control. It is recommended to use the other
|
||||||
|
/// constructors instead.
|
||||||
|
pub fn new_generic_unchecked(
|
||||||
|
mut reg_block: MmioUart<'static>,
|
||||||
|
uart_id: UartId,
|
||||||
|
cfg: UartConfig,
|
||||||
|
) -> Uart {
|
||||||
|
let periph_sel = match uart_id {
|
||||||
|
UartId::Uart0 => crate::PeripheralSelect::Uart0,
|
||||||
|
UartId::Uart1 => crate::PeripheralSelect::Uart1,
|
||||||
|
};
|
||||||
|
enable_amba_peripheral_clock(periph_sel);
|
||||||
|
reset(uart_id);
|
||||||
|
reg_block.modify_cr(|mut v| {
|
||||||
|
v.set_tx_dis(true);
|
||||||
|
v.set_rx_dis(true);
|
||||||
|
v
|
||||||
|
});
|
||||||
|
// Disable all interrupts.
|
||||||
|
reg_block.write_idr(InterruptControl::new_with_raw_value(0xFFFF_FFFF));
|
||||||
|
let mode = Mode::builder()
|
||||||
|
.with_chmode(cfg.chmode)
|
||||||
|
.with_nbstop(match cfg.stopbits {
|
||||||
|
Stopbits::One => zynq7000::uart::Stopbits::One,
|
||||||
|
Stopbits::OnePointFive => zynq7000::uart::Stopbits::OnePointFive,
|
||||||
|
Stopbits::Two => zynq7000::uart::Stopbits::Two,
|
||||||
|
})
|
||||||
|
.with_par(match cfg.parity {
|
||||||
|
Parity::Even => zynq7000::uart::Parity::Even,
|
||||||
|
Parity::Odd => zynq7000::uart::Parity::Odd,
|
||||||
|
Parity::None => zynq7000::uart::Parity::NoParity,
|
||||||
|
})
|
||||||
|
.with_chrl(match cfg.chrl {
|
||||||
|
CharLen::SixBits => zynq7000::uart::Chrl::SixBits,
|
||||||
|
CharLen::SevenBits => zynq7000::uart::Chrl::SevenBits,
|
||||||
|
CharLen::EightBits => zynq7000::uart::Chrl::EightBits,
|
||||||
|
})
|
||||||
|
.with_clksel(cfg.clk_sel)
|
||||||
|
.build();
|
||||||
|
reg_block.write_mr(mode);
|
||||||
|
reg_block.write_baudgen(
|
||||||
|
Baudgen::builder()
|
||||||
|
.with_cd(cfg.raw_clk_config().cd())
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
reg_block.write_baud_rate_div(
|
||||||
|
BaudRateDiv::builder()
|
||||||
|
.with_bdiv(cfg.raw_clk_config().bdiv())
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
// Soft reset for both TX and RX.
|
||||||
|
reg_block.modify_cr(|mut v| {
|
||||||
|
v.set_tx_rst(true);
|
||||||
|
v.set_rx_rst(true);
|
||||||
|
v
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write default value.
|
||||||
|
reg_block.write_rx_fifo_trigger(FifoTrigger::new_with_raw_value(
|
||||||
|
DEFAULT_RX_TRIGGER_LEVEL as u32,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Enable TX and RX.
|
||||||
|
reg_block.modify_cr(|mut v| {
|
||||||
|
v.set_tx_dis(false);
|
||||||
|
v.set_rx_dis(false);
|
||||||
|
v.set_tx_en(true);
|
||||||
|
v.set_rx_en(true);
|
||||||
|
v
|
||||||
|
});
|
||||||
|
|
||||||
|
Uart {
|
||||||
|
rx: Rx {
|
||||||
|
regs: unsafe { reg_block.clone() },
|
||||||
|
},
|
||||||
|
tx: Tx {
|
||||||
|
regs: reg_block,
|
||||||
|
idx: uart_id,
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_mode(&mut self, mode: ChMode) {
|
||||||
|
self.regs().modify_mr(|mut mr| {
|
||||||
|
mr.set_chmode(mode);
|
||||||
|
mr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn regs(&mut self) -> &mut MmioUart<'static> {
|
||||||
|
&mut self.rx.regs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn cfg(&self) -> &UartConfig {
|
||||||
|
&self.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn split(self) -> (Tx, Rx) {
|
||||||
|
(self.tx, self.rx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::ErrorType for Uart {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::Write for Uart {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||||
|
self.tx.write(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||||
|
self.tx.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::Read for Uart {
|
||||||
|
/// Read one byte from the FIFO.
|
||||||
|
///
|
||||||
|
/// This operation is infallible because pulling an available byte from the FIFO
|
||||||
|
/// always succeeds. If you want to be informed about RX errors, you should look at the
|
||||||
|
/// non-blocking API using interrupts, which also tracks the RX error bits.
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||||
|
self.rx.read()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::ErrorType for Uart {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Write for Uart {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
self.tx.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.tx.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Read for Uart {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
self.rx.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the UART peripheral using the SLCR reset register for UART.
|
||||||
|
///
|
||||||
|
/// Please note that this function will interfere with an already configured
|
||||||
|
/// UART instance.
|
||||||
|
#[inline]
|
||||||
|
pub fn reset(id: UartId) {
|
||||||
|
let assert_reset = match id {
|
||||||
|
UartId::Uart0 => DualRefAndClockReset::builder()
|
||||||
|
.with_periph1_ref_rst(false)
|
||||||
|
.with_periph0_ref_rst(true)
|
||||||
|
.with_periph1_cpu1x_rst(false)
|
||||||
|
.with_periph0_cpu1x_rst(true)
|
||||||
|
.build(),
|
||||||
|
UartId::Uart1 => DualRefAndClockReset::builder()
|
||||||
|
.with_periph1_ref_rst(true)
|
||||||
|
.with_periph0_ref_rst(false)
|
||||||
|
.with_periph1_cpu1x_rst(true)
|
||||||
|
.with_periph0_cpu1x_rst(false)
|
||||||
|
.build(),
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
Slcr::with(|regs| {
|
||||||
|
regs.reset_ctrl().write_uart(assert_reset);
|
||||||
|
// Keep it in reset for one cycle.. not sure if this is necessary.
|
||||||
|
cortex_ar::asm::nop();
|
||||||
|
regs.reset_ctrl().write_uart(DualRefAndClockReset::DEFAULT);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use approx::abs_diff_eq;
|
||||||
|
use fugit::HertzU32;
|
||||||
|
use zynq7000::uart::ClkSel;
|
||||||
|
|
||||||
|
const REF_UART_CLK: HertzU32 = HertzU32::from_raw(50_000_000);
|
||||||
|
const REF_UART_CLK_DIV_8: HertzU32 = HertzU32::from_raw(6_250_000);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_calc_0() {
|
||||||
|
// Baud 600
|
||||||
|
let cfg_0 = ClkConfigRaw::new(10417, 7).unwrap();
|
||||||
|
let actual_baud_0 = cfg_0.actual_baud(REF_UART_CLK);
|
||||||
|
assert!(abs_diff_eq!(actual_baud_0, 599.980, epsilon = 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_calc_1() {
|
||||||
|
// Baud 9600
|
||||||
|
let cfg = ClkConfigRaw::new(81, 7).unwrap();
|
||||||
|
let actual_baud = cfg.actual_baud(REF_UART_CLK_DIV_8);
|
||||||
|
assert!(abs_diff_eq!(actual_baud, 9645.061, epsilon = 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_calc_2() {
|
||||||
|
// Baud 9600
|
||||||
|
let cfg = ClkConfigRaw::new(651, 7).unwrap();
|
||||||
|
let actual_baud = cfg.actual_baud(REF_UART_CLK);
|
||||||
|
assert!(abs_diff_eq!(actual_baud, 9600.614, epsilon = 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_calc_3() {
|
||||||
|
// Baud 28800
|
||||||
|
let cfg = ClkConfigRaw::new(347, 4).unwrap();
|
||||||
|
let actual_baud = cfg.actual_baud(REF_UART_CLK);
|
||||||
|
assert!(abs_diff_eq!(actual_baud, 28818.44, epsilon = 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_calc_4() {
|
||||||
|
// Baud 921600
|
||||||
|
let cfg = ClkConfigRaw::new(9, 5).unwrap();
|
||||||
|
let actual_baud = cfg.actual_baud(REF_UART_CLK);
|
||||||
|
assert!(abs_diff_eq!(actual_baud, 925925.92, epsilon = 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_best_calc_0() {
|
||||||
|
let result = ClkConfigRaw::new_autocalc_with_raw_clk(REF_UART_CLK, ClkSel::UartRefClk, 600);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let (cfg, _error) = result.unwrap();
|
||||||
|
assert_eq!(cfg.cd(), 499);
|
||||||
|
assert_eq!(cfg.bdiv(), 166);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
fn test_viable_config_calculation() {
|
||||||
|
let cfgs = calculate_viable_configs(REF_UART_CLK, ClkSel::UartRefClk, 115200);
|
||||||
|
assert!(
|
||||||
|
cfgs.iter()
|
||||||
|
.find(|(cfg, _error)| { cfg.cd() == 62 && cfg.bdiv() == 6 })
|
||||||
|
.is_some()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
249
zynq7000-hal/src/uart/rx.rs
Normal file
249
zynq7000-hal/src/uart/rx.rs
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
use arbitrary_int::Number;
|
||||||
|
use zynq7000::uart::{InterruptControl, InterruptStatus, MmioUart};
|
||||||
|
|
||||||
|
use super::FIFO_DEPTH;
|
||||||
|
|
||||||
|
pub struct Rx {
|
||||||
|
pub(crate) regs: MmioUart<'static>,
|
||||||
|
}
|
||||||
|
// TODO: Remove once this is impelemnted for MmioUart
|
||||||
|
unsafe impl Send for Rx {}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
pub struct RxErrors {
|
||||||
|
framing: bool,
|
||||||
|
overrun: bool,
|
||||||
|
parity: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RxErrors {
|
||||||
|
#[inline]
|
||||||
|
pub const fn framing(&self) -> bool {
|
||||||
|
self.framing
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub const fn overrun(&self) -> bool {
|
||||||
|
self.overrun
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub const fn parity(&self) -> bool {
|
||||||
|
self.parity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct RxInterruptResult {
|
||||||
|
read_bytes: usize,
|
||||||
|
errors: Option<RxErrors>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RxInterruptResult {
|
||||||
|
pub fn read_bytes(&self) -> usize {
|
||||||
|
self.read_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn errors(&self) -> Option<RxErrors> {
|
||||||
|
self.errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rx {
|
||||||
|
#[inline]
|
||||||
|
pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
|
||||||
|
if self.regs.read_sr().rx_empty() {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
Ok(self.regs.read_fifo().fifo())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_fifo_unchecked(&mut self) -> u8 {
|
||||||
|
self.regs.read_fifo().fifo()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the receiver timeout value.
|
||||||
|
///
|
||||||
|
/// A value of 0 will disable the receiver timeout.
|
||||||
|
/// Otherwise, the 10-bit counter used by the receiver timeout mechanism of the UART will
|
||||||
|
/// load this value for the upper 8 bits on a reload. The counter is clocked by the UART
|
||||||
|
/// bit clock, so this value times 4 is the number of UART clock ticks until a timeout occurs.
|
||||||
|
#[inline]
|
||||||
|
pub fn set_rx_timeout_value(&mut self, rto: u8) {
|
||||||
|
self.regs.write_rx_tout(rto as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn soft_reset(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_rx_rst(true);
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
while self.regs.read_cr().rx_rst() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to start the interrupt driven reception of data.
|
||||||
|
///
|
||||||
|
/// This function will perform a soft-reset, clear RX related interrupts and then enable
|
||||||
|
/// all relevant interrupts for the RX side of the UART. These steps are recommended to have
|
||||||
|
/// a glitch-free start of the interrupt driven reception.
|
||||||
|
///
|
||||||
|
/// This should be called once at system start-up. After that, you only need to call
|
||||||
|
/// [Self::on_interrupt] in the interrupt handler for the UART peripheral.
|
||||||
|
pub fn start_interrupt_driven_reception(&mut self) {
|
||||||
|
self.soft_reset();
|
||||||
|
self.clear_interrupts();
|
||||||
|
self.enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables all interrupts relevant for the RX side of the UART.
|
||||||
|
///
|
||||||
|
/// It is recommended to also clear all interrupts immediately after enabling them.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_interrupts(&mut self) {
|
||||||
|
self.regs.write_ier(
|
||||||
|
InterruptControl::builder()
|
||||||
|
.with_tx_over(false)
|
||||||
|
.with_tx_near_full(false)
|
||||||
|
.with_tx_trig(false)
|
||||||
|
.with_rx_dms(false)
|
||||||
|
.with_rx_timeout(true)
|
||||||
|
.with_rx_parity(true)
|
||||||
|
.with_rx_framing(true)
|
||||||
|
.with_rx_over(true)
|
||||||
|
.with_tx_full(false)
|
||||||
|
.with_tx_empty(false)
|
||||||
|
.with_rx_full(true)
|
||||||
|
.with_rx_empty(false)
|
||||||
|
.with_rx_trg(true)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_interrupt(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut [u8; FIFO_DEPTH],
|
||||||
|
reset_rx_timeout: bool,
|
||||||
|
) -> RxInterruptResult {
|
||||||
|
let mut result = RxInterruptResult::default();
|
||||||
|
let imr = self.regs.read_imr();
|
||||||
|
if !imr.rx_full()
|
||||||
|
&& !imr.rx_trg()
|
||||||
|
&& !imr.rx_parity()
|
||||||
|
&& !imr.rx_framing()
|
||||||
|
&& !imr.rx_over()
|
||||||
|
&& !imr.rx_timeout()
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
let isr = self.regs.read_isr();
|
||||||
|
if isr.rx_full() {
|
||||||
|
// Read all bytes in the full RX fifo.
|
||||||
|
for byte in buf.iter_mut() {
|
||||||
|
*byte = self.read_fifo_unchecked();
|
||||||
|
}
|
||||||
|
result.read_bytes = FIFO_DEPTH;
|
||||||
|
} else if isr.rx_trg() {
|
||||||
|
// It is guaranteed that we can read the FIFO level amount of data
|
||||||
|
let fifo_trigger = self.regs.read_rx_fifo_trigger().trig().as_usize();
|
||||||
|
(0..fifo_trigger).for_each(|i| {
|
||||||
|
buf[i] = self.read_fifo_unchecked();
|
||||||
|
});
|
||||||
|
result.read_bytes = fifo_trigger;
|
||||||
|
}
|
||||||
|
// Read everything else that is available, as long as there is space left.
|
||||||
|
while result.read_bytes < buf.len() {
|
||||||
|
if let Ok(byte) = self.read_fifo() {
|
||||||
|
buf[result.read_bytes] = byte;
|
||||||
|
result.read_bytes += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle error events.
|
||||||
|
if isr.rx_parity() || isr.rx_framing() || isr.rx_over() {
|
||||||
|
result.errors = Some(RxErrors {
|
||||||
|
framing: isr.rx_framing(),
|
||||||
|
overrun: isr.rx_over(),
|
||||||
|
parity: isr.rx_parity(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Handle timeout event.
|
||||||
|
if isr.rx_timeout() && reset_rx_timeout {
|
||||||
|
self.regs.modify_cr(|mut cr| {
|
||||||
|
cr.set_rstto(true);
|
||||||
|
cr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.clear_interrupts();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// This clears all RX related interrupts.
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_interrupts(&mut self) {
|
||||||
|
self.regs.write_isr(
|
||||||
|
InterruptStatus::builder()
|
||||||
|
.with_tx_over(false)
|
||||||
|
.with_tx_near_full(false)
|
||||||
|
.with_tx_trig(false)
|
||||||
|
.with_rx_dms(true)
|
||||||
|
.with_rx_timeout(true)
|
||||||
|
.with_rx_parity(true)
|
||||||
|
.with_rx_framing(true)
|
||||||
|
.with_rx_over(true)
|
||||||
|
.with_tx_full(false)
|
||||||
|
.with_tx_empty(false)
|
||||||
|
.with_rx_full(true)
|
||||||
|
.with_rx_empty(true)
|
||||||
|
.with_rx_trg(true)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::ErrorType for Rx {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::Read for Rx {
|
||||||
|
/// Read one byte from the FIFO.
|
||||||
|
///
|
||||||
|
/// This operation is infallible because pulling an available byte from the FIFO
|
||||||
|
/// always succeeds. If you want to be informed about RX errors, you should look at the
|
||||||
|
/// non-blocking API using interrupts, which also tracks the RX error bits.
|
||||||
|
#[inline]
|
||||||
|
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||||
|
self.read_fifo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::ErrorType for Rx {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Read for Rx {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let mut read = 0;
|
||||||
|
loop {
|
||||||
|
if !self.regs.read_sr().rx_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for byte in buf.iter_mut() {
|
||||||
|
match <Self as embedded_hal_nb::serial::Read<u8>>::read(self) {
|
||||||
|
Ok(w) => {
|
||||||
|
*byte = w;
|
||||||
|
read += 1;
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(read)
|
||||||
|
}
|
||||||
|
}
|
201
zynq7000-hal/src/uart/tx.rs
Normal file
201
zynq7000-hal/src/uart/tx.rs
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
use zynq7000::uart::{Fifo, InterruptControl, InterruptStatus, MmioUart};
|
||||||
|
|
||||||
|
use super::UartId;
|
||||||
|
|
||||||
|
pub struct Tx {
|
||||||
|
pub(crate) regs: MmioUart<'static>,
|
||||||
|
pub(crate) idx: UartId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tx {
|
||||||
|
/// Steal the TX side of the UART for a given UART index.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Circumvents safety guarantees provided by the compiler.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn steal(idx: UartId) -> Self {
|
||||||
|
Tx {
|
||||||
|
regs: unsafe { idx.regs() },
|
||||||
|
idx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn uart_idx(&self) -> UartId {
|
||||||
|
self.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn regs(&mut self) -> &mut MmioUart<'static> {
|
||||||
|
&mut self.regs
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write_fifo(&mut self, word: u8) -> nb::Result<(), Infallible> {
|
||||||
|
if self.regs.read_sr().tx_full() {
|
||||||
|
return Err(nb::Error::WouldBlock);
|
||||||
|
}
|
||||||
|
self.write_fifo_unchecked(word);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables TX side of the UART.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self, with_reset: bool) {
|
||||||
|
if with_reset {
|
||||||
|
self.soft_reset();
|
||||||
|
}
|
||||||
|
self.regs.modify_cr(|mut val| {
|
||||||
|
val.set_tx_en(true);
|
||||||
|
val.set_tx_dis(false);
|
||||||
|
val
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables TX side of the UART.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut val| {
|
||||||
|
val.set_tx_en(false);
|
||||||
|
val.set_tx_dis(true);
|
||||||
|
val
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn soft_reset(&mut self) {
|
||||||
|
self.regs.modify_cr(|mut val| {
|
||||||
|
val.set_tx_rst(true);
|
||||||
|
val
|
||||||
|
});
|
||||||
|
loop {
|
||||||
|
if !self.regs.read_cr().tx_rst() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write_fifo_unchecked(&mut self, word: u8) {
|
||||||
|
self.regs.write_fifo(Fifo::new_with_raw_value(word as u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables interrupts relevant for the TX side of the UART except the TX trigger interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_interrupts(&mut self) {
|
||||||
|
self.regs.write_ier(
|
||||||
|
InterruptControl::builder()
|
||||||
|
.with_tx_over(true)
|
||||||
|
.with_tx_near_full(true)
|
||||||
|
.with_tx_trig(false)
|
||||||
|
.with_rx_dms(false)
|
||||||
|
.with_rx_timeout(false)
|
||||||
|
.with_rx_parity(false)
|
||||||
|
.with_rx_framing(false)
|
||||||
|
.with_rx_over(false)
|
||||||
|
.with_tx_full(true)
|
||||||
|
.with_tx_empty(true)
|
||||||
|
.with_rx_full(false)
|
||||||
|
.with_rx_empty(false)
|
||||||
|
.with_rx_trg(false)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable interrupts relevant for the TX side of the UART except the TX trigger interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable_interrupts(&mut self) {
|
||||||
|
self.regs.write_idr(
|
||||||
|
InterruptControl::builder()
|
||||||
|
.with_tx_over(true)
|
||||||
|
.with_tx_near_full(true)
|
||||||
|
.with_tx_trig(false)
|
||||||
|
.with_rx_dms(false)
|
||||||
|
.with_rx_timeout(false)
|
||||||
|
.with_rx_parity(false)
|
||||||
|
.with_rx_framing(false)
|
||||||
|
.with_rx_over(false)
|
||||||
|
.with_tx_full(true)
|
||||||
|
.with_tx_empty(true)
|
||||||
|
.with_rx_full(false)
|
||||||
|
.with_rx_empty(false)
|
||||||
|
.with_rx_trg(false)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears interrupts relevant for the TX side of the UART except the TX trigger interrupt.
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_interrupts(&mut self) {
|
||||||
|
self.regs.write_isr(
|
||||||
|
InterruptStatus::builder()
|
||||||
|
.with_tx_over(true)
|
||||||
|
.with_tx_near_full(true)
|
||||||
|
.with_tx_trig(false)
|
||||||
|
.with_rx_dms(false)
|
||||||
|
.with_rx_timeout(false)
|
||||||
|
.with_rx_parity(false)
|
||||||
|
.with_rx_framing(false)
|
||||||
|
.with_rx_over(false)
|
||||||
|
.with_tx_full(true)
|
||||||
|
.with_tx_empty(true)
|
||||||
|
.with_rx_full(false)
|
||||||
|
.with_rx_empty(false)
|
||||||
|
.with_rx_trg(false)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::ErrorType for Tx {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal_nb::serial::Write for Tx {
|
||||||
|
#[inline]
|
||||||
|
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||||
|
self.write_fifo(word)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||||
|
loop {
|
||||||
|
if self.regs.read_sr().tx_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::ErrorType for Tx {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Write for Tx {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let mut written = 0;
|
||||||
|
loop {
|
||||||
|
if !self.regs.read_sr().tx_full() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for byte in buf.iter() {
|
||||||
|
match self.write_fifo(*byte) {
|
||||||
|
Ok(_) => written += 1,
|
||||||
|
Err(nb::Error::WouldBlock) => return Ok(written),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
<Self as embedded_hal_nb::serial::Write<u8>>::flush(self).ok();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
205
zynq7000-hal/src/uart/tx_async.rs
Normal file
205
zynq7000-hal/src/uart/tx_async.rs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
use core::{cell::RefCell, convert::Infallible, sync::atomic::AtomicBool};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use raw_slice::RawBufSlice;
|
||||||
|
|
||||||
|
use crate::uart::{FIFO_DEPTH, Tx, UartId};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TransferType {
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
Transfer,
|
||||||
|
TransferInPlace,
|
||||||
|
}
|
||||||
|
|
||||||
|
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
|
||||||
|
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
|
||||||
|
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
|
||||||
|
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
||||||
|
// critical section.
|
||||||
|
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
|
||||||
|
|
||||||
|
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
|
||||||
|
/// UART peripheral.
|
||||||
|
///
|
||||||
|
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
|
||||||
|
/// the given UART bank.
|
||||||
|
pub fn on_interrupt_tx(peripheral: UartId) {
|
||||||
|
let mut tx_with_irq = unsafe { Tx::steal(peripheral) };
|
||||||
|
let idx = peripheral as usize;
|
||||||
|
let imr = tx_with_irq.regs().read_imr();
|
||||||
|
// IRQ is not related to TX.
|
||||||
|
if !imr.tx_over() && !imr.tx_near_full() && !imr.tx_full() && !imr.tx_empty() && !imr.tx_full()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let isr = tx_with_irq.regs().read_isr();
|
||||||
|
let unexpected_overrun = isr.tx_over();
|
||||||
|
let mut context = critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow()
|
||||||
|
});
|
||||||
|
// No transfer active.
|
||||||
|
if context.slice.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let slice_len = context.slice.len().unwrap();
|
||||||
|
context.tx_overrun = unexpected_overrun;
|
||||||
|
if (context.progress >= slice_len && isr.tx_empty()) || slice_len == 0 {
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = context;
|
||||||
|
});
|
||||||
|
// Transfer is done.
|
||||||
|
TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
tx_with_irq.disable_interrupts();
|
||||||
|
tx_with_irq.clear_interrupts();
|
||||||
|
UART_TX_WAKERS[idx].wake();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Safety: We documented that the user provided slice must outlive the future, so we convert
|
||||||
|
// the raw pointer back to the slice here.
|
||||||
|
let slice = unsafe { context.slice.get() }.expect("slice is invalid");
|
||||||
|
while context.progress < slice_len {
|
||||||
|
if tx_with_irq.regs().read_sr().tx_full() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Safety: TX structure is owned by the future which does not write into the the data
|
||||||
|
// register, so we can assume we are the only one writing to the data register.
|
||||||
|
tx_with_irq.write_fifo_unchecked(slice[context.progress]);
|
||||||
|
context.progress += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back updated context structure.
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
*context_ref.borrow_mut() = context;
|
||||||
|
});
|
||||||
|
// Clear interrupts.
|
||||||
|
tx_with_irq.clear_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct TxContext {
|
||||||
|
progress: usize,
|
||||||
|
tx_overrun: bool,
|
||||||
|
slice: RawBufSlice,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl TxContext {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
progress: 0,
|
||||||
|
tx_overrun: false,
|
||||||
|
slice: RawBufSlice::new_nulled(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TxFuture {
|
||||||
|
id: UartId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxFuture {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
|
||||||
|
/// that the slice outlives the data structure.
|
||||||
|
pub unsafe fn new(tx_with_irq: &mut Tx, data: &[u8]) -> Self {
|
||||||
|
let idx = tx_with_irq.uart_idx() as usize;
|
||||||
|
TX_DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
tx_with_irq.disable_interrupts();
|
||||||
|
tx_with_irq.disable();
|
||||||
|
|
||||||
|
let init_fill_count = core::cmp::min(data.len(), FIFO_DEPTH);
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let context_ref = TX_CONTEXTS[idx].borrow(cs);
|
||||||
|
let mut context = context_ref.borrow_mut();
|
||||||
|
unsafe {
|
||||||
|
context.slice.set(data);
|
||||||
|
}
|
||||||
|
context.progress = init_fill_count; // We fill the FIFO.
|
||||||
|
});
|
||||||
|
tx_with_irq.enable(true);
|
||||||
|
for data in data.iter().take(init_fill_count) {
|
||||||
|
tx_with_irq.write_fifo_unchecked(*data);
|
||||||
|
}
|
||||||
|
tx_with_irq.enable_interrupts();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
id: tx_with_irq.uart_idx(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for TxFuture {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
UART_TX_WAKERS[self.id as usize].register(cx.waker());
|
||||||
|
if TX_DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
let progress = critical_section::with(|cs| {
|
||||||
|
let mut ctx = TX_CONTEXTS[self.id as usize].borrow(cs).borrow_mut();
|
||||||
|
ctx.slice.set_null();
|
||||||
|
ctx.progress
|
||||||
|
});
|
||||||
|
return core::task::Poll::Ready(progress);
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TxFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut tx = unsafe { Tx::steal(self.id) };
|
||||||
|
tx.disable_interrupts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TxAsync {
|
||||||
|
tx: Tx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxAsync {
|
||||||
|
pub fn new(tx: Tx) -> Self {
|
||||||
|
Self { tx }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a buffer asynchronously.
|
||||||
|
///
|
||||||
|
/// This implementation is not side effect free, and a started future might have already
|
||||||
|
/// written part of the passed buffer.
|
||||||
|
pub async fn write(&mut self, buf: &[u8]) -> usize {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
|
||||||
|
fut.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Tx {
|
||||||
|
self.tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::ErrorType for TxAsync {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io_async::Write for TxAsync {
|
||||||
|
/// Write a buffer asynchronously.
|
||||||
|
///
|
||||||
|
/// This implementation is not side effect free, and a started future might have already
|
||||||
|
/// written part of the passed buffer.
|
||||||
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
Ok(self.write(buf).await)
|
||||||
|
}
|
||||||
|
}
|
7
zynq7000-rt/Cargo.lock
generated
Normal file
7
zynq7000-rt/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zynq-rt"
|
||||||
|
version = "0.1.0"
|
26
zynq7000-rt/Cargo.toml
Normal file
26
zynq7000-rt/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "zynq7000-rt"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-a-rt = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", optional = true, features = ["vfp-dp"] }
|
||||||
|
cortex-ar = { git = "https://github.com/rust-embedded/cortex-ar", branch = "main", features = ["critical-section-single-core"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["rt"]
|
||||||
|
tools = []
|
||||||
|
rt = ["dep:cortex-a-rt"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "table-gen"
|
||||||
|
path = "src/bin/table-gen.rs"
|
||||||
|
# Prevents default build
|
||||||
|
required-features = ["tools"]
|
27
zynq7000-rt/README.md
Normal file
27
zynq7000-rt/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Zynq7000 Rust Run-Time Support
|
||||||
|
========
|
||||||
|
|
||||||
|
Startup code and minimal runtime for the AMD Zynq7000 SoC to write bare metal Rust code.
|
||||||
|
This run-time crate is strongly based on the
|
||||||
|
[startup code provided by AMD](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S).
|
||||||
|
|
||||||
|
One major difference is that the MMU table is specified as Rust code. There are also modification
|
||||||
|
to the stack setup code, because a different linker script is used.
|
||||||
|
|
||||||
|
This crate pulls in the [cortex-a-rt](https://github.com/us-irs/cortex-ar/tree/cortex-a-addition/cortex-a-rt)
|
||||||
|
crate to provide ARM vectors and the linker script.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- `rt` is a default feature which activates the run-time.
|
||||||
|
|
||||||
|
## Re-Generating the MMU table
|
||||||
|
|
||||||
|
The MMU table is a static flat map of 4096 entries for each 1 MB in the memory map.
|
||||||
|
It was generated using the `table-gen` binary tool.
|
||||||
|
|
||||||
|
You can re-run the tool using
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo +stable --target <hostTarget> run --bin table-gen --no-default-features --features tools
|
||||||
|
```
|
2
zynq7000-rt/regen-table.sh
Executable file
2
zynq7000-rt/regen-table.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cargo +stable run --target $(rustc -vV | grep host | cut -d ' ' -f2) --bin table-gen --no-default-features --features tools
|
285
zynq7000-rt/src/bin/table-gen.rs
Normal file
285
zynq7000-rt/src/bin/table-gen.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::process::Command;
|
||||||
|
use zynq7000_rt::mmu::ONE_MB;
|
||||||
|
pub use zynq7000_rt::mmu::segments::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let file_path = "src/mmu_table.rs";
|
||||||
|
let file = File::create(file_path).expect("Failed to create file");
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
let attr_ddr = stringify!(section_attrs::DDR);
|
||||||
|
let attr_unassigned = stringify!(section_attrs::UNASSIGNED_RESERVED);
|
||||||
|
let attr_fpga_slaves = stringify!(section_attrs::FPGA_SLAVES);
|
||||||
|
let attr_shared_dev = stringify!(section_attrs::SHAREABLE_DEVICE);
|
||||||
|
let attr_sram = stringify!(section_attrs::SRAM);
|
||||||
|
let attr_qspi = stringify!(section_attrs::QSPI_XIP);
|
||||||
|
let attr_ocm_high = stringify!(section_attrs::OCM_MAPPED_HIGH);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
1 + DDR_FULL_ACCESSIBLE
|
||||||
|
+ FPGA_SLAVE
|
||||||
|
+ FPGA_SLAVE
|
||||||
|
+ UNASSIGNED_0
|
||||||
|
+ IO_PERIPHS
|
||||||
|
+ UNASSIGNED_1
|
||||||
|
+ NAND
|
||||||
|
+ NOR
|
||||||
|
+ SRAM
|
||||||
|
+ SEGMENTS_UNASSIGNED_2
|
||||||
|
+ AMBA_APB
|
||||||
|
+ UNASSIGNED_3
|
||||||
|
+ QSPI_XIP
|
||||||
|
+ UNASSIGNED_4
|
||||||
|
+ OCM_MAPPED_HIGH,
|
||||||
|
4096
|
||||||
|
);
|
||||||
|
let mut buf_writer = std::io::BufWriter::new(file);
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"//! This file was auto-generated by table-gen.rs"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(buf_writer, "use cortex_ar::mmu::L1Section;").unwrap();
|
||||||
|
writeln!(buf_writer, "use crate::mmu::{{section_attrs, L1Table}};").unwrap();
|
||||||
|
writeln!(buf_writer, "").unwrap();
|
||||||
|
|
||||||
|
writeln!(buf_writer, "/// MMU Level 1 Page table.").unwrap();
|
||||||
|
writeln!(buf_writer, "///").unwrap();
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"/// 4096 entries, each covering 1MB of the address space."
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"pub const MMU_L1_PAGE_TABLE: L1Table = L1Table(["
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// First DDR segment, OCM memory (0x0000_0000 - 0x0010_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_ddr
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
offset += ONE_MB;
|
||||||
|
writeln!(buf_writer, "// DDR memory (0x00100000 - 0x4000_0000)").unwrap();
|
||||||
|
for _ in 0..DDR_FULL_ACCESSIBLE {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_ddr
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// FPGA slave 0 (0x4000_0000 - 0x8000_0000)").unwrap();
|
||||||
|
for _ in 0..FPGA_SLAVE {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_fpga_slaves
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// FPGA slave 1 (0x8000_0000 - 0xC000_0000)").unwrap();
|
||||||
|
for _ in 0..FPGA_SLAVE {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_fpga_slaves
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Unassigned/Reserved (0xC000_0000 - 0xE000_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..UNASSIGNED_0 {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_unassigned
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Segments IO peripherals (0xE000_0000 - 0xE030_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..IO_PERIPHS {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_shared_dev
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Unassigned/Reserved (0xE030_0000 - 0xE100_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..UNASSIGNED_1 {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_unassigned
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// NAND (0xE100_0000 - 0xE200_0000)").unwrap();
|
||||||
|
for _ in 0..NAND {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_shared_dev
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// NOR (0xE200_0000 - 0xE400_0000)").unwrap();
|
||||||
|
for _ in 0..NOR {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_shared_dev
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// SRAM (0xE400_0000 - 0xE600_0000)").unwrap();
|
||||||
|
for _ in 0..SRAM {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_sram
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Unassigned/Reserved (0xE600_0000 - 0xF800_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..SEGMENTS_UNASSIGNED_2 {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_unassigned
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// AMBA APB peripherals (0xF800_0000 - 0xF900_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..AMBA_APB {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_shared_dev
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Unassigned/Reserved (0xF900_0000 - 0xFC00_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..UNASSIGNED_3 {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_unassigned
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// QSPI XIP (0xFC00_0000 - 0xFE00_0000)").unwrap();
|
||||||
|
for _ in 0..QSPI_XIP {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_qspi
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"// Unassiged/Reserved (0xFE00_0000 - 0xFFF0_0000)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for _ in 0..UNASSIGNED_4 {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_unassigned
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
offset += ONE_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(buf_writer, "// OCM High (0xFFF0_0000 - 0xFFFF_FFFF)").unwrap();
|
||||||
|
let mut offset_u64 = offset as u64;
|
||||||
|
for _ in 0..OCM_MAPPED_HIGH {
|
||||||
|
writeln!(
|
||||||
|
buf_writer,
|
||||||
|
"L1Section::new({}, {}).raw_value(),",
|
||||||
|
offset, attr_ocm_high
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
offset_u64 += ONE_MB as u64;
|
||||||
|
}
|
||||||
|
// Check that the full 4 GB were covered (not too much, or less)
|
||||||
|
assert_eq!(offset_u64, 0x1_0000_0000 as u64);
|
||||||
|
|
||||||
|
writeln!(buf_writer, "]);").unwrap();
|
||||||
|
// Finish the file.
|
||||||
|
drop(buf_writer);
|
||||||
|
println!("Generated mmu_table.rs");
|
||||||
|
|
||||||
|
// Run rustfmt on the generated file
|
||||||
|
let output = Command::new("rustfmt")
|
||||||
|
.arg(file_path)
|
||||||
|
.output()
|
||||||
|
.expect("Failed to run rustfmt");
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
eprintln!("rustfmt failed: {:?}", output);
|
||||||
|
}
|
||||||
|
}
|
11
zynq7000-rt/src/lib.rs
Normal file
11
zynq7000-rt/src/lib.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//! Rust bare metal run-time support for the AMD Zynq 7000 SoCs
|
||||||
|
//!
|
||||||
|
//! This includes basic low-level startup code similar to the bare-metal boot routines
|
||||||
|
//! [provided by Xilinx](https://github.com/Xilinx/embeddedsw/tree/master/lib/bsp/standalone/src/arm/cortexa9/gcc).
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
pub mod mmu;
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
mod mmu_table;
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
pub mod rt;
|
191
zynq7000-rt/src/mmu.rs
Normal file
191
zynq7000-rt/src/mmu.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
//! The overview of translation table memory attributes is described below.
|
||||||
|
//!
|
||||||
|
//!| | Memory Range | Definition in Translation Table |
|
||||||
|
//!|-----------------------|-------------------------|-----------------------------------|
|
||||||
|
//!| DDR | 0x00000000 - 0x3FFFFFFF | Normal write-back Cacheable |
|
||||||
|
//!| PL | 0x40000000 - 0xBFFFFFFF | Strongly Ordered |
|
||||||
|
//!| Reserved | 0xC0000000 - 0xDFFFFFFF | Unassigned |
|
||||||
|
//!| Memory mapped devices | 0xE0000000 - 0xE02FFFFF | Device Memory |
|
||||||
|
//!| Reserved | 0xE0300000 - 0xE0FFFFFF | Unassigned |
|
||||||
|
//!| NAND, NOR | 0xE1000000 - 0xE3FFFFFF | Device memory |
|
||||||
|
//!| SRAM | 0xE4000000 - 0xE5FFFFFF | Normal write-back Cacheable |
|
||||||
|
//!| Reserved | 0xE6000000 - 0xF7FFFFFF | Unassigned |
|
||||||
|
//!| AMBA APB Peripherals | 0xF8000000 - 0xF8FFFFFF | Device Memory |
|
||||||
|
//!| Reserved | 0xF9000000 - 0xFBFFFFFF | Unassigned |
|
||||||
|
//!| Linear QSPI - XIP | 0xFC000000 - 0xFDFFFFFF | Normal write-through cacheable |
|
||||||
|
//!| Reserved | 0xFE000000 - 0xFFEFFFFF | Unassigned |
|
||||||
|
//!| OCM | 0xFFF00000 - 0xFFFFFFFF | Normal inner write-back cacheable |
|
||||||
|
//!
|
||||||
|
//! For region 0x00000000 - 0x3FFFFFFF, a system where DDR is less than 1 GB,
|
||||||
|
//! region after DDR and before PL is marked as undefined/reserved in translation
|
||||||
|
//! table. In 0xF8000000 - 0xF8FFFFFF, 0xF8000C00 - 0xF8000FFF, 0xF8010000 -
|
||||||
|
//! 0xF88FFFFF and 0xF8F03000 to 0xF8FFFFFF are reserved but due to granual size
|
||||||
|
//! of 1 MB, it is not possible to define separate regions for them. For region
|
||||||
|
//! 0xFFF00000 - 0xFFFFFFFF, 0xFFF00000 to 0xFFFB0000 is reserved but due to 1MB
|
||||||
|
//! granual size, it is not possible to define separate region for it.
|
||||||
|
pub const MAX_DDR_SIZE: usize = 0x4000_0000;
|
||||||
|
pub const ONE_MB: usize = 0x10_0000;
|
||||||
|
|
||||||
|
pub mod offsets {
|
||||||
|
pub const OFFSET_DDR: usize = 0;
|
||||||
|
pub const OFFSET_DDR_ALL_ACCESSIBLE: usize = 0x10_0000;
|
||||||
|
|
||||||
|
pub const OFFSET_FPGA_SLAVE_0: usize = 0x4000_0000;
|
||||||
|
pub const OFFSET_FPGA_SLAVE_1_START: usize = 0x8000_0000;
|
||||||
|
pub const OFFSET_FPGA_SLAVE_1_END: usize = 0xC000_0000;
|
||||||
|
|
||||||
|
pub const OFFSET_IO_PERIPHERALS_START: usize = 0xE000_0000;
|
||||||
|
pub const OFFSET_IO_PERIPHERALS_END: usize = 0xE030_0000;
|
||||||
|
|
||||||
|
pub const OFFSET_NAND_MEMORY: usize = 0xE100_0000;
|
||||||
|
pub const OFFSET_NOR_MEMORY: usize = 0xE200_0000;
|
||||||
|
pub const OFFSET_SRAM_MEMORY: usize = 0xE400_0000;
|
||||||
|
pub const OFFSET_SMC_MEMORIES_END: usize = 0xE600_0000;
|
||||||
|
|
||||||
|
/// 0xf8000c00 to 0xf8000fff, 0xf8010000 to 0xf88fffff and
|
||||||
|
/// 0xf8f03000 to 0xf8ffffff are reserved but due to granual size of
|
||||||
|
/// 1MB, it is not possible to define separate regions for them.
|
||||||
|
pub const OFFSET_AMBA_APB_START: usize = 0xF800_0000;
|
||||||
|
pub const OFFSET_AMBA_APB_END: usize = 0xF900_0000;
|
||||||
|
|
||||||
|
pub const OFFSET_QSPI_XIP_START: usize = 0xFC00_0000;
|
||||||
|
pub const OFFSET_QSPI_XIP_END: usize = 0xFE00_0000;
|
||||||
|
|
||||||
|
/// 0xfff00000 to 0xfffb0000 is reserved but due to granual size of
|
||||||
|
/// 1MB, it is not possible to define separate region for it
|
||||||
|
pub const OFFSET_OCM_MAPPED_HIGH_START: usize = 0xFFF0_0000;
|
||||||
|
pub const OFFSET_OCM_MAPPED_HIGH_END: u64 = 0x1_0000_0000;
|
||||||
|
}
|
||||||
|
pub mod segments {
|
||||||
|
pub use super::offsets::*;
|
||||||
|
use super::{MAX_DDR_SIZE, ONE_MB};
|
||||||
|
|
||||||
|
/// First 1 MB of DDR has special treatment, access is dependant on SCU/OCM state.
|
||||||
|
/// Refer to Zynq TRM UG585 p.106 for more details.
|
||||||
|
pub const DDR_FULL_ACCESSIBLE: usize = (MAX_DDR_SIZE - ONE_MB) / ONE_MB;
|
||||||
|
pub const FPGA_SLAVE: usize = (OFFSET_FPGA_SLAVE_1_START - OFFSET_FPGA_SLAVE_0) / ONE_MB;
|
||||||
|
pub const UNASSIGNED_0: usize =
|
||||||
|
(OFFSET_IO_PERIPHERALS_START - OFFSET_FPGA_SLAVE_1_END) / ONE_MB;
|
||||||
|
pub const IO_PERIPHS: usize =
|
||||||
|
(OFFSET_IO_PERIPHERALS_END - OFFSET_IO_PERIPHERALS_START) / ONE_MB;
|
||||||
|
pub const UNASSIGNED_1: usize = (OFFSET_NAND_MEMORY - OFFSET_IO_PERIPHERALS_END) / ONE_MB;
|
||||||
|
pub const NAND: usize = (OFFSET_NOR_MEMORY - OFFSET_NAND_MEMORY) / ONE_MB;
|
||||||
|
pub const NOR: usize = (OFFSET_SRAM_MEMORY - OFFSET_NOR_MEMORY) / ONE_MB;
|
||||||
|
pub const SRAM: usize = (OFFSET_SMC_MEMORIES_END - OFFSET_SRAM_MEMORY) / ONE_MB;
|
||||||
|
pub const SEGMENTS_UNASSIGNED_2: usize =
|
||||||
|
(OFFSET_AMBA_APB_START - OFFSET_SMC_MEMORIES_END) / ONE_MB;
|
||||||
|
pub const AMBA_APB: usize = (OFFSET_AMBA_APB_END - OFFSET_AMBA_APB_START) / ONE_MB;
|
||||||
|
pub const UNASSIGNED_3: usize = (OFFSET_QSPI_XIP_START - OFFSET_AMBA_APB_END) / ONE_MB;
|
||||||
|
pub const QSPI_XIP: usize = (OFFSET_QSPI_XIP_END - OFFSET_QSPI_XIP_START) / ONE_MB;
|
||||||
|
pub const UNASSIGNED_4: usize = (OFFSET_OCM_MAPPED_HIGH_START - OFFSET_QSPI_XIP_END) / ONE_MB;
|
||||||
|
pub const OCM_MAPPED_HIGH: usize = ((OFFSET_OCM_MAPPED_HIGH_END
|
||||||
|
- OFFSET_OCM_MAPPED_HIGH_START as u64)
|
||||||
|
/ ONE_MB as u64) as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod section_attrs {
|
||||||
|
use cortex_ar::mmu::{
|
||||||
|
AccessPermissions, CacheableMemoryAttribute, MemoryRegionAttributes, SectionAttributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DDR: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: true,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
// Manager domain
|
||||||
|
domain: 0b1111,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::CacheableMemory {
|
||||||
|
inner: CacheableMemoryAttribute::WriteBackWriteAlloc,
|
||||||
|
outer: CacheableMemoryAttribute::WriteBackWriteAlloc,
|
||||||
|
}
|
||||||
|
.as_raw(),
|
||||||
|
};
|
||||||
|
pub const FPGA_SLAVES: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::StronglyOrdered.as_raw(),
|
||||||
|
};
|
||||||
|
pub const SHAREABLE_DEVICE: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::ShareableDevice.as_raw(),
|
||||||
|
};
|
||||||
|
pub const SRAM: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::OuterAndInnerWriteBackNoWriteAlloc.as_raw(),
|
||||||
|
};
|
||||||
|
pub const QSPI_XIP: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::OuterAndInnerWriteThroughNoWriteAlloc.as_raw(),
|
||||||
|
};
|
||||||
|
pub const OCM_MAPPED_HIGH: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::FullAccess,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::CacheableMemory {
|
||||||
|
inner: CacheableMemoryAttribute::WriteThroughNoWriteAlloc,
|
||||||
|
outer: CacheableMemoryAttribute::NonCacheable,
|
||||||
|
}
|
||||||
|
.as_raw(),
|
||||||
|
};
|
||||||
|
pub const UNASSIGNED_RESERVED: SectionAttributes = SectionAttributes {
|
||||||
|
non_global: false,
|
||||||
|
p_bit: false,
|
||||||
|
shareable: false,
|
||||||
|
access: AccessPermissions::PermissionFault,
|
||||||
|
domain: 0b0000,
|
||||||
|
execute_never: false,
|
||||||
|
memory_attrs: MemoryRegionAttributes::StronglyOrdered.as_raw(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const NUM_L1_PAGE_TABLE_ENTRIES: usize = 4096;
|
||||||
|
|
||||||
|
#[repr(C, align(16384))]
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
pub struct L1Table(pub(crate) [u32; NUM_L1_PAGE_TABLE_ENTRIES]);
|
||||||
|
|
||||||
|
/// Load the MMU translation table base address into the MMU.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function is unsafe because it directly writes to the MMU related registers. It has to be
|
||||||
|
/// called once in the boot code before enabling the MMU, and it should be called while the MMU is
|
||||||
|
/// disabled.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
unsafe extern "C" fn load_mmu_table() {
|
||||||
|
let table_base = &crate::mmu_table::MMU_L1_PAGE_TABLE.0 as *const _ as u32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"orr {0}, {0}, #0x5B", // Outer-cacheable, WB
|
||||||
|
"mcr p15, 0, {0}, c2, c0, 0", // Load table pointer
|
||||||
|
inout(reg) table_base => _,
|
||||||
|
options(nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4121
zynq7000-rt/src/mmu_table.rs
Normal file
4121
zynq7000-rt/src/mmu_table.rs
Normal file
File diff suppressed because it is too large
Load Diff
396
zynq7000-rt/src/rt.rs
Normal file
396
zynq7000-rt/src/rt.rs
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
//! Start-up code for Zynq 7000
|
||||||
|
//!
|
||||||
|
//! The bootup routine was kepts as similar to the one
|
||||||
|
//! [provided by Xilinx](https://github.com/Xilinx/embeddedsw/blob/master/lib/bsp/standalone/src/arm/cortexa9/gcc/boot.S)
|
||||||
|
//! as possible. The boot routine includes stack, MMU, cache and .bss/.data section initialization.
|
||||||
|
use cortex_a_rt as _;
|
||||||
|
use cortex_ar::register::{Cpsr, cpsr::ProcessorMode};
|
||||||
|
|
||||||
|
// Start-up code for Armv7-A
|
||||||
|
//
|
||||||
|
// We set up our stacks and `kmain` in system mode.
|
||||||
|
core::arch::global_asm!(
|
||||||
|
r#"
|
||||||
|
.set PSS_L2CC_BASE_ADDR, 0xF8F02000
|
||||||
|
.set PSS_SLCR_BASE_ADDR, 0xF8000000
|
||||||
|
|
||||||
|
.set RESERVED, 0x0fffff00
|
||||||
|
.set LRemap, 0xFE00000F /* set the base address of the peripheral block as not shared */
|
||||||
|
.set L2CCWay, (PSS_L2CC_BASE_ADDR + 0x077C) /*(PSS_L2CC_BASE_ADDR + PSS_L2CC_CACHE_INVLD_WAY_OFFSET)*/
|
||||||
|
.set L2CCSync, (PSS_L2CC_BASE_ADDR + 0x0730) /*(PSS_L2CC_BASE_ADDR + PSS_L2CC_CACHE_SYNC_OFFSET)*/
|
||||||
|
.set L2CCCrtl, (PSS_L2CC_BASE_ADDR + 0x0100) /*(PSS_L2CC_BASE_ADDR + PSS_L2CC_CNTRL_OFFSET)*/
|
||||||
|
.set L2CCAuxCrtl, (PSS_L2CC_BASE_ADDR + 0x0104) /*(PSS_L2CC_BASE_ADDR + XPSS_L2CC_AUX_CNTRL_OFFSET)*/
|
||||||
|
.set L2CCTAGLatReg, (PSS_L2CC_BASE_ADDR + 0x0108) /*(PSS_L2CC_BASE_ADDR + XPSS_L2CC_TAG_RAM_CNTRL_OFFSET)*/
|
||||||
|
.set L2CCDataLatReg, (PSS_L2CC_BASE_ADDR + 0x010C) /*(PSS_L2CC_BASE_ADDR + XPSS_L2CC_DATA_RAM_CNTRL_OFFSET)*/
|
||||||
|
.set L2CCIntClear, (PSS_L2CC_BASE_ADDR + 0x0220) /*(PSS_L2CC_BASE_ADDR + XPSS_L2CC_IAR_OFFSET)*/
|
||||||
|
.set L2CCIntRaw, (PSS_L2CC_BASE_ADDR + 0x021C) /*(PSS_L2CC_BASE_ADDR + XPSS_L2CC_ISR_OFFSET)*/
|
||||||
|
|
||||||
|
.set SLCRlockReg, (PSS_SLCR_BASE_ADDR + 0x04) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_LOCK_OFFSET)*/
|
||||||
|
.set SLCRUnlockReg, (PSS_SLCR_BASE_ADDR + 0x08) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_UNLOCK_OFFSET)*/
|
||||||
|
.set SLCRL2cRamReg, (PSS_SLCR_BASE_ADDR + 0xA1C) /*(PSS_SLCR_BASE_ADDR + XPSS_SLCR_L2C_RAM_OFFSET)*/
|
||||||
|
.set SLCRCPURSTReg, (0xF8000000 + 0x244) /*(XPS_SYS_CTRL_BASEADDR + A9_CPU_RST_CTRL_OFFSET)*/
|
||||||
|
.set EFUSEStatus, (0xF800D000 + 0x10) /*(XPS_EFUSE_BASEADDR + EFUSE_STATUS_OFFSET)*/
|
||||||
|
|
||||||
|
.set CRValMmuCac, 0b01000000000101 /* Enable IDC, and MMU */
|
||||||
|
.set CRValHiVectorAddr, 0b10000000000000 /* Set the Vector address to high, 0xFFFF0000 */
|
||||||
|
|
||||||
|
.set L2CCAuxControl, 0x72360000 /* Enable all prefetching, Cache replacement policy, Parity enable,
|
||||||
|
Event monitor bus enable and Way Size (64 KB) */
|
||||||
|
.set L2CCControl, 0x01 /* Enable L2CC */
|
||||||
|
.set L2CCTAGLatency, 0x0111 /* latency for TAG RAM */
|
||||||
|
.set L2CCDataLatency, 0x0121 /* latency for DATA RAM */
|
||||||
|
|
||||||
|
.set SLCRlockKey, 0x767B /* SLCR lock key */
|
||||||
|
.set SLCRUnlockKey, 0xDF0D /* SLCR unlock key */
|
||||||
|
.set SLCRL2cRamConfig, 0x00020202 /* SLCR L2C ram configuration */
|
||||||
|
|
||||||
|
.set FPEXC_EN, 0x40000000 /* FPU enable bit, (1 << 30) */
|
||||||
|
|
||||||
|
.section .text.startup
|
||||||
|
.align 0
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
.type _start, %function
|
||||||
|
_start:
|
||||||
|
// only allow cpu0 through
|
||||||
|
// Read MPIDR
|
||||||
|
mrc p15,0,r1,c0,c0,5
|
||||||
|
// Extract CPU ID bits. For single-core systems, this should always be 0
|
||||||
|
and r1, r1, #0x3
|
||||||
|
cmp r1, #0
|
||||||
|
beq check_efuse
|
||||||
|
b initialize
|
||||||
|
|
||||||
|
// Zynq specific code. It is recommended to reset CPU1 according to page 160 of the datasheet
|
||||||
|
check_efuse:
|
||||||
|
ldr r0,=EFUSEStatus
|
||||||
|
ldr r1,[r0] /* Read eFuse setting */
|
||||||
|
ands r1,r1,#0x80 /* Check whether device is having single core */
|
||||||
|
beq initialize
|
||||||
|
|
||||||
|
/* single core device, reset cpu1 */
|
||||||
|
ldr r0,=SLCRUnlockReg /* Load SLCR base address base + unlock register */
|
||||||
|
ldr r1,=SLCRUnlockKey /* set unlock key */
|
||||||
|
str r1, [r0] /* Unlock SLCR */
|
||||||
|
|
||||||
|
ldr r0,=SLCRCPURSTReg
|
||||||
|
ldr r1,[r0] /* Read CPU Software Reset Control register */
|
||||||
|
orr r1,r1,#0x22
|
||||||
|
str r1,[r0] /* Reset CPU1 */
|
||||||
|
|
||||||
|
ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */
|
||||||
|
ldr r1,=SLCRlockKey /* set lock key */
|
||||||
|
str r1, [r0] /* lock SLCR */
|
||||||
|
initialize:
|
||||||
|
mrc p15, 0, r0, c0, c0, 0 /* Get the revision */
|
||||||
|
and r5, r0, #0x00f00000
|
||||||
|
and r6, r0, #0x0000000f
|
||||||
|
orr r6, r6, r5, lsr #20-4
|
||||||
|
|
||||||
|
/* set VBAR to the _vector_table address in linker script */
|
||||||
|
ldr r0, =_vector_table
|
||||||
|
mcr p15, 0, r0, c12, c0, 0
|
||||||
|
|
||||||
|
/* Invalidate scu */
|
||||||
|
ldr r7, =0xf8f0000c
|
||||||
|
ldr r6, =0xffff
|
||||||
|
str r6, [r7]
|
||||||
|
|
||||||
|
/* Invalidate caches and TLBs */
|
||||||
|
mov r0,#0 /* r0 = 0 */
|
||||||
|
mcr p15, 0, r0, c8, c7, 0 /* invalidate TLBs */
|
||||||
|
mcr p15, 0, r0, c7, c5, 0 /* invalidate icache */
|
||||||
|
mcr p15, 0, r0, c7, c5, 6 /* Invalidate branch predictor array */
|
||||||
|
bl invalidate_dcache /* invalidate dcache */
|
||||||
|
|
||||||
|
/* Disable MMU, if enabled */
|
||||||
|
mrc p15, 0, r0, c1, c0, 0 /* read CP15 register 1 */
|
||||||
|
bic r0, r0, #0x1 /* clear bit 0 */
|
||||||
|
mcr p15, 0, r0, c1, c0, 0 /* write value back */
|
||||||
|
|
||||||
|
// Set up stacks first.
|
||||||
|
ldr r3, =_stack_top
|
||||||
|
|
||||||
|
// get the current PSR
|
||||||
|
mrs r0, cpsr
|
||||||
|
// mask for mode bits
|
||||||
|
mvn r1, #0x1f
|
||||||
|
and r2, r1, r0
|
||||||
|
// IRQ mode
|
||||||
|
orr r2, r2, {irq_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// IRQ stack pointer
|
||||||
|
mov sp, r3
|
||||||
|
ldr r1, =_irq_stack_size
|
||||||
|
sub r3, r3, r1
|
||||||
|
|
||||||
|
mrs r0, cpsr
|
||||||
|
and r2, r1, r0
|
||||||
|
// Supervisor mode
|
||||||
|
orr r2, r2, {svc_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// Supervisor stack pointer
|
||||||
|
mov sp, r3
|
||||||
|
ldr r1, =_svc_stack_size
|
||||||
|
sub r3, r3, r1
|
||||||
|
|
||||||
|
mrs r0, cpsr
|
||||||
|
and r2, r1, r0
|
||||||
|
// Abort mode
|
||||||
|
orr r2, r2, {abt_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// Abort stack pointer
|
||||||
|
mov sp, r3
|
||||||
|
ldr r1, =_abt_stack_size
|
||||||
|
sub r3, r3, r1
|
||||||
|
|
||||||
|
mrs r0, cpsr
|
||||||
|
and r2, r1, r0
|
||||||
|
// FIQ mode
|
||||||
|
orr r2, r2, {fiq_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// FIQ stack pointer
|
||||||
|
mov sp, r3
|
||||||
|
ldr r1, =_fiq_stack_size
|
||||||
|
sub r3, r3, r1
|
||||||
|
|
||||||
|
mrs r0, cpsr
|
||||||
|
and r2, r1, r0
|
||||||
|
// Undefined mode
|
||||||
|
orr r2, r2, {und_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// Undefined stack pointer
|
||||||
|
mov sp, r3
|
||||||
|
ldr r1, =_und_stack_size
|
||||||
|
sub r3, r3, r1
|
||||||
|
|
||||||
|
mrs r0, cpsr
|
||||||
|
and r2, r1, r0
|
||||||
|
// System mode
|
||||||
|
orr r2, r2, {sys_mode}
|
||||||
|
msr cpsr, r2
|
||||||
|
// System stack pointer (main stack)
|
||||||
|
mov sp, r3
|
||||||
|
|
||||||
|
// set scu enable bit in scu
|
||||||
|
ldr r7, =0xf8f00000
|
||||||
|
ldr r0, [r7]
|
||||||
|
orr r0, r0, #0x1
|
||||||
|
str r0, [r7]
|
||||||
|
|
||||||
|
/* enable MMU and cache */
|
||||||
|
bl load_mmu_table
|
||||||
|
|
||||||
|
mvn r0,#0 /* Load MMU domains -- all ones=manager */
|
||||||
|
mcr p15,0,r0,c3,c0,0
|
||||||
|
|
||||||
|
/* Enable mmu, icahce and dcache */
|
||||||
|
ldr r0,=CRValMmuCac
|
||||||
|
mcr p15,0,r0,c1,c0,0 /* Enable cache and MMU */
|
||||||
|
dsb /* dsb allow the MMU to start up */
|
||||||
|
isb /* isb flush prefetch buffer */
|
||||||
|
|
||||||
|
/* Write to ACTLR */
|
||||||
|
mrc p15, 0, r0, c1, c0, 1 /* Read ACTLR*/
|
||||||
|
orr r0, r0, #(0x01 << 6) /* set SMP bit */
|
||||||
|
orr r0, r0, #(0x01 ) /* Cache/TLB maintenance broadcast */
|
||||||
|
mcr p15, 0, r0, c1, c0, 1 /* Write ACTLR*/
|
||||||
|
|
||||||
|
/* Invalidate L2 Cache and enable L2 Cache*/
|
||||||
|
/* For AMP, assume running on CPU1. Don't initialize L2 Cache (up to Linux) */
|
||||||
|
ldr r0,=L2CCCrtl /* Load L2CC base address base + control register */
|
||||||
|
mov r1, #0 /* force the disable bit */
|
||||||
|
str r1, [r0] /* disable the L2 Caches */
|
||||||
|
|
||||||
|
ldr r0,=L2CCAuxCrtl /* Load L2CC base address base + Aux control register */
|
||||||
|
ldr r1,[r0] /* read the register */
|
||||||
|
ldr r2,=L2CCAuxControl /* set the default bits */
|
||||||
|
orr r1,r1,r2
|
||||||
|
str r1, [r0] /* store the Aux Control Register */
|
||||||
|
|
||||||
|
ldr r0,=L2CCTAGLatReg /* Load L2CC base address base + TAG Latency address */
|
||||||
|
ldr r1,=L2CCTAGLatency /* set the latencies for the TAG*/
|
||||||
|
str r1, [r0] /* store the TAG Latency register Register */
|
||||||
|
|
||||||
|
ldr r0,=L2CCDataLatReg /* Load L2CC base address base + Data Latency address */
|
||||||
|
ldr r1,=L2CCDataLatency /* set the latencies for the Data*/
|
||||||
|
str r1, [r0] /* store the Data Latency register Register */
|
||||||
|
|
||||||
|
ldr r0,=L2CCWay /* Load L2CC base address base + way register*/
|
||||||
|
ldr r2, =0xFFFF
|
||||||
|
str r2, [r0] /* force invalidate */
|
||||||
|
|
||||||
|
ldr r0,=L2CCSync /* need to poll 0x730, PSS_L2CC_CACHE_SYNC_OFFSET */
|
||||||
|
/* Load L2CC base address base + sync register*/
|
||||||
|
/* poll for completion */
|
||||||
|
Sync:
|
||||||
|
ldr r1, [r0]
|
||||||
|
cmp r1, #0
|
||||||
|
bne Sync
|
||||||
|
|
||||||
|
ldr r0,=L2CCIntRaw /* clear pending interrupts */
|
||||||
|
ldr r1,[r0]
|
||||||
|
ldr r0,=L2CCIntClear
|
||||||
|
str r1,[r0]
|
||||||
|
|
||||||
|
ldr r0,=SLCRUnlockReg /* Load SLCR base address base + unlock register */
|
||||||
|
ldr r1,=SLCRUnlockKey /* set unlock key */
|
||||||
|
str r1, [r0] /* Unlock SLCR */
|
||||||
|
|
||||||
|
ldr r0,=SLCRL2cRamReg /* Load SLCR base address base + l2c Ram Control register */
|
||||||
|
ldr r1,=SLCRL2cRamConfig /* set the configuration value */
|
||||||
|
str r1, [r0] /* store the L2c Ram Control Register */
|
||||||
|
|
||||||
|
ldr r0,=SLCRlockReg /* Load SLCR base address base + lock register */
|
||||||
|
ldr r1,=SLCRlockKey /* set lock key */
|
||||||
|
str r1, [r0] /* lock SLCR */
|
||||||
|
|
||||||
|
ldr r0,=L2CCCrtl /* Load L2CC base address base + control register */
|
||||||
|
ldr r1,[r0] /* read the register */
|
||||||
|
mov r2, #L2CCControl /* set the enable bit */
|
||||||
|
orr r1,r1,r2
|
||||||
|
str r1, [r0] /* enable the L2 Caches */
|
||||||
|
|
||||||
|
mov r0, r0
|
||||||
|
mrc p15, 0, r1, c1, c0, 2 /* read cp access control register (CACR) into r1 */
|
||||||
|
orr r1, r1, #(0xf << 20) /* enable full access for p10 & p11 */
|
||||||
|
mcr p15, 0, r1, c1, c0, 2 /* write back into CACR */
|
||||||
|
|
||||||
|
/* enable vfp */
|
||||||
|
fmrx r1, FPEXC /* read the exception register */
|
||||||
|
orr r1,r1, #FPEXC_EN /* set VFP enable bit, leave the others in orig state */
|
||||||
|
fmxr FPEXC, r1 /* write back the exception register */
|
||||||
|
|
||||||
|
mrc p15,0,r0,c1,c0,0 /* flow prediction enable */
|
||||||
|
orr r0, r0, #(0x01 << 11) /* #0x8000 */
|
||||||
|
mcr p15,0,r0,c1,c0,0
|
||||||
|
|
||||||
|
mrc p15,0,r0,c1,c0,1 /* read Auxiliary Control Register */
|
||||||
|
orr r0, r0, #(0x1 << 2) /* enable Dside prefetch */
|
||||||
|
orr r0, r0, #(0x1 << 1) /* enable L2 Prefetch hint */
|
||||||
|
mcr p15,0,r0,c1,c0,1 /* write Auxiliary Control Register */
|
||||||
|
|
||||||
|
mrs r0, cpsr /* get the current PSR */
|
||||||
|
bic r0, r0, #0x100 /* enable asynchronous abort exception */
|
||||||
|
msr cpsr_xsf, r0
|
||||||
|
|
||||||
|
/* Zero BSS and initialize data before calling any function which might require them. */
|
||||||
|
|
||||||
|
// Initialise .bss
|
||||||
|
ldr r0, =__sbss
|
||||||
|
ldr r1, =__ebss
|
||||||
|
mov r2, 0
|
||||||
|
0:
|
||||||
|
cmp r1, r0
|
||||||
|
beq 1f
|
||||||
|
stm r0!, {{r2}}
|
||||||
|
b 0b
|
||||||
|
1:
|
||||||
|
// Initialise .data
|
||||||
|
ldr r0, =__sdata
|
||||||
|
ldr r1, =__edata
|
||||||
|
ldr r2, =__sidata
|
||||||
|
0:
|
||||||
|
cmp r1, r0
|
||||||
|
beq 1f
|
||||||
|
ldm r2!, {{r3}}
|
||||||
|
stm r0!, {{r3}}
|
||||||
|
b 0b
|
||||||
|
1:
|
||||||
|
// Jump to application
|
||||||
|
// Load CPU ID 0, which will be used as a function argument to the boot_core function.
|
||||||
|
mov r0, #0x0
|
||||||
|
bl boot_core
|
||||||
|
// In case the application returns, loop forever
|
||||||
|
b .
|
||||||
|
.size _start, . - _start
|
||||||
|
|
||||||
|
.type _invalidate_dcache, %function
|
||||||
|
invalidate_dcache:
|
||||||
|
mrc p15, 1, r0, c0, c0, 1 /* read CLIDR */
|
||||||
|
ands r3, r0, #0x7000000
|
||||||
|
mov r3, r3, lsr #23 /* cache level value (naturally aligned) */
|
||||||
|
beq finished
|
||||||
|
mov r10, #0 /* start with level 0 */
|
||||||
|
loop1:
|
||||||
|
add r2, r10, r10, lsr #1 /* work out 3xcachelevel */
|
||||||
|
mov r1, r0, lsr r2 /* bottom 3 bits are the Cache type for this level */
|
||||||
|
and r1, r1, #7 /* get those 3 bits alone */
|
||||||
|
cmp r1, #2
|
||||||
|
blt skip /* no cache or only instruction cache at this level */
|
||||||
|
mcr p15, 2, r10, c0, c0, 0 /* write the Cache Size selection register */
|
||||||
|
isb /* isb to sync the change to the CacheSizeID reg */
|
||||||
|
mrc p15, 1, r1, c0, c0, 0 /* reads current Cache Size ID register */
|
||||||
|
and r2, r1, #7 /* extract the line length field */
|
||||||
|
add r2, r2, #4 /* add 4 for the line length offset (log2 16 bytes) */
|
||||||
|
ldr r4, =0x3ff
|
||||||
|
ands r4, r4, r1, lsr #3 /* r4 is the max number on the way size (right aligned) */
|
||||||
|
clz r5, r4 /* r5 is the bit position of the way size increment */
|
||||||
|
ldr r7, =0x7fff
|
||||||
|
ands r7, r7, r1, lsr #13 /* r7 is the max number of the index size (right aligned) */
|
||||||
|
loop2:
|
||||||
|
mov r9, r4 /* r9 working copy of the max way size (right aligned) */
|
||||||
|
loop3:
|
||||||
|
orr r11, r10, r9, lsl r5 /* factor in the way number and cache number into r11 */
|
||||||
|
orr r11, r11, r7, lsl r2 /* factor in the index number */
|
||||||
|
mcr p15, 0, r11, c7, c6, 2 /* invalidate by set/way */
|
||||||
|
subs r9, r9, #1 /* decrement the way number */
|
||||||
|
bge loop3
|
||||||
|
subs r7, r7, #1 /* decrement the index */
|
||||||
|
bge loop2
|
||||||
|
skip:
|
||||||
|
add r10, r10, #2 /* increment the cache number */
|
||||||
|
cmp r3, r10
|
||||||
|
bgt loop1
|
||||||
|
|
||||||
|
finished:
|
||||||
|
mov r10, #0 /* switch back to cache level 0 */
|
||||||
|
mcr p15, 2, r10, c0, c0, 0 /* select current cache level in cssr */
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
bx lr
|
||||||
|
.size invalidate_dcache, . - invalidate_dcache
|
||||||
|
"#,
|
||||||
|
fiq_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Fiq)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
irq_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Irq)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
svc_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Svc)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
und_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Und)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
abt_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Abt)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
sys_mode = const {
|
||||||
|
Cpsr::new_with_raw_value(0)
|
||||||
|
.with_mode(ProcessorMode::Sys)
|
||||||
|
.with_i(true)
|
||||||
|
.with_f(true)
|
||||||
|
.raw_value()
|
||||||
|
},
|
||||||
|
);
|
27
zynq7000/Cargo.toml
Normal file
27
zynq7000/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "zynq7000"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2024"
|
||||||
|
description = "PAC for the Zynq7000 family of SoCs"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||||
|
categories = ["embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
static_assertions = "1.1"
|
||||||
|
derive-mmio = { path = "../../derive-mmio", default-features = false }
|
||||||
|
bitbybit = "1.3"
|
||||||
|
arbitrary-int = "1.3"
|
||||||
|
rustversion = "1"
|
||||||
|
thiserror = { version = "2", default-features = false }
|
||||||
|
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
approx = "0.5"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--generate-link-to-definition"]
|
201
zynq7000/LICENSE-APACHE
Normal file
201
zynq7000/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
21
zynq7000/LICENSE-MIT
Normal file
21
zynq7000/LICENSE-MIT
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Robin A. Mueller
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
8
zynq7000/README.md
Normal file
8
zynq7000/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# PAC for the AMD Zynq 7000 SoC family
|
||||||
|
|
||||||
|
This repository contains the Peripheral Access Crate (PAC) for the AMD Zynq7000 SoC family.
|
||||||
|
|
||||||
|
If you are interested in higher-level abstractions, it is recommended you visit
|
||||||
|
the `zynq7000-hal` HAL crate which build on top of this PAC.
|
||||||
|
|
||||||
|
Check out the documentation for more details.
|
3
zynq7000/build.rs
Normal file
3
zynq7000/build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
202
zynq7000/src/gic.rs
Normal file
202
zynq7000/src/gic.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
//! # GIC (Generic Interrupt Controller) register module.
|
||||||
|
pub use crate::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR};
|
||||||
|
use arbitrary_int::{u3, u5, u10};
|
||||||
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
|
/// Distributor Control Register
|
||||||
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
|
pub struct Dcr {
|
||||||
|
#[bit(1, rw)]
|
||||||
|
enable_non_secure: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
enable_secure: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read only bit. This register only returns fixed constants.
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct TypeRegister {
|
||||||
|
#[bits(11..=15, r)]
|
||||||
|
lspi: u5,
|
||||||
|
#[bit(10, r)]
|
||||||
|
security_extension: bool,
|
||||||
|
#[bits(5..=7, r)]
|
||||||
|
cpu_number: u3,
|
||||||
|
#[bits(0..=4, r)]
|
||||||
|
it_lines_number: u5,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeRegister {
|
||||||
|
pub const SECURITY_EXTNS_BIT: bool = true;
|
||||||
|
/// 31 LSPIs.
|
||||||
|
pub const NUM_LSPI: usize = 0x1f;
|
||||||
|
/// Encoding: 0b001 means that the Cortex-A9 MPCore has 2 processors.
|
||||||
|
pub const CPU_NUMBER_BITS: u8 = 0b001;
|
||||||
|
/// The distributor provides 96 interrupts.
|
||||||
|
pub const IT_LINES_NUMBER: u8 = 0x2;
|
||||||
|
|
||||||
|
pub const NUM_OF_CPUS: usize = 2;
|
||||||
|
pub const NUM_OF_INTERRUPTS: usize = 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Typer = TypeRegister;
|
||||||
|
|
||||||
|
/// GIC Distributor registers.
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
pub struct Gicd {
|
||||||
|
/// Distributor Control Register
|
||||||
|
pub dcr: Dcr,
|
||||||
|
/// Interrupt Controller Type Register
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
pub ictr: Typer,
|
||||||
|
/// Distributor Implementer Identification Register
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
pub iidr: u32,
|
||||||
|
_reserved_0: [u32; 0x1D],
|
||||||
|
/// Interrupt security registers
|
||||||
|
pub isr: [u32; 3],
|
||||||
|
_reserved_1: [u32; 0x1D],
|
||||||
|
/// Interrupt Set-Enable Registers
|
||||||
|
pub iser: [u32; 0x3],
|
||||||
|
_reserved_3: [u32; 0x1D],
|
||||||
|
/// Interrupt Clear-Enable Registers
|
||||||
|
pub icer: [u32; 0x3],
|
||||||
|
_reserved_4: [u32; 0x1D],
|
||||||
|
/// Interrupt Set-Pending Registers
|
||||||
|
pub ispr: [u32; 0x3],
|
||||||
|
_reserved_5: [u32; 0x1D],
|
||||||
|
/// Interrupt Clear-Pending Registers
|
||||||
|
pub icpr: [u32; 0x3],
|
||||||
|
_reserved_6: [u32; 0x1D],
|
||||||
|
/// Active Bit Registers
|
||||||
|
pub abr: [u32; 0x3],
|
||||||
|
_reserved_10: [u32; 0x3D],
|
||||||
|
/// Interrupt Priority Registers
|
||||||
|
pub ipr: [u32; 0x18],
|
||||||
|
_reserved_11: [u32; 0xE8],
|
||||||
|
/// Interrupt Processor Targes Registers
|
||||||
|
pub iptr_sgi: [u32; 0x4],
|
||||||
|
// TODO: Mark those read-only as soon as that works for arrays.
|
||||||
|
pub iptr_ppi: [u32; 0x4],
|
||||||
|
pub iptr_spi: [u32; 0x10],
|
||||||
|
// Those are split in the ARM documentation for some reason..
|
||||||
|
_reserved_12: [u32; 0xE8],
|
||||||
|
/// Interrupt Configuration Registers
|
||||||
|
/// Interupt sensitivity register for software generated interrupts (SGI)
|
||||||
|
pub icfr_0_sgi: u32,
|
||||||
|
/// Interupt sensitivity register for private peripheral interrupts (PPI)
|
||||||
|
pub icfr_1_ppi: u32,
|
||||||
|
pub icfr_2_spi: u32,
|
||||||
|
pub icfr_3_spi: u32,
|
||||||
|
pub icfr_4_spi: u32,
|
||||||
|
pub icfr_5_spi: u32,
|
||||||
|
_reserved_13: [u32; 0x3A],
|
||||||
|
pub ppi_status: u32,
|
||||||
|
pub spi_status_0: u32,
|
||||||
|
pub spi_status_1: u32,
|
||||||
|
_reserved_14: [u32; 0x7D],
|
||||||
|
/// Software Generated Interrupt Register.
|
||||||
|
pub sgir: u32,
|
||||||
|
_reserved_15: [u32; 0x33],
|
||||||
|
pub pidr_4: u32,
|
||||||
|
pub pidr_5: u32,
|
||||||
|
pub pidr_6: u32,
|
||||||
|
pub pidr_7: u32,
|
||||||
|
pub pidr_0: u32,
|
||||||
|
pub pidr_1: u32,
|
||||||
|
pub pidr_2: u32,
|
||||||
|
pub pidr_3: u32,
|
||||||
|
pub cidr: [u32; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(core::mem::size_of::<Gicd>(), 0x1000);
|
||||||
|
|
||||||
|
impl Gicd {
|
||||||
|
/// Create a new Global Interrupt Controller Distributor MMIO instance at the fixed address of
|
||||||
|
/// the processing system.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioGicd<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(GICD_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CPU interface control register.
|
||||||
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
|
pub struct Icr {
|
||||||
|
#[bit(4, rw)]
|
||||||
|
sbpr: bool,
|
||||||
|
#[bit(3, rw)]
|
||||||
|
fiq_en: bool,
|
||||||
|
#[bit(2, rw)]
|
||||||
|
ack_ctrl: bool,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
enable_non_secure: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
enable_secure: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Priority Mask Register
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct PriorityRegister {
|
||||||
|
#[bits(0..=7, rw)]
|
||||||
|
priority: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Interrupt acknowledge register.
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InterruptSignalRegister {
|
||||||
|
#[bits(10..=12, rw)]
|
||||||
|
cpu_id: u3,
|
||||||
|
#[bits(0..=9, rw)]
|
||||||
|
ack_int_id: u10,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GIC CPU interface registers.
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
pub struct Gicc {
|
||||||
|
/// CPU Interface Control Register.
|
||||||
|
pub icr: Icr,
|
||||||
|
/// Interrupt Priority Mask Register.
|
||||||
|
pub pmr: PriorityRegister,
|
||||||
|
/// Binary Point Register.
|
||||||
|
pub bpr: u32,
|
||||||
|
/// Interrupt Acknowledge Register.
|
||||||
|
pub iar: InterruptSignalRegister,
|
||||||
|
/// End of Interrupt Register.
|
||||||
|
pub eoir: InterruptSignalRegister,
|
||||||
|
/// Running Priority Register.
|
||||||
|
pub rpr: PriorityRegister,
|
||||||
|
/// Highest Pending Interrupt Register.
|
||||||
|
pub hpir: InterruptSignalRegister,
|
||||||
|
/// Aliased Binary Point Register
|
||||||
|
pub abpr: u32,
|
||||||
|
_reserved_0: [u32; 0x37],
|
||||||
|
/// CPU Interface Identification Register.
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
pub iidr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(core::mem::size_of::<Gicc>(), 0x100);
|
||||||
|
|
||||||
|
impl Gicc {
|
||||||
|
/// Create a new Global Interrupt Controller CPU MMIO instance at the fixed address of the
|
||||||
|
/// processing system.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioGicc<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(GICC_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
121
zynq7000/src/gpio.rs
Normal file
121
zynq7000/src/gpio.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
//! # GPIO register module.
|
||||||
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MaskedOutput {
|
||||||
|
#[bits(16..=31, w)]
|
||||||
|
pub mask: u16,
|
||||||
|
#[bits(0..=15, rw)]
|
||||||
|
pub output: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct BankCtrl {
|
||||||
|
/// Direction mode
|
||||||
|
dirm: u32,
|
||||||
|
/// Output enable
|
||||||
|
out_en: u32,
|
||||||
|
/// Interrupt mask status
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
int_mask: u32,
|
||||||
|
/// Interrupt enable/unmask
|
||||||
|
#[mmio(Write)]
|
||||||
|
int_en: u32,
|
||||||
|
/// Interrupt disable/mask
|
||||||
|
#[mmio(Write)]
|
||||||
|
int_dis: u32,
|
||||||
|
/// Interrupt status
|
||||||
|
#[mmio(PureRead, Write)]
|
||||||
|
int_sts: u32,
|
||||||
|
/// Interrupt type
|
||||||
|
int_type: u32,
|
||||||
|
/// Interrupt polarity
|
||||||
|
int_pol: u32,
|
||||||
|
/// Interrupt any edge sensitivity
|
||||||
|
int_any: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Gpio {
|
||||||
|
/// Maskable output data (GPIO bank 0, MIO, lower 16 bits)
|
||||||
|
masked_out_0_lsw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 0, MIO, upper 16 bits)
|
||||||
|
masked_out_0_msw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 1, MIO, lower 16 bits)
|
||||||
|
masked_out_1_lsw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 1, MIO, upper 16 bits)
|
||||||
|
masked_out_1_msw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 2, EMIO, lower 16 bits)
|
||||||
|
masked_out_2_lsw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 2, EMIO, upper 16 bits)
|
||||||
|
masked_out_2_msw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 3, EMIO, lower 16 bits)
|
||||||
|
masked_out_3_lsw: MaskedOutput,
|
||||||
|
/// Maskable output data (GPIO bank 3, EMIO, upper 16 bits)
|
||||||
|
masked_out_3_msw: MaskedOutput,
|
||||||
|
|
||||||
|
_reserved_0: [u32; 8],
|
||||||
|
|
||||||
|
/// Output data (GPIO bank 0, MIO)
|
||||||
|
out_0: u32,
|
||||||
|
/// Output data (GPIO bank 1, MIO)
|
||||||
|
out_1: u32,
|
||||||
|
/// Output data (GPIO bank 2, EMIO)
|
||||||
|
out_2: u32,
|
||||||
|
/// Output data (GPIO bank 3, EMIO)
|
||||||
|
out_3: u32,
|
||||||
|
|
||||||
|
_reserved_1: [u32; 4],
|
||||||
|
|
||||||
|
/// Input data (GPIO bank 0, MIO)
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
in_0: u32,
|
||||||
|
/// Input data (GPIO bank 1, MIO)
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
in_1: u32,
|
||||||
|
/// Input data (GPIO bank 2, EMIO)
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
in_2: u32,
|
||||||
|
/// Input data (GPIO bank 3, EMIO)
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
in_3: u32,
|
||||||
|
|
||||||
|
_reserved_2: [u32; 101],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
bank_0: BankCtrl,
|
||||||
|
|
||||||
|
_reserved_3: [u32; 7],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
bank_1: BankCtrl,
|
||||||
|
|
||||||
|
_reserved_4: [u32; 7],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
bank_2: BankCtrl,
|
||||||
|
|
||||||
|
_reserved_5: [u32; 7],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
bank_3: BankCtrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x2E8);
|
||||||
|
|
||||||
|
impl Gpio {
|
||||||
|
/// Create a new XGPIOPS GPIO MMIO instance.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioGpio<'static> {
|
||||||
|
MmioGpio {
|
||||||
|
ptr: 0xE000A000 as *mut Gpio,
|
||||||
|
phantom: core::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
zynq7000/src/gtc.rs
Normal file
60
zynq7000/src/gtc.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//! # Global timer counter module.
|
||||||
|
|
||||||
|
pub const GTC_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0200;
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct GtcCtrl {
|
||||||
|
#[bits(8..=15, rw)]
|
||||||
|
prescaler: u8,
|
||||||
|
#[bit(3, rw)]
|
||||||
|
auto_increment: bool,
|
||||||
|
#[bit(2, rw)]
|
||||||
|
irq_enable: bool,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
comparator_enable: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct InterruptStatus {
|
||||||
|
#[bit(0, rw)]
|
||||||
|
event_flag: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global timer counter.
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Gtc {
|
||||||
|
/// Count register 0, lower 32 bits
|
||||||
|
count_lower: u32,
|
||||||
|
/// Count register 1, upper 32 bits
|
||||||
|
count_upper: u32,
|
||||||
|
/// Control register
|
||||||
|
ctrl: GtcCtrl,
|
||||||
|
/// Interrupt status register
|
||||||
|
#[mmio(PureRead, Write)]
|
||||||
|
isr: InterruptStatus,
|
||||||
|
/// Comparator 0, lower 32 bits
|
||||||
|
comparator_lower: u32,
|
||||||
|
/// Comparator 1, upper 32 bits
|
||||||
|
comparator_upper: u32,
|
||||||
|
/// Auto-increment register
|
||||||
|
auto_increment: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<Gtc>(), 0x1C);
|
||||||
|
|
||||||
|
impl Gtc {
|
||||||
|
/// Create a new GTC MMIO instance at the fixed base address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioGtc<'static> {
|
||||||
|
unsafe { Gtc::new_mmio_at(GTC_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
203
zynq7000/src/i2c.rs
Normal file
203
zynq7000/src/i2c.rs
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//! SPI register module.
|
||||||
|
use arbitrary_int::{u2, u6, u10};
|
||||||
|
|
||||||
|
pub const I2C_0_BASE_ADDR: usize = 0xE000_4000;
|
||||||
|
pub const I2C_1_BASE_ADDR: usize = 0xE000_5000;
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Direction {
|
||||||
|
Receiver = 0b1,
|
||||||
|
Transmitter = 0b0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Mode {
|
||||||
|
Slave = 0b0,
|
||||||
|
Master = 0b1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
|
pub struct Control {
|
||||||
|
/// Divides the input PCLK frequency by this value + 1
|
||||||
|
#[bits(14..=15, rw)]
|
||||||
|
div_a: u2,
|
||||||
|
/// Divides the output from divisor A by this value + 1
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
div_b: u6,
|
||||||
|
#[bit(6, rw)]
|
||||||
|
clear_fifo: bool,
|
||||||
|
#[bit(5, rw)]
|
||||||
|
slv_mon: bool,
|
||||||
|
/// 0: Allow transfer to terminate as soon as all data has been transmitted or received.
|
||||||
|
/// 1: When no more data is avilable for transmit or no more data can be received, hold
|
||||||
|
/// the SCK line low until services by the host.
|
||||||
|
#[bit(4, rw)]
|
||||||
|
hold_bus: bool,
|
||||||
|
/// Should be set to 1. 0: Disabled, NACK transmitted. 1: Enabled, ACK transmitted.
|
||||||
|
#[bit(3, rw)]
|
||||||
|
acken: bool,
|
||||||
|
/// Only used in master mode. 0: Reserved. 1: Normal 7-bit address.
|
||||||
|
#[bit(2, rw)]
|
||||||
|
addressing: bool,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
mode: Mode,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
dir: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct Status {
|
||||||
|
#[bit(8, r)]
|
||||||
|
bus_active: bool,
|
||||||
|
/// FIFO is full and new byte was received. The new byte is not acknowledged and the contents
|
||||||
|
/// of the FIFO remain unchanged.
|
||||||
|
#[bit(6, r)]
|
||||||
|
rx_overflow: bool,
|
||||||
|
/// 1: There is still a byte of data to be transmitted by the interface.
|
||||||
|
#[bit(6, r)]
|
||||||
|
tx_busy: bool,
|
||||||
|
/// Receiver data valid, ca be read from the interface.
|
||||||
|
#[bit(5, r)]
|
||||||
|
rx_valid: bool,
|
||||||
|
#[bit(3, r)]
|
||||||
|
rx_rw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct Addr {
|
||||||
|
#[bits(0..=9, rw)]
|
||||||
|
addr: u10,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct Fifo {
|
||||||
|
#[bits(0..=7, rw)]
|
||||||
|
data: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct InterruptStatus {
|
||||||
|
#[bit(9, rw)]
|
||||||
|
arbitration_lost: bool,
|
||||||
|
#[bit(7, rw)]
|
||||||
|
rx_underflow: bool,
|
||||||
|
#[bit(6, rw)]
|
||||||
|
tx_overflow: bool,
|
||||||
|
#[bit(5, rw)]
|
||||||
|
rx_overflow: bool,
|
||||||
|
#[bit(4, rw)]
|
||||||
|
slave_ready: bool,
|
||||||
|
#[bit(3, rw)]
|
||||||
|
timeout: bool,
|
||||||
|
#[bit(2, rw)]
|
||||||
|
nack: bool,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
data: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
complete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct InterruptMask {
|
||||||
|
#[bit(9, r)]
|
||||||
|
arbitration_lost: bool,
|
||||||
|
#[bit(7, r)]
|
||||||
|
rx_underflow: bool,
|
||||||
|
#[bit(6, r)]
|
||||||
|
tx_overflow: bool,
|
||||||
|
#[bit(5, r)]
|
||||||
|
rx_overflow: bool,
|
||||||
|
#[bit(4, r)]
|
||||||
|
slave_ready: bool,
|
||||||
|
#[bit(3, r)]
|
||||||
|
timeout: bool,
|
||||||
|
#[bit(2, r)]
|
||||||
|
nack: bool,
|
||||||
|
#[bit(1, r)]
|
||||||
|
data: bool,
|
||||||
|
#[bit(0, r)]
|
||||||
|
complete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct InterruptControl {
|
||||||
|
#[bit(9, w)]
|
||||||
|
arbitration_lost: bool,
|
||||||
|
#[bit(7, w)]
|
||||||
|
rx_underflow: bool,
|
||||||
|
#[bit(6, w)]
|
||||||
|
tx_overflow: bool,
|
||||||
|
#[bit(5, w)]
|
||||||
|
rx_overflow: bool,
|
||||||
|
#[bit(4, w)]
|
||||||
|
slave_ready: bool,
|
||||||
|
#[bit(3, w)]
|
||||||
|
timeout: bool,
|
||||||
|
#[bit(2, w)]
|
||||||
|
nack: bool,
|
||||||
|
#[bit(1, w)]
|
||||||
|
data: bool,
|
||||||
|
#[bit(0, w)]
|
||||||
|
complete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct Timeout {
|
||||||
|
/// Reset value: 0x1F.
|
||||||
|
#[bits(0..=7, rw)]
|
||||||
|
timeout: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||||
|
pub struct TransferSize {
|
||||||
|
#[bits(0..=7, rw)]
|
||||||
|
size: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct I2c {
|
||||||
|
cr: Control,
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
sr: Status,
|
||||||
|
addr: Addr,
|
||||||
|
#[mmio(Read, Write)]
|
||||||
|
data: Fifo,
|
||||||
|
#[mmio(PureRead, Write, Modify)]
|
||||||
|
isr: InterruptStatus,
|
||||||
|
transfer_size: TransferSize,
|
||||||
|
slave_pause: u32,
|
||||||
|
timeout: Timeout,
|
||||||
|
#[mmio(PureRead)]
|
||||||
|
imr: InterruptMask,
|
||||||
|
#[mmio(Write)]
|
||||||
|
ier: InterruptControl,
|
||||||
|
#[mmio(Write)]
|
||||||
|
idr: InterruptControl,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2c {
|
||||||
|
/// Create a new I2C MMIO instance for I2C0 at address [I2C_0_BASE_ADDR].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
pub const unsafe fn new_mmio_fixed_0() -> MmioI2c<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(I2C_0_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new I2C MMIO instance for I2C1 at address [I2C_1_BASE_ADDR].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
pub const unsafe fn new_mmio_fixed_1() -> MmioI2c<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(I2C_1_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
85
zynq7000/src/lib.rs
Normal file
85
zynq7000/src/lib.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//! # Rust peripheral acess crate to the AMD Zynq 7000 SoCs
|
||||||
|
//!
|
||||||
|
//! This crate provides a low-level register access API building on the
|
||||||
|
//! [`derive-mmio` crate](https://crates.io/crates/derive-mmio). However, its structure
|
||||||
|
//! is similar to the crates auto-generated by [`svd2rust`](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api).
|
||||||
|
//!
|
||||||
|
//! This crate is purposely kept low-level to allow building higher level abstractions like HALs
|
||||||
|
//! on top of it.
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate std;
|
||||||
|
|
||||||
|
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
|
||||||
|
|
||||||
|
pub mod gic;
|
||||||
|
pub mod gpio;
|
||||||
|
pub mod gtc;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod mpcore;
|
||||||
|
pub mod slcr;
|
||||||
|
pub mod spi;
|
||||||
|
pub mod ttc;
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
|
static PERIPHERALS_TAKEN: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
/// This is a collection of all the processing peripherals.
|
||||||
|
///
|
||||||
|
/// It is a singleton which exposes all peripherals supported by this crate.
|
||||||
|
/// The [`svd2rust` documentation](https://docs.rs/svd2rust/latest/svd2rust/#peripheral-api)
|
||||||
|
/// provides some more information about this.
|
||||||
|
pub struct PsPeripherals {
|
||||||
|
pub gicc: gic::MmioGicc<'static>,
|
||||||
|
pub gicd: gic::MmioGicd<'static>,
|
||||||
|
pub uart_0: uart::MmioUart<'static>,
|
||||||
|
pub uart_1: uart::MmioUart<'static>,
|
||||||
|
pub spi_0: spi::MmioSpi<'static>,
|
||||||
|
pub spi_1: spi::MmioSpi<'static>,
|
||||||
|
pub i2c_0: i2c::MmioI2c<'static>,
|
||||||
|
pub i2c_1: i2c::MmioI2c<'static>,
|
||||||
|
pub gtc: gtc::MmioGtc<'static>,
|
||||||
|
pub gpio: gpio::MmioGpio<'static>,
|
||||||
|
pub slcr: slcr::MmioSlcr<'static>,
|
||||||
|
pub ttc_0: ttc::MmioTtc<'static>,
|
||||||
|
pub ttc_1: ttc::MmioTtc<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PsPeripherals {
|
||||||
|
/// Returns all supported processing system peripherals *once*.
|
||||||
|
pub fn take() -> Option<Self> {
|
||||||
|
let taken = PERIPHERALS_TAKEN.swap(true, Ordering::Relaxed);
|
||||||
|
if taken {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(unsafe { Self::steal() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unchecked version of [Self::take].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each of the returned peripherals must be used at most once.
|
||||||
|
pub unsafe fn steal() -> Self {
|
||||||
|
unsafe {
|
||||||
|
Self {
|
||||||
|
gicc: gic::Gicc::new_mmio_fixed(),
|
||||||
|
gicd: gic::Gicd::new_mmio_fixed(),
|
||||||
|
uart_0: uart::Uart::new_mmio_fixed_0(),
|
||||||
|
uart_1: uart::Uart::new_mmio_fixed_1(),
|
||||||
|
gtc: gtc::Gtc::new_mmio_fixed(),
|
||||||
|
gpio: gpio::Gpio::new_mmio_fixed(),
|
||||||
|
slcr: slcr::Slcr::new_mmio_fixed(),
|
||||||
|
spi_0: spi::Spi::new_mmio_fixed_0(),
|
||||||
|
spi_1: spi::Spi::new_mmio_fixed_1(),
|
||||||
|
i2c_0: i2c::I2c::new_mmio_fixed_0(),
|
||||||
|
i2c_1: i2c::I2c::new_mmio_fixed_1(),
|
||||||
|
ttc_0: ttc::Ttc::new_mmio_fixed_0(),
|
||||||
|
ttc_1: ttc::Ttc::new_mmio_fixed_1(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
zynq7000/src/mpcore.rs
Normal file
97
zynq7000/src/mpcore.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
//! Application Processing Unit Registers (mpcore)
|
||||||
|
//!
|
||||||
|
//! Based on p.1483 of the Zynq-7000 TRM.
|
||||||
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
gic::{Gicc, Gicd, MmioGicc, MmioGicd},
|
||||||
|
gtc::{Gtc, MmioGtc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
|
||||||
|
pub const SCU_BASE_ADDR: usize = MPCORE_BASE_ADDR;
|
||||||
|
pub const GICC_BASE_ADDR: usize = MPCORE_BASE_ADDR + 0x100;
|
||||||
|
pub const GICD_BASE_ADDR: usize = MPCORE_BASE_ADDR + 0x1000;
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Scu {
|
||||||
|
ctrl: u32,
|
||||||
|
config: u32,
|
||||||
|
cpu_power_status: u32,
|
||||||
|
invalidate_all_regs_in_secure_state: u32,
|
||||||
|
_reserved_0: [u32; 0xC],
|
||||||
|
filtering_start_addr: u32,
|
||||||
|
filtering_end_addr: u32,
|
||||||
|
_reserved_1: [u32; 0x2],
|
||||||
|
access_ctrl: u32,
|
||||||
|
non_secure_access_ctrl: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scu {
|
||||||
|
/// Create a new Snoop Control Unit interface at the fixed base address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioScu<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(SCU_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(core::mem::size_of::<Scu>(), 0x58);
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Mpcore {
|
||||||
|
#[mmio(inner)]
|
||||||
|
scu: Scu,
|
||||||
|
|
||||||
|
_reserved_0: [u32; 0x2A],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
gicc: Gicc,
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
gt: Gtc,
|
||||||
|
|
||||||
|
_reserved_1: [u32; 0xF9],
|
||||||
|
|
||||||
|
private_timer_load: u32,
|
||||||
|
private_timer_counter: u32,
|
||||||
|
private_timer_ctrl: u32,
|
||||||
|
private_interrupt_status: u32,
|
||||||
|
|
||||||
|
_reserved_2: [u32; 0x4],
|
||||||
|
|
||||||
|
watchdog_load: u32,
|
||||||
|
watchdog_counter: u32,
|
||||||
|
watchdog_ctrl: u32,
|
||||||
|
watchdog_interrupt_status: u32,
|
||||||
|
watchdog_reset_status: u32,
|
||||||
|
watchdog_disable: u32,
|
||||||
|
|
||||||
|
_reserved_3: [u32; 0x272],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
gicd: Gicd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert_eq!(core::mem::size_of::<Mpcore>(), 0x2000);
|
||||||
|
|
||||||
|
impl Mpcore {
|
||||||
|
/// Create a MP core peripheral interface at the fixed base address.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||||
|
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||||
|
/// interfere with each other.
|
||||||
|
#[inline]
|
||||||
|
pub const unsafe fn new_mmio_fixed() -> MmioMpcore<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(MPCORE_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
364
zynq7000/src/slcr/clocks.rs
Normal file
364
zynq7000/src/slcr/clocks.rs
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
//! SLCR clock control registers.
|
||||||
|
//!
|
||||||
|
//! Writing any of these registers required unlocking the SLCR first.
|
||||||
|
use super::{CLOCK_CONTROL_OFFSET, SLCR_BASE_ADDR};
|
||||||
|
use arbitrary_int::{u4, u6, u7, u10};
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BypassForce {
|
||||||
|
EnabledOrSetByBootMode = 0b0,
|
||||||
|
Bypassed = 0b1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum BypassQual {
|
||||||
|
BypassForceBit = 0b0,
|
||||||
|
BootModeFourthBit = 0b1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PllCtrl {
|
||||||
|
/// Feedback divisor for the PLL.
|
||||||
|
///
|
||||||
|
/// NOTE: Before changing this value, the PLL must first be bypassed and then put into
|
||||||
|
/// reset mode.
|
||||||
|
#[bits(12..=18, rw)]
|
||||||
|
fdiv: u7,
|
||||||
|
/// Select source for the ARM PLL bypass control
|
||||||
|
#[bit(4, rw)]
|
||||||
|
bypass_force: BypassForce,
|
||||||
|
/// Select source for the ARM PLL bypass control
|
||||||
|
#[bit(3, rw)]
|
||||||
|
bypass_qual: BypassQual,
|
||||||
|
// Power-down control
|
||||||
|
#[bit(1, rw)]
|
||||||
|
pwrdwn: bool,
|
||||||
|
/// Reset control
|
||||||
|
#[bit(0, rw)]
|
||||||
|
reset: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PllCfg {
|
||||||
|
#[bits(12..=21, rw)]
|
||||||
|
lock_count: u10,
|
||||||
|
/// Charge Pump control
|
||||||
|
#[bits(8..=11, rw)]
|
||||||
|
pll_cp: u4,
|
||||||
|
/// Loop resistor control
|
||||||
|
#[bits(4..=7, rw)]
|
||||||
|
pll_res: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PllStatus {
|
||||||
|
#[bit(5)]
|
||||||
|
io_pll_stable: bool,
|
||||||
|
#[bit(4)]
|
||||||
|
ddr_pll_stable: bool,
|
||||||
|
#[bit(3)]
|
||||||
|
arm_pll_stable: bool,
|
||||||
|
#[bit(2)]
|
||||||
|
io_pll_lock: bool,
|
||||||
|
#[bit(1)]
|
||||||
|
drr_pll_lock: bool,
|
||||||
|
#[bit(0)]
|
||||||
|
arm_pll_lock: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FpgaClkControl {
|
||||||
|
// Reset value 0x1
|
||||||
|
#[bits(20..=25, rw)]
|
||||||
|
divisor_1: u6,
|
||||||
|
// Reset value 0x18
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor_0: u6,
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelIo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FpgaClkBlock {
|
||||||
|
clk_ctrl: FpgaClkControl,
|
||||||
|
thr_ctrl: u32,
|
||||||
|
thr_cnt: u32,
|
||||||
|
thr_status: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<FpgaClkBlock>(), 0x10);
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u2, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SrcSelArm {
|
||||||
|
ArmPll = 0b00,
|
||||||
|
ArmPllAlt = 0b01,
|
||||||
|
DdrPll = 0b10,
|
||||||
|
IoPll = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct ArmClkCtrl {
|
||||||
|
#[bit(28, rw)]
|
||||||
|
cpu_peri_clk_act: bool,
|
||||||
|
#[bit(27, rw)]
|
||||||
|
cpu_1x_clk_act: bool,
|
||||||
|
#[bit(26, rw)]
|
||||||
|
cpu_2x_clk_act: bool,
|
||||||
|
#[bit(25, rw)]
|
||||||
|
cpu_3or2x_clk_act: bool,
|
||||||
|
#[bit(24, rw)]
|
||||||
|
cpu_6or4x_clk_act: bool,
|
||||||
|
/// Reset value: 0x4. There is a requirement for the quality of the high speed clock that
|
||||||
|
/// it has to be divided by an even number. This field must be equal to or greater than 2.
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor: u6,
|
||||||
|
/// Reset value: 0x0
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelArm,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct DdrClkCtrl {
|
||||||
|
/// Divisor for DDR 2x clock. Reset value: 0x6
|
||||||
|
#[bits(26..=31, rw)]
|
||||||
|
div_2x_clk: u6,
|
||||||
|
/// Divisor for DDR 3x clock. Only even divisors are allowed! Reset value: 0x4
|
||||||
|
#[bits(20..=25, rw)]
|
||||||
|
div_3x_clk: u6,
|
||||||
|
/// Reset value: 0x1
|
||||||
|
#[bit(1, rw)]
|
||||||
|
ddr_2x_clk_act: bool,
|
||||||
|
/// Reset value: 0x1
|
||||||
|
#[bit(0, rw)]
|
||||||
|
ddr_3x_clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct DciClkCtrl {
|
||||||
|
/// Second cascade divider. Reset value: 0x1E
|
||||||
|
#[bits(20..=25, rw)]
|
||||||
|
divisor_1: u6,
|
||||||
|
/// Reset value: 0x32
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor_0: u6,
|
||||||
|
/// Reset value: 0x1
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClockRatioSelectReg {
|
||||||
|
/// Reset value: 0x1 (6:2:1 clock)
|
||||||
|
#[bit(0, rw)]
|
||||||
|
sel: ClockRatioSelect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ClockRatioSelect {
|
||||||
|
/// 4:2:1 clock ratio, which is an abbreviation for 4:2:2:1.
|
||||||
|
FourToTwoToOne = 0b0,
|
||||||
|
/// 6:2:1 clock ratio, which is an abbreviation for 6:3:2:1.
|
||||||
|
SixToTwoToOne = 0b1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u2, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SrcSelIo {
|
||||||
|
IoPll = 0b00,
|
||||||
|
IoPllAlt = 0b01,
|
||||||
|
ArmPll = 0b10,
|
||||||
|
DdrPll = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GigEthClkCtrl {
|
||||||
|
#[bits(20..=25, rw)]
|
||||||
|
divisor_1: u6,
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor_0: u6,
|
||||||
|
#[bit(6, rw)]
|
||||||
|
use_emio_tx_clk: bool,
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelIo,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CanClkCtrl {
|
||||||
|
#[bits(20..=25, rw)]
|
||||||
|
divisor_1: u6,
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor_0: u6,
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelIo,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
clk_1_act: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_0_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct SingleCommonPeriphIoClkCtrl {
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor: u6,
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelIo,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DualCommonPeriphIoClkCtrl {
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor: u6,
|
||||||
|
#[bits(4..=5, rw)]
|
||||||
|
srcsel: SrcSelIo,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
clk_1_act: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_0_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u3, exhaustive = true)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SrcSelTpiu {
|
||||||
|
IoPll = 0b000,
|
||||||
|
IoPllAlt = 0b001,
|
||||||
|
ArmPll = 0b010,
|
||||||
|
DdrPll = 0b011,
|
||||||
|
EmioTraceClk = 0b100,
|
||||||
|
EmioTraceClkAlt0 = 0b101,
|
||||||
|
EmioTraceClkAlt1 = 0b110,
|
||||||
|
EmioTraceClkAlt2 = 0b111,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct TracePortClkCtrl {
|
||||||
|
#[bits(8..=13, rw)]
|
||||||
|
divisor: u6,
|
||||||
|
#[bits(4..=6, rw)]
|
||||||
|
srcsel: SrcSelTpiu,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
clk_1x_clk_act: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AMBA peripheral clock control.
|
||||||
|
///
|
||||||
|
/// These clocks must be enabled if you want to read from the peripheral register space.
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AperClkCtrl {
|
||||||
|
#[bit(24, rw)]
|
||||||
|
smc_1x_clk_act: bool,
|
||||||
|
#[bit(23, rw)]
|
||||||
|
lqspi_1x_clk_act: bool,
|
||||||
|
#[bit(22, rw)]
|
||||||
|
gpio_1x_clk_act: bool,
|
||||||
|
#[bit(21, rw)]
|
||||||
|
uart_1_1x_clk_act: bool,
|
||||||
|
#[bit(20, rw)]
|
||||||
|
uart_0_1x_clk_act: bool,
|
||||||
|
#[bit(19, rw)]
|
||||||
|
i2c_1_1x_clk_act: bool,
|
||||||
|
#[bit(18, rw)]
|
||||||
|
i2c_0_1x_clk_act: bool,
|
||||||
|
#[bit(17, rw)]
|
||||||
|
can_1_1x_clk_act: bool,
|
||||||
|
#[bit(16, rw)]
|
||||||
|
can_0_1x_clk_act: bool,
|
||||||
|
#[bit(15, rw)]
|
||||||
|
spi_1_1x_clk_act: bool,
|
||||||
|
#[bit(14, rw)]
|
||||||
|
spi_0_1x_clk_act: bool,
|
||||||
|
#[bit(11, rw)]
|
||||||
|
sdio_1_1x_clk_act: bool,
|
||||||
|
#[bit(10, rw)]
|
||||||
|
sdio_0_1x_clk_act: bool,
|
||||||
|
#[bit(7, rw)]
|
||||||
|
gem_1_1x_clk_act: bool,
|
||||||
|
#[bit(6, rw)]
|
||||||
|
gem_0_1x_clk_act: bool,
|
||||||
|
#[bit(3, rw)]
|
||||||
|
usb_1_cpu_1x_clk_act: bool,
|
||||||
|
#[bit(2, rw)]
|
||||||
|
usb_0_cpu_1x_clk_act: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
dma_cpu_2x_clk_act: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ClockControl {
|
||||||
|
arm_pll: PllCtrl,
|
||||||
|
ddr_pll: PllCtrl,
|
||||||
|
io_pll: PllCtrl,
|
||||||
|
pll_status: PllStatus,
|
||||||
|
arm_pll_cfg: PllCfg,
|
||||||
|
ddr_pll_cfg: PllCfg,
|
||||||
|
io_pll_cfg: PllCfg,
|
||||||
|
_gap0: u32,
|
||||||
|
arm_clk_ctrl: ArmClkCtrl,
|
||||||
|
ddr_clk_ctrl: DdrClkCtrl,
|
||||||
|
dci_clk_ctrl: DciClkCtrl,
|
||||||
|
/// AMBA peripheral clock control
|
||||||
|
aper_clk_ctrl: AperClkCtrl,
|
||||||
|
usb_0_clk_ctrl: u32,
|
||||||
|
usb_1_clk_ctrl: u32,
|
||||||
|
gem_0_rclk_ctrl: u32,
|
||||||
|
gem_1_rclk_ctrl: u32,
|
||||||
|
gem_0_clk_ctrl: GigEthClkCtrl,
|
||||||
|
gem_1_clk_ctrl: GigEthClkCtrl,
|
||||||
|
smc_clk_ctrl: SingleCommonPeriphIoClkCtrl,
|
||||||
|
lqspi_clk_ctrl: SingleCommonPeriphIoClkCtrl,
|
||||||
|
sdio_clk_ctrl: DualCommonPeriphIoClkCtrl,
|
||||||
|
uart_clk_ctrl: DualCommonPeriphIoClkCtrl,
|
||||||
|
spi_clk_ctrl: DualCommonPeriphIoClkCtrl,
|
||||||
|
can_clk_ctrl: CanClkCtrl,
|
||||||
|
can_mioclk_ctrl: u32,
|
||||||
|
/// Debug or Trace Port clock control.
|
||||||
|
dbg_clk_ctrl: TracePortClkCtrl,
|
||||||
|
pcap_clk_ctrl: SingleCommonPeriphIoClkCtrl,
|
||||||
|
topsw_clk_ctrl: u32,
|
||||||
|
#[mmio(inner)]
|
||||||
|
fpga_0_clk_ctrl: FpgaClkBlock,
|
||||||
|
#[mmio(inner)]
|
||||||
|
fpga_1_clk_ctrl: FpgaClkBlock,
|
||||||
|
#[mmio(inner)]
|
||||||
|
fpga_2_clk_ctrl: FpgaClkBlock,
|
||||||
|
#[mmio(inner)]
|
||||||
|
fpga_3_clk_ctrl: FpgaClkBlock,
|
||||||
|
_gap1: [u32; 5],
|
||||||
|
clk_621_true: ClockRatioSelectReg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockControl {
|
||||||
|
/// Create a new handle to this peripheral.
|
||||||
|
///
|
||||||
|
/// Writing to this register requires unlocking the SLCR registers first.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If you create multiple instances of this handle at the same time, you are responsible for
|
||||||
|
/// ensuring that there are no read-modify-write races on any of the registers.
|
||||||
|
pub unsafe fn new_mmio_fixed() -> MmioClockControl<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + CLOCK_CONTROL_OFFSET) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<ClockControl>(), 0xC8);
|
41
zynq7000/src/slcr/mio.rs
Normal file
41
zynq7000/src/slcr/mio.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//! # SLCR MIO (Multiplexed I/O) configuration registers
|
||||||
|
//!
|
||||||
|
//! Writing any of these registers required unlocking the SLCR first.
|
||||||
|
use arbitrary_int::{u2, u3};
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||||
|
pub enum Speed {
|
||||||
|
SlowCmosEdge = 0b0,
|
||||||
|
FastCmosEdge = 0b1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u3)]
|
||||||
|
pub enum IoType {
|
||||||
|
LvCmos18 = 0b001,
|
||||||
|
LvCmos25 = 0b010,
|
||||||
|
LvCmos33 = 0b011,
|
||||||
|
Hstl = 0b100,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Config {
|
||||||
|
#[bit(13, rw)]
|
||||||
|
disable_hstl_rcvr: bool,
|
||||||
|
#[bit(12, rw)]
|
||||||
|
pullup: bool,
|
||||||
|
#[bits(9..=11, rw)]
|
||||||
|
io_type: Option<IoType>,
|
||||||
|
#[bit(8, rw)]
|
||||||
|
speed: Speed,
|
||||||
|
#[bits(5..=7, rw)]
|
||||||
|
l3_sel: u3,
|
||||||
|
#[bits(3..=4, rw)]
|
||||||
|
l2_sel: u2,
|
||||||
|
#[bit(2, rw)]
|
||||||
|
l1_sel: bool,
|
||||||
|
#[bit(1, rw)]
|
||||||
|
l0_sel: bool,
|
||||||
|
#[bit(0, rw)]
|
||||||
|
tri_enable: bool,
|
||||||
|
}
|
206
zynq7000/src/slcr/mod.rs
Normal file
206
zynq7000/src/slcr/mod.rs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
//! System Level Control Registers (slcr)
|
||||||
|
//!
|
||||||
|
//! Writing any of these registers required unlocking the SLCR first.
|
||||||
|
use arbitrary_int::u4;
|
||||||
|
pub use clocks::{ClockControl, MmioClockControl};
|
||||||
|
pub use reset::{MmioResetControl, ResetControl};
|
||||||
|
|
||||||
|
const SLCR_BASE_ADDR: usize = 0xF8000000;
|
||||||
|
const CLOCK_CONTROL_OFFSET: usize = 0x100;
|
||||||
|
const RESET_BLOCK_OFFSET: usize = 0x200;
|
||||||
|
const GPIOB_OFFSET: usize = 0xB00;
|
||||||
|
const DDRIOB_OFFSET: usize = 0xB40;
|
||||||
|
|
||||||
|
pub mod clocks;
|
||||||
|
pub mod mio;
|
||||||
|
pub mod reset;
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct DdrIoB {
|
||||||
|
ddriob_addr0: u32,
|
||||||
|
ddriob_addr1: u32,
|
||||||
|
ddriob_data0: u32,
|
||||||
|
ddriob_data1: u32,
|
||||||
|
ddriob_diff0: u32,
|
||||||
|
ddriob_diff1: u32,
|
||||||
|
ddriob_clock: u32,
|
||||||
|
ddriob_drive_slew_addr: u32,
|
||||||
|
ddriob_drive_slew_data: u32,
|
||||||
|
ddriob_drive_slew_diff: u32,
|
||||||
|
ddriob_drive_slew_clock: u32,
|
||||||
|
ddriob_ddr_ctrl: u32,
|
||||||
|
ddriob_dci_ctrl: u32,
|
||||||
|
ddriob_dci_status: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DdrIoB {
|
||||||
|
/// Create a new handle to this peripheral.
|
||||||
|
///
|
||||||
|
/// Writing to this register requires unlocking the SLCR registers first.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If you create multiple instances of this handle at the same time, you are responsible for
|
||||||
|
/// ensuring that there are no read-modify-write races on any of the registers.
|
||||||
|
pub unsafe fn new_mmio_fixed() -> MmioDdrIoB<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + DDRIOB_OFFSET) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<DdrIoB>(), 0x38);
|
||||||
|
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GpiobCtrl {
|
||||||
|
ctrl: u32,
|
||||||
|
cfg_cmos18: u32,
|
||||||
|
cfg_cmos25: u32,
|
||||||
|
cfg_cmos33: u32,
|
||||||
|
_gap17: u32,
|
||||||
|
cfg_hstl: u32,
|
||||||
|
drvr_bias_ctrl: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpiobCtrl {
|
||||||
|
/// Create a new handle to this peripheral.
|
||||||
|
///
|
||||||
|
/// Writing to this register requires unlocking the SLCR registers first.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If you create multiple instances of this handle at the same time, you are responsible for
|
||||||
|
/// ensuring that there are no read-modify-write races on any of the registers.
|
||||||
|
pub unsafe fn new_mmio_fixed() -> MmioGpiobCtrl<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + GPIOB_OFFSET) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct BootModeRegister {
|
||||||
|
#[bit(4, r)]
|
||||||
|
pll_bypass: bool,
|
||||||
|
#[bits(0..=3, r)]
|
||||||
|
boot_mode: u4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitenum(u4)]
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum LevelShifterConfig {
|
||||||
|
DisableAll = 0x00,
|
||||||
|
EnablePsToPl = 0xA,
|
||||||
|
EnableAll = 0xF,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitbybit::bitfield(u32)]
|
||||||
|
pub struct LevelShifterReg {
|
||||||
|
#[bits(0..=3, rw)]
|
||||||
|
user_lvl_shftr_en: Option<LevelShifterConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System Level Control Registers
|
||||||
|
#[derive(derive_mmio::Mmio)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Slcr {
|
||||||
|
/// Secure configuration lock.
|
||||||
|
scl: u32,
|
||||||
|
/// SLCR write protection lock
|
||||||
|
lock: u32,
|
||||||
|
/// SLCR write protection unlock
|
||||||
|
unlock: u32,
|
||||||
|
/// SLCR write protection status
|
||||||
|
lock_status: u32,
|
||||||
|
|
||||||
|
_gap0: [u32; 0x3C],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
clk_ctrl: ClockControl,
|
||||||
|
|
||||||
|
_gap1: [u32; 0x0E],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
reset_ctrl: ResetControl,
|
||||||
|
|
||||||
|
_gap2: [u32; 0x02],
|
||||||
|
|
||||||
|
reboot_status: u32,
|
||||||
|
boot_mode: BootModeRegister,
|
||||||
|
|
||||||
|
_gap3: [u32; 0x28],
|
||||||
|
|
||||||
|
apu_ctrl: u32,
|
||||||
|
wdt_clk_set: u32,
|
||||||
|
|
||||||
|
_gap4: [u32; 0x4E],
|
||||||
|
|
||||||
|
tz_dma_ns: u32,
|
||||||
|
tz_dma_irq_ns: u32,
|
||||||
|
tz_dma_periph_ns: u32,
|
||||||
|
|
||||||
|
_gap5: [u32; 0x39],
|
||||||
|
|
||||||
|
pss_idcode: u32,
|
||||||
|
|
||||||
|
_gap6: [u32; 0x33],
|
||||||
|
|
||||||
|
ddr_urgent: u32,
|
||||||
|
_gap7: [u32; 0x02],
|
||||||
|
ddr_cal_start: u32,
|
||||||
|
_gap8: u32,
|
||||||
|
ddr_ref_start: u32,
|
||||||
|
ddr_cmd_status: u32,
|
||||||
|
ddr_urgent_sel: u32,
|
||||||
|
ddr_dfi_status: u32,
|
||||||
|
|
||||||
|
_gap9: [u32; 0x37],
|
||||||
|
|
||||||
|
mio_pins: [mio::Config; 0x36],
|
||||||
|
|
||||||
|
_gap10: [u32; 0x0B],
|
||||||
|
|
||||||
|
mio_loopback: u32,
|
||||||
|
_gap11: u32,
|
||||||
|
mio_mst_tri_0: u32,
|
||||||
|
mio_mst_tri_1: u32,
|
||||||
|
_gap12: [u32; 7],
|
||||||
|
sd_0_wp_cd_sel: u32,
|
||||||
|
sd_1_wp_cd_sel: u32,
|
||||||
|
|
||||||
|
_gap13: [u32; 0x32],
|
||||||
|
|
||||||
|
lvl_shftr_en: LevelShifterReg,
|
||||||
|
|
||||||
|
_gap14: [u32; 0x03],
|
||||||
|
|
||||||
|
ocm_cfg: u32,
|
||||||
|
|
||||||
|
_gap15: [u32; 0x42],
|
||||||
|
|
||||||
|
reserved: u32,
|
||||||
|
|
||||||
|
_gap16: [u32; 0x38],
|
||||||
|
|
||||||
|
_gap18: [u32; 0x09],
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
gpiob: GpiobCtrl,
|
||||||
|
|
||||||
|
#[mmio(inner)]
|
||||||
|
ddriob: DdrIoB,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(core::mem::size_of::<Slcr>(), 0xB78);
|
||||||
|
|
||||||
|
impl Slcr {
|
||||||
|
/// Create a new handle to this peripheral.
|
||||||
|
///
|
||||||
|
/// Writing to this register requires unlocking the SLCR registers first.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// If you create multiple instances of this handle at the same time, you are responsible for
|
||||||
|
/// ensuring that there are no read-modify-write races on any of the registers.
|
||||||
|
pub unsafe fn new_mmio_fixed() -> MmioSlcr<'static> {
|
||||||
|
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR) }
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user