init commit

This commit is contained in:
2025-02-19 11:00:04 +01:00
commit f842673e3a
104 changed files with 21595 additions and 0 deletions

27
zynq7000/Cargo.toml Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
}

202
zynq7000/src/gic.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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) }
}
}

View File

@ -0,0 +1,79 @@
use super::{RESET_BLOCK_OFFSET, SLCR_BASE_ADDR};
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DualClockReset {
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
periph1_cpu1x_rst: bool,
/// Peripheral 0 AMBA software reset.
#[bit(0, rw)]
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DualRefAndClockReset {
/// Periperal 1 Reference software reset.
#[bit(3, rw)]
periph1_ref_rst: bool,
/// Peripheral 0 Reference software reset.
#[bit(2, rw)]
periph0_ref_rst: bool,
/// Peripheral 1 AMBA software reset.
#[bit(1, rw)]
periph1_cpu1x_rst: bool,
/// Peripheral 0 AMBA software reset.
#[bit(0, rw)]
periph0_cpu1x_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct GpioClockReset {
#[bit(0, rw)]
gpio_cpu1x_rst: bool,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct ResetControl {
/// PS Software reset control
pss: u32,
ddr: u32,
/// Central interconnect reset control
topsw: u32,
dmac: u32,
usb: u32,
gem: u32,
sdio: DualRefAndClockReset,
spi: DualRefAndClockReset,
can: DualClockReset,
i2c: DualClockReset,
uart: DualRefAndClockReset,
gpio: GpioClockReset,
lqspi: u32,
smc: u32,
ocm: u32,
_gap0: u32,
fpga: u32,
a9_cpu: u32,
_gap1: u32,
rs_awdt: u32,
}
impl ResetControl {
/// 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() -> MmioResetControl<'static> {
unsafe { Self::new_mmio_at(SLCR_BASE_ADDR + RESET_BLOCK_OFFSET) }
}
}
static_assertions::const_assert_eq!(core::mem::size_of::<ResetControl>(), 0x50);

236
zynq7000/src/spi.rs Normal file
View File

@ -0,0 +1,236 @@
//! SPI register module.
use arbitrary_int::{Number, u4};
pub const SPI_0_BASE_ADDR: usize = 0xE000_6000;
pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
/// The SPI reference block will be divided by a divisor value.
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
pub enum BaudDivSelect {
By4 = 0b001,
By8 = 0b010,
By16 = 0b011,
By32 = 0b100,
By64 = 0b101,
By128 = 0b110,
By256 = 0b111,
}
impl BaudDivSelect {
pub const fn div_value(&self) -> usize {
match self {
BaudDivSelect::By4 => 4,
BaudDivSelect::By8 => 8,
BaudDivSelect::By16 => 16,
BaudDivSelect::By32 => 32,
BaudDivSelect::By64 => 64,
BaudDivSelect::By128 => 128,
BaudDivSelect::By256 => 256,
}
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(17, rw)]
modefail_gen_en: bool,
#[bit(16, w)]
manual_start: bool,
#[bit(15, rw)]
manual_start_enable: bool,
#[bit(14, rw)]
manual_cs: bool,
#[bits(10..=13, rw)]
cs_raw: u4,
/// Peripheral select decode, 1: Allow external 3-to-8 decode.
/// I am not sure how exactly this work, but I suspect the last three bits of the chip
/// select bits will be output directly to the 3 chip select output lines.
#[bit(9, rw)]
peri_sel: bool,
/// Uses SPI reference clock, value 1 is not supported.
#[bit(8, r)]
ref_clk: bool,
#[bits(3..=5, rw)]
baud_rate_div: Option<BaudDivSelect>,
/// Clock phase. 1: The SPI clock is inactive outside the word.
#[bit(2, rw)]
cpha: bool,
/// Clock phase. 1: The SPI clock is quiescent high.
#[bit(1, rw)]
cpol: bool,
/// Master mode enable. 1 is master mode.
#[bit(0, rw)]
master_ern: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(6, rw)]
tx_underflow: bool,
#[bit(5, rw)]
rx_full: bool,
#[bit(4, rw)]
rx_not_empty: bool,
#[bit(3, rw)]
tx_full: bool,
#[bit(2, rw)]
tx_not_full: bool,
#[bit(1, rw)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, rw)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(6, w)]
tx_underflow: bool,
#[bit(5, w)]
rx_full: bool,
#[bit(4, w)]
rx_not_empty: bool,
#[bit(3, w)]
tx_full: bool,
#[bit(2, w)]
tx_trig: bool,
#[bit(1, w)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, w)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptMask {
#[bit(6, r)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_not_empty: bool,
#[bit(3, r)]
tx_full: bool,
#[bit(2, r)]
tx_trig: bool,
#[bit(1, r)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, r)]
rx_ovr: bool,
}
#[derive(Debug)]
pub struct FifoWrite(arbitrary_int::UInt<u32, 8>);
impl FifoWrite {
#[inline]
pub fn new(data: u8) -> Self {
Self(data.into())
}
#[inline]
pub fn value(&self) -> u8 {
self.0.as_u8()
}
#[inline]
pub fn write(&mut self, value: u8) {
self.0 = value.into();
}
}
#[derive(Debug)]
pub struct FifoRead(arbitrary_int::UInt<u32, 8>);
impl FifoRead {
#[inline]
pub fn new(data: u8) -> Self {
Self(data.into())
}
#[inline]
pub fn value(&self) -> u8 {
self.0.as_u8()
}
}
/// The numbers specified in the register fields are always specified in number of
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DelayControl {
/// Number of cycles the chip select is de-asserted between words when CPHA = 0
#[bits(24..=31, rw)]
inter_word_cs_deassert: u8,
/// Delay between one chip select being de-activated, and activation of another.
#[bits(16..=23, rw)]
between_cs_assertion: u8,
/// Delay between words.
#[bits(8..=15, rw)]
inter_word: u8,
/// Added delay between assertion of slave select and first bit transfer.
#[bits(0..=7, rw)]
cs_to_first_bit: u8,
}
/// Register block specification for both PS SPIs.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Spi {
cr: Config,
#[mmio(PureRead, Write)]
isr: InterruptStatus,
/// Interrupt Enable Register.
#[mmio(Write)]
ier: InterruptControl,
/// Interrupt Disable Register.
#[mmio(Write)]
idr: InterruptControl,
/// Interrupt Mask Register.
#[mmio(PureRead)]
imr: InterruptMask,
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]
txd: FifoWrite,
#[mmio(Read)]
rxd: FifoRead,
sicr: u32,
tx_trig: u32,
rx_trig: u32,
_reserved: [u32; 0x33],
// Reset value: 0x90106
#[mmio(PureRead)]
mod_id: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x100);
impl Spi {
/// Create a new SPI MMIO instance for SPI0 at address [SPI_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() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_0_BASE_ADDR) }
}
/// Create a new SPI MMIO instance for SPI1 at address [SPI_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() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_1_BASE_ADDR) }
}
}

189
zynq7000/src/ttc.rs Normal file
View File

@ -0,0 +1,189 @@
//! Triple-timer counter (TTC) register module.
use arbitrary_int::u4;
pub const TTC_0_BASE_ADDR: usize = 0xF800_1000;
pub const TTC_1_BASE_ADDR: usize = 0xF800_2000;
#[derive(Debug, Default)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum ClockSource {
/// PS internal bus clock.
#[default]
Pclk = 0b0,
External = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct ClockControl {
/// When this bit is set and the external clock is selected, the counter clocks on the
/// negative edge of the external clock input.
#[bit(6, rw)]
ext_clk_edge: bool,
#[bit(5, rw)]
clk_src: ClockSource,
#[bits(1..=4, rw)]
prescaler: u4,
#[bit(0, rw)]
prescale_enable: bool,
}
#[derive(Debug)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum Mode {
Overflow = 0b0,
Interval = 0b1,
}
#[derive(Debug, Default)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum WavePolarity {
/// The waveform output goes from high to low on a match 0 interrupt and returns high on
/// overflow or interval interrupt.
#[default]
HighToLowOnMatch1 = 0b0,
/// The waveform output goes from low to high on a match 0 interrupt and returns low on
/// overflow or interval interrupt.
LowToHighOnMatch1 = 0b1,
}
#[derive(Debug)]
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum WaveEnable {
Enable = 0b0,
Disable = 0b1,
}
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct CounterControl {
#[bit(6, rw)]
wave_polarity: WavePolarity,
/// Output waveform enable, active low. Reset value 1.
#[bit(5, rw)]
wave_enable_n: WaveEnable,
/// Resets the counter and restarts counting. Automatically cleared on restart.
#[bit(4, rw)]
reset: bool,
/// When this bit is set, an interrupt is generated when the count value matches one of the
/// three match registers and the corresponding bit is set in the IER register.
#[bit(3, rw)]
match_enable: bool,
/// When this bit is high, the timer counts down.
#[bit(2, rw)]
decrementing: bool,
#[bit(1, rw)]
mode: Mode,
#[bit(0, rw)]
disable: bool,
}
#[bitbybit::bitfield(u32)]
pub struct Counter {
#[bits(0..=15, r)]
count: u16,
}
#[bitbybit::bitfield(u32)]
pub struct RwValue {
#[bits(0..=15, rw)]
value: u16,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptStatus {
/// Even timer overflow interrupt.
#[bit(5, r)]
event: bool,
#[bit(4, r)]
counter_overflow: bool,
#[bit(3, r)]
match_2: bool,
#[bit(2, r)]
match_1: bool,
#[bit(1, r)]
match_0: bool,
#[bit(0, r)]
interval: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptControl {
/// Even timer overflow interrupt.
#[bit(5, rw)]
event: bool,
#[bit(4, rw)]
counter_overflow: bool,
#[bit(3, rw)]
match_2: bool,
#[bit(2, rw)]
match_1: bool,
#[bit(1, rw)]
match_0: bool,
#[bit(0, rw)]
interval: bool,
}
#[bitbybit::bitfield(u32)]
pub struct EventControl {
/// E_Ov bit. When set to 0, the event timer is disabled and set to 0 when an event timer
/// register overflow occurs. Otherwise, continue counting on overflow.
#[bit(2, rw)]
continuous_mode: bool,
/// E_Lo bit. When set to 1, counts PCLK cycles during low level duration of the external
/// clock. Otherwise, counts it during high level duration.
#[bit(1, rw)]
count_low_level_of_ext_clk: bool,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32)]
pub struct EventCount {
#[bits(0..=15, r)]
count: u16,
}
/// Triple-timer counter
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Ttc {
clk_cntr: [ClockControl; 3],
cnt_ctrl: [CounterControl; 3],
#[mmio(PureRead)]
current_counter: [Counter; 3],
interval_value: [RwValue; 3],
match_value_0: [RwValue; 3],
match_value_1: [RwValue; 3],
match_value_2: [RwValue; 3],
#[mmio(Read)]
isr: [InterruptStatus; 3],
ier: [InterruptControl; 3],
event_cntrl: [EventControl; 3],
#[mmio(PureRead)]
event_reg: [EventCount; 3],
}
static_assertions::const_assert_eq!(core::mem::size_of::<Ttc>(), 0x84);
impl Ttc {
/// Create a new TTC MMIO instance for TTC0 at address [TTC_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() -> MmioTtc<'static> {
unsafe { Self::new_mmio_at(TTC_0_BASE_ADDR) }
}
/// Create a new TTC MMIO instance for TTC1 at address [TTC_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() -> MmioTtc<'static> {
unsafe { Self::new_mmio_at(TTC_1_BASE_ADDR) }
}
}

356
zynq7000/src/uart.rs Normal file
View File

@ -0,0 +1,356 @@
//! PS UART register module.
use arbitrary_int::u6;
pub const UART_0_BASE: usize = 0xE000_0000;
pub const UART_1_BASE: usize = 0xE000_1000;
#[bitbybit::bitenum(u3, exhaustive = true)]
pub enum Parity {
Even = 0b000,
Odd = 0b001,
/// Forced to 0 (Space)
ForcedTo0 = 0b010,
/// Forced to 1 (Mark)
ForcedTo1 = 0b011,
NoParity = 0b100,
NoParityAlt0 = 0b101,
NoParityAlt1 = 0b110,
NoParityAlt2 = 0b111,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum Chrl {
SixBits = 0b11,
SevenBits = 0b10,
#[default]
EightBits = 0b00,
EightBitsAlt = 0b01,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum ClkSel {
#[default]
UartRefClk = 0b0,
UartRefClkDiv8 = 0b1,
}
#[bitbybit::bitenum(u2)]
#[derive(Default, Debug, PartialEq, Eq)]
pub enum Stopbits {
#[default]
One = 0b00,
OnePointFive = 0b01,
Two = 0b10,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[derive(Debug, Default)]
pub enum ChMode {
#[default]
Normal = 0b00,
AutoEcho = 0b01,
LocalLoopback = 0b10,
RemoteLoopback = 0b11,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Ctrl {
/// Stop transmitter break.
#[bit(8, rw)]
stopbrk: bool,
/// Start transmitter break.
#[bit(7, rw)]
startbrk: bool,
/// Restart receiver timeout counter.
#[bit(6, rw)]
rstto: bool,
/// TX disable. If this is 1, TX is disabled, regardless of TXEN.
#[bit(5, rw)]
tx_dis: bool,
/// TX enable. TX will be enabled if this bit is 1 and the TXDIS is 0.
#[bit(4, rw)]
tx_en: bool,
/// RX disable. If this is 1, RX is disabled, regardless of RXEN.
#[bit(3, rw)]
rx_dis: bool,
/// RX enable. RX will be enabled if this bit is 1 and the RXDIS is 0.
#[bit(2, rw)]
rx_en: bool,
/// TX soft reset.
#[bit(1, rw)]
tx_rst: bool,
/// RX soft reset.
#[bit(0, rw)]
rx_rst: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Mode {
#[bits(8..=9, rw)]
chmode: ChMode,
#[bits(6..=7, rw)]
nbstop: Option<Stopbits>,
#[bits(3..=5, rw)]
par: Parity,
/// Char length.
#[bits(1..=2, rw)]
chrl: Chrl,
#[bit(0, rw)]
clksel: ClkSel,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Baudgen {
#[bits(0..=15, rw)]
cd: u16,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct BaudRateDiv {
#[bits(0..=7, rw)]
bdiv: u8,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Fifo {
#[bits(0..=7, rw)]
fifo: u8,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
pub enum Ttrig {
LessThanTTrig = 0b0,
GreaterEqualTTrig = 0b1,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
#[bit(14, r)]
tx_near_full: bool,
#[bit(13, r)]
tx_trig: Ttrig,
#[bit(12, r)]
flowdel: bool,
/// Transmitter state machine active.
#[bit(11, r)]
tx_active: bool,
/// Receiver state machine active.
#[bit(10, r)]
rx_active: bool,
#[bit(4, r)]
tx_full: bool,
#[bit(3, r)]
tx_empty: bool,
#[bit(2, r)]
rx_full: bool,
#[bit(1, r)]
rx_empty: bool,
/// RX FIFO trigger level was reached.
#[bit(0, r)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptControl {
#[bit(12, w)]
tx_over: bool,
#[bit(11, w)]
tx_near_full: bool,
#[bit(10, w)]
tx_trig: bool,
#[bit(9, w)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, w)]
rx_timeout: bool,
#[bit(7, w)]
rx_parity: bool,
#[bit(6, w)]
rx_framing: bool,
#[bit(5, w)]
rx_over: bool,
#[bit(4, w)]
tx_full: bool,
#[bit(3, w)]
tx_empty: bool,
#[bit(2, w)]
rx_full: bool,
#[bit(1, w)]
rx_empty: bool,
#[bit(0, w)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FifoTrigger {
#[bits(0..=5, rw)]
trig: u6,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptMask {
#[bit(12, r)]
tx_over: bool,
#[bit(11, r)]
tx_near_full: bool,
#[bit(10, r)]
tx_trig: bool,
#[bit(9, r)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, r)]
rx_timeout: bool,
#[bit(7, r)]
rx_parity: bool,
#[bit(6, r)]
rx_framing: bool,
#[bit(5, r)]
rx_over: bool,
#[bit(4, r)]
tx_full: bool,
#[bit(3, r)]
tx_empty: bool,
#[bit(2, r)]
rx_full: bool,
#[bit(1, r)]
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, r)]
rx_trg: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct InterruptStatus {
#[bit(12, rw)]
tx_over: bool,
#[bit(11, rw)]
tx_near_full: bool,
#[bit(10, rw)]
tx_trig: bool,
#[bit(9, rw)]
rx_dms: bool,
/// Receiver timeout error interrupt.
#[bit(8, rw)]
rx_timeout: bool,
#[bit(7, rw)]
rx_parity: bool,
#[bit(6, rw)]
rx_framing: bool,
#[bit(5, rw)]
rx_over: bool,
#[bit(4, rw)]
tx_full: bool,
#[bit(3, rw)]
tx_empty: bool,
#[bit(2, rw)]
rx_full: bool,
#[bit(1, rw)]
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, rw)]
rx_trg: bool,
}
impl InterruptStatus {
pub fn new_for_clearing_rx_errors() -> Self {
Self::builder()
.with_tx_over(false)
.with_tx_near_full(false)
.with_tx_trig(false)
.with_rx_dms(false)
.with_rx_timeout(false)
.with_rx_parity(true)
.with_rx_framing(true)
.with_rx_over(true)
.with_tx_full(false)
.with_tx_empty(false)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.build()
}
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Uart {
/// Control Register
cr: Ctrl,
/// Mode register
mr: Mode,
/// Interrupt enable register
#[mmio(Write)]
ier: InterruptControl,
/// Interrupt disable register
#[mmio(Write)]
idr: InterruptControl,
/// Interrupt mask register, showing enabled interrupts.
#[mmio(PureRead)]
imr: InterruptMask,
/// Interrupt status register
#[mmio(PureRead, Write)]
isr: InterruptStatus,
/// Baudgen register
baudgen: Baudgen,
/// RX timeout register
rx_tout: u32,
/// RX FIFO trigger level register
rx_fifo_trigger: FifoTrigger,
/// Modem control register
modem_cr: u32,
/// Modem status register
modem_sr: u32,
/// Channel status register
#[mmio(PureRead)]
sr: Status,
/// FIFO register
#[mmio(Read, Write)]
fifo: Fifo,
/// Baud rate divider register
baud_rate_div: BaudRateDiv,
/// Flow control delay register
flow_delay: u32,
_reserved: [u32; 2],
/// TX fifo trigger level
tx_fifo_trigger: FifoTrigger,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Uart>(), 0x48);
impl Uart {
/// Create a new UART MMIO instance for uart0 at address 0xE000_0000.
///
/// # 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() -> MmioUart<'static> {
unsafe { Self::new_mmio_at(UART_0_BASE) }
}
/// Create a new UART MMIO instance for uart1 at address 0xE000_1000.
///
/// # 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() -> MmioUart<'static> {
unsafe { Self::new_mmio_at(UART_1_BASE) }
}
}