Compare commits
2 Commits
main
...
2022spring
Author | SHA1 | Date |
---|---|---|
|
a12923c70c | 3 years ago |
|
dbc3b795e2 | 3 years ago |
@ -0,0 +1,13 @@
|
||||
[workspace]
|
||||
|
||||
members=[
|
||||
"easy-fs",
|
||||
"easy-fs-fuse",
|
||||
"os",
|
||||
"user",
|
||||
"third-party/k210-hal",
|
||||
"third-party/k210-pac",
|
||||
"third-party/k210-soc",
|
||||
"third-party/riscv",
|
||||
"third-party/virtio-drivers",
|
||||
]
|
@ -0,0 +1,16 @@
|
||||
on: [push, pull_request]
|
||||
name: Clippy Check
|
||||
|
||||
jobs:
|
||||
clippy_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: clippy
|
||||
override: true
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
@ -0,0 +1,19 @@
|
||||
on: [push, pull_request]
|
||||
name: Cross Compile
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: riscv64gc-unknown-none-elf
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: build
|
||||
args: --target riscv64gc-unknown-none-elf
|
@ -0,0 +1,12 @@
|
||||
name: Security Audit
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
@ -0,0 +1,37 @@
|
||||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [RISC-V team](https://github.com/rust-embedded/wg#the-riscv-team)
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
|
||||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
|
||||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
|
||||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [RISC-V team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
|
||||
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
|
||||
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
|
||||
|
||||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||
|
||||
The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org).
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-riscv-team
|
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "k210-hal"
|
||||
version = "0.2.0"
|
||||
authors = ["The RISC-V Team <risc-v@teams.rust-embedded.org>"]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
description = "Hardware Abstract Layer (HAL) support for K210, dual-core RV64GC SoC"
|
||||
repository = "https://github.com/riscv-rust/k210-hal"
|
||||
keywords = ["riscv", "k210", "hal"]
|
||||
license = "ISC"
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["riscv64gc-unknown-none-elf"]
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = "=1.0.0-alpha.1"
|
||||
nb = "0.1.1"
|
||||
k210-pac = { path = "../k210-pac" }
|
||||
bitflags = "1.2.1"
|
@ -0,0 +1,37 @@
|
||||
[](https://crates.io/crates/k210-hal)
|
||||
[](https://crates.io/crates/k210-hal)
|
||||
[](https://travis-ci.org/riscv-rust/k210-hal)
|
||||
|
||||
# `k210-hal`
|
||||
|
||||
> Hardware abstract layer (HAL) for K210 chip, a dual RV64GC SoC with hardware
|
||||
> accelerated AI peripherals.
|
||||
|
||||
This project is developed and maintained by the [RISC-V team][team].
|
||||
|
||||
## [Documentation](https://docs.rs/k210-hal)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2019 [RISC-V team][team]
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-risc-v-team
|
@ -0,0 +1,79 @@
|
||||
//! (TODO) Hardware AES calculator (AES)
|
||||
use core::marker::PhantomData;
|
||||
use crate::pac::AES;
|
||||
|
||||
pub struct Aes<MODE, KLEN> {
|
||||
aes: AES,
|
||||
_mode: PhantomData<MODE>,
|
||||
_klen: PhantomData<KLEN>,
|
||||
}
|
||||
|
||||
pub struct Ecb;
|
||||
|
||||
pub struct Cbc;
|
||||
|
||||
pub struct Gcm;
|
||||
|
||||
pub struct K128;
|
||||
|
||||
pub struct K192;
|
||||
|
||||
pub struct K256;
|
||||
|
||||
#[allow(unused)] // todo: remove
|
||||
impl<MODE, KLEN> Aes<MODE, KLEN> {
|
||||
pub fn ecb128(aes: AES) -> Aes<Ecb, K128> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn ecb192(aes: AES) -> Aes<Ecb, K192> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn ecb256(aes: AES) -> Aes<Ecb, K256> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn cbc128(aes: AES) -> Aes<Cbc, K128> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn cbc192(aes: AES) -> Aes<Cbc, K192> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn cbc256(aes: AES) -> Aes<Cbc, K256> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn gcm128(aes: AES) -> Aes<Gcm, K128> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn gcm192(aes: AES) -> Aes<Gcm, K192> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn gcm256(aes: AES) -> Aes<Gcm, K256> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE, KLEN> Aes<MODE, KLEN> {
|
||||
// todo: clock
|
||||
pub fn free(self) -> AES {
|
||||
self.aes
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)] // todo: remove
|
||||
impl<MODE, KLEN> Aes<MODE, KLEN> {
|
||||
// entrypt block in-place
|
||||
pub fn encrypt_block(&self, block: &mut [u8], key: &[u8]) {
|
||||
todo!()
|
||||
}
|
||||
// decrypt block in-place
|
||||
pub fn decrypt_block(&self, block: &mut [u8], key: &[u8]) {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
//! (TODO) Audio Processor Unit (APU)
|
@ -0,0 +1,117 @@
|
||||
//! (TODO) Bypass cache
|
||||
//!
|
||||
//! Todo: verify this module!
|
||||
|
||||
use core::slice;
|
||||
use core::str;
|
||||
|
||||
/// Convert a buffer or a pointer into ones with uncached address.
|
||||
///
|
||||
/// Section 3.4.1, Kendryte K210 Datasheet
|
||||
pub fn uncached<T: Uncache>(buf: T) -> T {
|
||||
buf.uncache()
|
||||
}
|
||||
|
||||
/// Uncacheable buffer or pointer.
|
||||
pub trait Uncache {
|
||||
/// Convert this buffer or pointer to uncached addressed ones
|
||||
fn uncache(self) -> Self;
|
||||
}
|
||||
|
||||
impl<T> Uncache for &T {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self as *const T as usize;
|
||||
assert_addr_cached(addr);
|
||||
// note(unsafe): safe for source address is safe
|
||||
unsafe { &*((addr - 0x4000_0000) as *const T) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uncache for &mut T {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self as *mut T as usize;
|
||||
assert_addr_cached(addr);
|
||||
// note(unsafe): safe for source address is safe
|
||||
unsafe { &mut *((addr - 0x4000_0000) as *mut T) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uncache for &[T] {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self.as_ptr() as usize;
|
||||
assert_addr_cached(addr);
|
||||
let new_ptr = (addr - 0x4000_0000) as *const T;
|
||||
// note(unsafe): source address is safe; passing ownership
|
||||
unsafe { slice::from_raw_parts(new_ptr, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uncache for &mut [T] {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self.as_ptr() as usize;
|
||||
assert_addr_cached(addr);
|
||||
let new_ptr = (addr - 0x4000_0000) as *mut T;
|
||||
// note(unsafe): source address is safe; passing ownership
|
||||
unsafe { slice::from_raw_parts_mut(new_ptr, self.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Uncache for &str {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self.as_ptr() as usize;
|
||||
assert_addr_cached(addr);
|
||||
let new_ptr = (addr - 0x4000_0000) as *const u8;
|
||||
// note(unsafe): source address is safe; passing ownership
|
||||
let slice = unsafe { slice::from_raw_parts(new_ptr, self.len()) };
|
||||
// note(unsafe): source slice is guaranteed valid in UTF-8
|
||||
unsafe { str::from_utf8_unchecked(slice) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Uncache for &mut str {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
let addr = self.as_ptr() as usize;
|
||||
assert_addr_cached(addr);
|
||||
let new_ptr = (addr - 0x4000_0000) as *mut u8;
|
||||
// note(unsafe): source address is safe; passing ownership
|
||||
let slice = unsafe { slice::from_raw_parts_mut(new_ptr, self.len()) };
|
||||
// note(unsafe): source slice is guaranteed valid in UTF-8
|
||||
unsafe { str::from_utf8_unchecked_mut(slice) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uncache for *const T {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
assert_addr_cached(self as usize);
|
||||
(self as usize - 0x4000_0000) as *const T
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Uncache for *mut T {
|
||||
#[inline]
|
||||
fn uncache(self) -> Self {
|
||||
assert_addr_cached(self as usize);
|
||||
(self as usize - 0x4000_0000) as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_addr_cached(addr: usize) {
|
||||
/*
|
||||
SRAM memory: 0x8000_0000 to 0x805F_FFFF
|
||||
AI memory: 0x8060_0000 to 0x807F_FFFF
|
||||
SRAM memory includes two parts: MEM0 and MEM1
|
||||
MEM0 memory: 0x8000_0000 to 0x803F_FFFF
|
||||
MEM1 memory: 0x8040_0000 to 0x805F_FFFF
|
||||
*/
|
||||
// assure that memory is within SRAM or AI
|
||||
// todo: should we exclude AI?
|
||||
assert!(addr <= 0x807F_FFFF && addr >= 0x8000_0000);
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
//! Core Local Interruptor (CLINT)
|
||||
//!
|
||||
//! TODO: Should this module designed in a somehow IP-core peripheral create?
|
||||
|
||||
/// mtime register
|
||||
pub mod mtime {
|
||||
use crate::pac;
|
||||
/// Read mtime register.
|
||||
pub fn read() -> u64 {
|
||||
unsafe { (*pac::CLINT::ptr()).mtime.read().bits() }
|
||||
}
|
||||
}
|
||||
|
||||
/// msip register
|
||||
pub mod msip {
|
||||
use crate::pac;
|
||||
|
||||
/// set IPI interrupt flag for one given hart
|
||||
pub fn set_ipi(hart_id: usize) {
|
||||
unsafe {
|
||||
(*pac::CLINT::ptr()).msip[hart_id].write(|w|
|
||||
w.bits(1))
|
||||
}
|
||||
}
|
||||
/// clear IPI interrupt flag for one given hart
|
||||
pub fn clear_ipi(hart_id: usize) {
|
||||
unsafe {
|
||||
(*pac::CLINT::ptr()).msip[hart_id].write(|w|
|
||||
w.bits(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// mtimecmp register
|
||||
pub mod mtimecmp {
|
||||
use crate::pac;
|
||||
|
||||
/// Read 64-bit mtimecmp register for certain hart id
|
||||
pub fn read(hart_id: usize) -> u64 {
|
||||
unsafe { (*pac::CLINT::ptr()).mtimecmp[hart_id].read().bits() }
|
||||
}
|
||||
|
||||
/// Write 64-bit mtimecmp register for certain hart id
|
||||
pub fn write(hart_id: usize, bits: u64) {
|
||||
// Volume II: RISC-V Privileged Architectures V1.10 p.31, figure 3.15
|
||||
unsafe { (*pac::CLINT::ptr()).mtimecmp[hart_id].write(|w|
|
||||
w.bits(bits)) };
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
//! Clock configuration
|
||||
//use crate::pac::PRCI;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// Frozen clock frequencies
|
||||
///
|
||||
/// The existence of this value indicates that the clock configuration can no
|
||||
/// longer be changed.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Clocks {
|
||||
cpu: Hertz,
|
||||
apb0: Hertz,
|
||||
}
|
||||
|
||||
impl Clocks {
|
||||
#[doc(hidden)]
|
||||
pub fn new() -> Self {
|
||||
/*
|
||||
[MAIXPY]Pll0:freq:806000000
|
||||
[MAIXPY]Pll1:freq:398666666
|
||||
[MAIXPY]Pll2:freq:45066666
|
||||
[MAIXPY]cpu:freq:403000000
|
||||
[MAIXPY]kpu:freq:398666666
|
||||
in freq: 26000000
|
||||
cpu_freq: 390000000
|
||||
*/
|
||||
Self {
|
||||
cpu: Hertz(403_000_000),
|
||||
apb0: Hertz(195_000_000),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns CPU frequency
|
||||
pub fn cpu(&self) -> Hertz {
|
||||
Hertz(self.cpu.0)
|
||||
}
|
||||
|
||||
/// Returns APB0 frequency
|
||||
pub fn apb0(&self) -> Hertz {
|
||||
self.apb0
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
//! (TODO) Direct Memory Access Controller (DMAC)
|
||||
use crate::{pac, sysctl};
|
||||
|
||||
pub fn dmac_id() -> u64 {
|
||||
unsafe { (*pac::DMAC::ptr()).id.read().bits() }
|
||||
}
|
||||
|
||||
pub fn dmac_version() -> u64 {
|
||||
unsafe { (*pac::DMAC::ptr()).compver.read().bits() }
|
||||
}
|
||||
|
||||
pub trait DmacExt {
|
||||
fn configure(self, /* sysctl ACLK clock */) -> Dmac;
|
||||
}
|
||||
|
||||
impl DmacExt for pac::DMAC {
|
||||
fn configure(self, /* sysctl ACLK clock */) -> Dmac {
|
||||
// enable
|
||||
sysctl::clk_en_peri().modify(|_, w| w.dma_clk_en().set_bit());
|
||||
// todo: reset
|
||||
Dmac {} // todo
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dmac {
|
||||
|
||||
}
|
||||
|
||||
// pub struct C0 {
|
||||
// // todo
|
||||
// pub async fn poll() {
|
||||
|
||||
// }
|
||||
// }
|
@ -0,0 +1 @@
|
||||
//! (TODO) Fast Fourier Transform (FFT)
|
@ -0,0 +1,486 @@
|
||||
//! Field Programmable IO Array (FPIOA)
|
||||
|
||||
/*
|
||||
new design: Split FPIOA into several IO pins (IO0, IO1, .., IO47)
|
||||
with ownership of IOx struct we may change their functions.
|
||||
For GPIOs and GPIOHS's we should split them into another GpioXx structs.
|
||||
|
||||
note: other modules do not need to require for ownership of certain IOx struct
|
||||
for that pins and peripherals are considered separate modules.
|
||||
If this design results in inconvenience or violent of Rust's ownership role
|
||||
please fire an issue to tell us.
|
||||
*/
|
||||
|
||||
use crate::pac::FPIOA;
|
||||
use crate::sysctl::{self, APB0};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// FPIOA function
|
||||
pub trait Function {
|
||||
const INDEX: u8;
|
||||
}
|
||||
|
||||
/// Pull direction
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Pull {
|
||||
/// No pulls
|
||||
None,
|
||||
/// Pull down
|
||||
Down,
|
||||
/// Pull up
|
||||
Up,
|
||||
}
|
||||
|
||||
/// FPIOA I/O Pin
|
||||
pub trait IoPin {
|
||||
const INDEX: u8;
|
||||
|
||||
fn set_io_pull(&mut self, pull: Pull) {
|
||||
unsafe {
|
||||
let fpioa = &*FPIOA::ptr();
|
||||
fpioa.io[Self::INDEX as usize].modify(|_, w| match pull {
|
||||
Pull::None => w.pu().bit(false).pd().bit(false),
|
||||
Pull::Down => w.pu().bit(false).pd().bit(true),
|
||||
Pull::Up => w.pu().bit(true).pd().bit(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker trait for I/O pin function detection
|
||||
pub trait Mode<FUNC> {}
|
||||
|
||||
/// Extension trait to split a FPIOA peripheral in independent pins and registers
|
||||
pub trait FpioaExt {
|
||||
/// Splits the FPIOA block into independent pins and registers
|
||||
///
|
||||
/// todo: split sysctl into two apb's, then use the APB0 to split Fpioa
|
||||
fn split(self, apb0: &mut APB0) -> Parts;
|
||||
}
|
||||
|
||||
/// All I/O pins
|
||||
macro_rules! def_io_pin {
|
||||
($($IoX: ident: ($id: expr, $iox: ident, $func: ident);)+) => {
|
||||
impl FpioaExt for FPIOA {
|
||||
fn split(self, apb0: &mut APB0) -> Parts {
|
||||
// enable APB0 bus
|
||||
apb0.enable();
|
||||
// enable sysctl peripheral
|
||||
sysctl::clk_en_peri().modify(|_r, w| w.fpioa_clk_en().set_bit());
|
||||
// return ownership
|
||||
Parts {
|
||||
$( $iox: $IoX { _function: PhantomData }, )+
|
||||
}
|
||||
}
|
||||
}
|
||||
/// FPIOA Parts
|
||||
pub struct Parts {
|
||||
$(
|
||||
/// Programmable I/O pin
|
||||
pub $iox: $IoX<$func>,
|
||||
)+
|
||||
// todo: tie controller Tie (force set high or low as input)
|
||||
}
|
||||
pub use io_pins::*;
|
||||
/// All I/O pins
|
||||
pub mod io_pins {
|
||||
use core::marker::PhantomData;
|
||||
use super::{Function, IoPin, Mode, FUNCTION_DEFAULTS};
|
||||
use crate::pac::FPIOA;
|
||||
|
||||
$(
|
||||
/// Programmable I/O pin
|
||||
pub struct $IoX<FUNC> {
|
||||
pub(crate) _function: PhantomData<FUNC>
|
||||
}
|
||||
|
||||
impl<FUNC> $IoX<FUNC> {
|
||||
/// Change the programmable I/O pin into given function
|
||||
///
|
||||
/// You may refer to module [functions] for all functions.
|
||||
///
|
||||
/// [functions]: ../functions/index.html
|
||||
pub fn into_function<F: Function>(self, func: F) -> $IoX<F> {
|
||||
let _ = func; // note(discard): Zero-sized typestate value
|
||||
unsafe { &(*FPIOA::ptr()).io[$id].write(|w|
|
||||
w.bits(FUNCTION_DEFAULTS[F::INDEX as usize])
|
||||
) };
|
||||
$IoX { _function: PhantomData }
|
||||
}
|
||||
}
|
||||
impl<FUNC> IoPin for $IoX<FUNC> {
|
||||
const INDEX: u8 = $id;
|
||||
}
|
||||
impl<FUNC> Mode<FUNC> for $IoX<FUNC> {}
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
def_io_pin! {
|
||||
Io0: (0, io0, JTAG_TCLK);
|
||||
Io1: (1, io1, JTAG_TDI);
|
||||
Io2: (2, io2, JTAG_TMS);
|
||||
Io3: (3, io3, JTAG_TDO);
|
||||
Io4: (4, io4, UARTHS_RX);
|
||||
Io5: (5, io5, UARTHS_TX);
|
||||
Io6: (6, io6, RESV0); // (FLOAT*): no default function
|
||||
Io7: (7, io7, RESV0); // (FLOAT*): no default function
|
||||
Io8: (8, io8, GPIO0);
|
||||
Io9: (9, io9, GPIO1);
|
||||
Io10: (10, io10, GPIO2);
|
||||
Io11: (11, io11, GPIO3);
|
||||
Io12: (12, io12, GPIO4);
|
||||
Io13: (13, io13, GPIO5);
|
||||
Io14: (14, io14, GPIO6);
|
||||
Io15: (15, io15, GPIO7);
|
||||
Io16: (16, io16, GPIOHS0);
|
||||
Io17: (17, io17, GPIOHS1);
|
||||
Io18: (18, io18, GPIOHS2);
|
||||
Io19: (19, io19, GPIOHS3);
|
||||
Io20: (20, io20, GPIOHS4);
|
||||
Io21: (21, io21, GPIOHS5);
|
||||
Io22: (22, io22, GPIOHS6);
|
||||
Io23: (23, io23, GPIOHS7);
|
||||
Io24: (24, io24, GPIOHS8);
|
||||
Io25: (25, io25, GPIOHS9);
|
||||
Io26: (26, io26, GPIOHS10);
|
||||
Io27: (27, io27, GPIOHS11);
|
||||
Io28: (28, io28, GPIOHS12);
|
||||
Io29: (29, io29, GPIOHS13);
|
||||
Io30: (30, io30, GPIOHS14);
|
||||
Io31: (31, io31, GPIOHS15);
|
||||
Io32: (32, io32, GPIOHS16);
|
||||
Io33: (33, io33, GPIOHS17);
|
||||
Io34: (34, io34, GPIOHS18);
|
||||
Io35: (35, io35, GPIOHS19);
|
||||
Io36: (36, io36, GPIOHS20);
|
||||
Io37: (37, io37, GPIOHS21);
|
||||
Io38: (38, io38, GPIOHS22);
|
||||
Io39: (39, io39, GPIOHS23);
|
||||
Io40: (40, io40, GPIOHS24);
|
||||
Io41: (41, io41, GPIOHS25);
|
||||
Io42: (42, io42, GPIOHS26);
|
||||
Io43: (43, io43, GPIOHS27);
|
||||
Io44: (44, io44, GPIOHS28);
|
||||
Io45: (45, io45, GPIOHS29);
|
||||
Io46: (46, io46, GPIOHS30);
|
||||
Io47: (47, io47, GPIOHS31);
|
||||
}
|
||||
|
||||
/** Defaults per function (from Kendryte fpioa.c) */
|
||||
#[rustfmt::skip]
|
||||
static FUNCTION_DEFAULTS: &[u32] = &[
|
||||
0x00900000, 0x00900001, 0x00900002, 0x00001f03, 0x00b03f04, 0x00b03f05, 0x00b03f06, 0x00b03f07,
|
||||
0x00b03f08, 0x00b03f09, 0x00b03f0a, 0x00b03f0b, 0x00001f0c, 0x00001f0d, 0x00001f0e, 0x00001f0f,
|
||||
0x03900010, 0x00001f11, 0x00900012, 0x00001f13, 0x00900014, 0x00900015, 0x00001f16, 0x00001f17,
|
||||
0x00901f18, 0x00901f19, 0x00901f1a, 0x00901f1b, 0x00901f1c, 0x00901f1d, 0x00901f1e, 0x00901f1f,
|
||||
0x00901f20, 0x00901f21, 0x00901f22, 0x00901f23, 0x00901f24, 0x00901f25, 0x00901f26, 0x00901f27,
|
||||
0x00901f28, 0x00901f29, 0x00901f2a, 0x00901f2b, 0x00901f2c, 0x00901f2d, 0x00901f2e, 0x00901f2f,
|
||||
0x00901f30, 0x00901f31, 0x00901f32, 0x00901f33, 0x00901f34, 0x00901f35, 0x00901f36, 0x00901f37,
|
||||
0x00901f38, 0x00901f39, 0x00901f3a, 0x00901f3b, 0x00901f3c, 0x00901f3d, 0x00901f3e, 0x00901f3f,
|
||||
0x00900040, 0x00001f41, 0x00900042, 0x00001f43, 0x00900044, 0x00001f45, 0x00b03f46, 0x00b03f47,
|
||||
0x00b03f48, 0x00b03f49, 0x00b03f4a, 0x00b03f4b, 0x00b03f4c, 0x00b03f4d, 0x00001f4e, 0x00001f4f,
|
||||
0x00001f50, 0x00001f51, 0x03900052, 0x00001f53, 0x00b03f54, 0x00900055, 0x00900056, 0x00001f57,
|
||||
0x00001f58, 0x00001f59, 0x0090005a, 0x0090005b, 0x0090005c, 0x0090005d, 0x00001f5e, 0x00001f5f,
|
||||
0x00001f60, 0x00001f61, 0x00001f62, 0x00001f63, 0x00001f64, 0x00900065, 0x00900066, 0x00900067,
|
||||
0x00900068, 0x00001f69, 0x00001f6a, 0x00001f6b, 0x00001f6c, 0x00001f6d, 0x00001f6e, 0x00001f6f,
|
||||
0x00900070, 0x00900071, 0x00900072, 0x00900073, 0x00001f74, 0x00001f75, 0x00001f76, 0x00001f77,
|
||||
0x00000078, 0x00000079, 0x0000007a, 0x0000007b, 0x0000007c, 0x0000007d, 0x0099107e, 0x0099107f,
|
||||
0x00991080, 0x00991081, 0x00991082, 0x00991083, 0x00001f84, 0x00001f85, 0x00001f86, 0x00900087,
|
||||
0x00900088, 0x00900089, 0x0090008a, 0x0090008b, 0x0090008c, 0x0090008d, 0x0090008e, 0x0090008f,
|
||||
0x00900090, 0x00900091, 0x00993092, 0x00993093, 0x00900094, 0x00900095, 0x00900096, 0x00900097,
|
||||
0x00900098, 0x00001f99, 0x00001f9a, 0x00001f9b, 0x00001f9c, 0x00001f9d, 0x00001f9e, 0x00001f9f,
|
||||
0x00001fa0, 0x00001fa1, 0x009000a2, 0x009000a3, 0x009000a4, 0x009000a5, 0x009000a6, 0x00001fa7,
|
||||
0x00001fa8, 0x00001fa9, 0x00001faa, 0x00001fab, 0x00001fac, 0x00001fad, 0x00001fae, 0x00001faf,
|
||||
0x009000b0, 0x009000b1, 0x009000b2, 0x009000b3, 0x009000b4, 0x00001fb5, 0x00001fb6, 0x00001fb7,
|
||||
0x00001fb8, 0x00001fb9, 0x00001fba, 0x00001fbb, 0x00001fbc, 0x00001fbd, 0x00001fbe, 0x00001fbf,
|
||||
0x00001fc0, 0x00001fc1, 0x00001fc2, 0x00001fc3, 0x00001fc4, 0x00001fc5, 0x00001fc6, 0x00001fc7,
|
||||
0x00001fc8, 0x00001fc9, 0x00001fca, 0x00001fcb, 0x00001fcc, 0x00001fcd, 0x00001fce, 0x00001fcf,
|
||||
0x00001fd0, 0x00001fd1, 0x00001fd2, 0x00001fd3, 0x00001fd4, 0x009000d5, 0x009000d6, 0x009000d7,
|
||||
0x009000d8, 0x009100d9, 0x00991fda, 0x009000db, 0x009000dc, 0x009000dd, 0x000000de, 0x009000df,
|
||||
0x00001fe0, 0x00001fe1, 0x00001fe2, 0x00001fe3, 0x00001fe4, 0x00001fe5, 0x00001fe6, 0x00001fe7,
|
||||
0x00001fe8, 0x00001fe9, 0x00001fea, 0x00001feb, 0x00001fec, 0x00001fed, 0x00001fee, 0x00001fef,
|
||||
0x00001ff0, 0x00001ff1, 0x00001ff2, 0x00001ff3, 0x00001ff4, 0x00001ff5, 0x00001ff6, 0x00001ff7,
|
||||
0x00001ff8, 0x00001ff9, 0x00001ffa, 0x00001ffb, 0x00001ffc, 0x00001ffd, 0x00001ffe, 0x00001fff,
|
||||
];
|
||||
|
||||
pub use functions::*;
|
||||
|
||||
/// All programmable functions
|
||||
pub mod functions {
|
||||
use super::Function;
|
||||
|
||||
macro_rules! def_function {
|
||||
($($name: ident: ($index: expr, $doc: expr);)+) => {
|
||||
$(
|
||||
#[doc = $doc]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name;
|
||||
|
||||
impl Function for $name {
|
||||
const INDEX: u8 = $index;
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
def_function! {
|
||||
JTAG_TCLK: (0, "JTAG Test Clock");
|
||||
JTAG_TDI: (1, "JTAG Test Data In");
|
||||
JTAG_TMS: (2, "JTAG Test Mode Select");
|
||||
JTAG_TDO: (3, "JTAG Test Data Out");
|
||||
SPI0_D0: (4, "SPI0 Data 0");
|
||||
SPI0_D1: (5, "SPI0 Data 1");
|
||||
SPI0_D2: (6, "SPI0 Data 2");
|
||||
SPI0_D3: (7, "SPI0 Data 3");
|
||||
SPI0_D4: (8, "SPI0 Data 4");
|
||||
SPI0_D5: (9, "SPI0 Data 5");
|
||||
SPI0_D6: (10, "SPI0 Data 6");
|
||||
SPI0_D7: (11, "SPI0 Data 7");
|
||||
SPI0_SS0: (12, "SPI0 Chip Select 0");
|
||||
SPI0_SS1: (13, "SPI0 Chip Select 1");
|
||||
SPI0_SS2: (14, "SPI0 Chip Select 2");
|
||||
SPI0_SS3: (15, "SPI0 Chip Select 3");
|
||||
SPI0_ARB: (16, "SPI0 Arbitration");
|
||||
SPI0_SCLK: (17, "SPI0 Serial Clock");
|
||||
UARTHS_RX: (18, "UART High speed Receiver");
|
||||
UARTHS_TX: (19, "UART High speed Transmitter");
|
||||
RESV6: (20, "Reserved function");
|
||||
RESV7: (21, "Reserved function");
|
||||
CLK_SPI1: (22, "Clock SPI1");
|
||||
CLK_I2C1: (23, "Clock I2C1");
|
||||
GPIOHS0: (24, "GPIO High speed 0");
|
||||
GPIOHS1: (25, "GPIO High speed 1");
|
||||
GPIOHS2: (26, "GPIO High speed 2");
|
||||
GPIOHS3: (27, "GPIO High speed 3");
|
||||
GPIOHS4: (28, "GPIO High speed 4");
|
||||
GPIOHS5: (29, "GPIO High speed 5");
|
||||
GPIOHS6: (30, "GPIO High speed 6");
|
||||
GPIOHS7: (31, "GPIO High speed 7");
|
||||
GPIOHS8: (32, "GPIO High speed 8");
|
||||
GPIOHS9: (33, "GPIO High speed 9");
|
||||
GPIOHS10: (34, "GPIO High speed 10");
|
||||
GPIOHS11: (35, "GPIO High speed 11");
|
||||
GPIOHS12: (36, "GPIO High speed 12");
|
||||
GPIOHS13: (37, "GPIO High speed 13");
|
||||
GPIOHS14: (38, "GPIO High speed 14");
|
||||
GPIOHS15: (39, "GPIO High speed 15");
|
||||
GPIOHS16: (40, "GPIO High speed 16");
|
||||
GPIOHS17: (41, "GPIO High speed 17");
|
||||
GPIOHS18: (42, "GPIO High speed 18");
|
||||
GPIOHS19: (43, "GPIO High speed 19");
|
||||
GPIOHS20: (44, "GPIO High speed 20");
|
||||
GPIOHS21: (45, "GPIO High speed 21");
|
||||
GPIOHS22: (46, "GPIO High speed 22");
|
||||
GPIOHS23: (47, "GPIO High speed 23");
|
||||
GPIOHS24: (48, "GPIO High speed 24");
|
||||
GPIOHS25: (49, "GPIO High speed 25");
|
||||
GPIOHS26: (50, "GPIO High speed 26");
|
||||
GPIOHS27: (51, "GPIO High speed 27");
|
||||
GPIOHS28: (52, "GPIO High speed 28");
|
||||
GPIOHS29: (53, "GPIO High speed 29");
|
||||
GPIOHS30: (54, "GPIO High speed 30");
|
||||
GPIOHS31: (55, "GPIO High speed 31");
|
||||
GPIO0: (56, "GPIO pin 0");
|
||||
GPIO1: (57, "GPIO pin 1");
|
||||
GPIO2: (58, "GPIO pin 2");
|
||||
GPIO3: (59, "GPIO pin 3");
|
||||
GPIO4: (60, "GPIO pin 4");
|
||||
GPIO5: (61, "GPIO pin 5");
|
||||
GPIO6: (62, "GPIO pin 6");
|
||||
GPIO7: (63, "GPIO pin 7");
|
||||
UART1_RX: (64, "UART1 Receiver");
|
||||
UART1_TX: (65, "UART1 Transmitter");
|
||||
UART2_RX: (66, "UART2 Receiver");
|
||||
UART2_TX: (67, "UART2 Transmitter");
|
||||
UART3_RX: (68, "UART3 Receiver");
|
||||
UART3_TX: (69, "UART3 Transmitter");
|
||||
SPI1_D0: (70, "SPI1 Data 0");
|
||||
SPI1_D1: (71, "SPI1 Data 1");
|
||||
SPI1_D2: (72, "SPI1 Data 2");
|
||||
SPI1_D3: (73, "SPI1 Data 3");
|
||||
SPI1_D4: (74, "SPI1 Data 4");
|
||||
SPI1_D5: (75, "SPI1 Data 5");
|
||||
SPI1_D6: (76, "SPI1 Data 6");
|
||||
SPI1_D7: (77, "SPI1 Data 7");
|
||||
SPI1_SS0: (78, "SPI1 Chip Select 0");
|
||||
SPI1_SS1: (79, "SPI1 Chip Select 1");
|
||||
SPI1_SS2: (80, "SPI1 Chip Select 2");
|
||||
SPI1_SS3: (81, "SPI1 Chip Select 3");
|
||||
SPI1_ARB: (82, "SPI1 Arbitration");
|
||||
SPI1_SCLK: (83, "SPI1 Serial Clock");
|
||||
SPI_SLAVE_D0: (84, "SPI Slave Data 0");
|
||||
SPI_SLAVE_SS: (85, "SPI Slave Select");
|
||||
SPI_SLAVE_SCLK: (86, "SPI Slave Serial Clock");
|
||||
I2S0_MCLK: (87, "I2S0 Master Clock");
|
||||
I2S0_SCLK: (88, "I2S0 Serial Clock(BCLK)");
|
||||
I2S0_WS: (89, "I2S0 Word Select(LRCLK)");
|
||||
I2S0_IN_D0: (90, "I2S0 Serial Data Input 0");
|
||||
I2S0_IN_D1: (91, "I2S0 Serial Data Input 1");
|
||||
I2S0_IN_D2: (92, "I2S0 Serial Data Input 2");
|
||||
I2S0_IN_D3: (93, "I2S0 Serial Data Input 3");
|
||||
I2S0_OUT_D0: (94, "I2S0 Serial Data Output 0");
|
||||
I2S0_OUT_D1: (95, "I2S0 Serial Data Output 1");
|
||||
I2S0_OUT_D2: (96, "I2S0 Serial Data Output 2");
|
||||
I2S0_OUT_D3: (97, "I2S0 Serial Data Output 3");
|
||||
I2S1_MCLK: (98, "I2S1 Master Clock");
|
||||
I2S1_SCLK: (99, "I2S1 Serial Clock(BCLK)");
|
||||
I2S1_WS: (100, "I2S1 Word Select(LRCLK)");
|
||||
I2S1_IN_D0: (101, "I2S1 Serial Data Input 0");
|
||||
I2S1_IN_D1: (102, "I2S1 Serial Data Input 1");
|
||||
I2S1_IN_D2: (103, "I2S1 Serial Data Input 2");
|
||||
I2S1_IN_D3: (104, "I2S1 Serial Data Input 3");
|
||||
I2S1_OUT_D0: (105, "I2S1 Serial Data Output 0");
|
||||
I2S1_OUT_D1: (106, "I2S1 Serial Data Output 1");
|
||||
I2S1_OUT_D2: (107, "I2S1 Serial Data Output 2");
|
||||
I2S1_OUT_D3: (108, "I2S1 Serial Data Output 3");
|
||||
I2S2_MCLK: (109, "I2S2 Master Clock");
|
||||
I2S2_SCLK: (110, "I2S2 Serial Clock(BCLK)");
|
||||
I2S2_WS: (111, "I2S2 Word Select(LRCLK)");
|
||||
I2S2_IN_D0: (112, "I2S2 Serial Data Input 0");
|
||||
I2S2_IN_D1: (113, "I2S2 Serial Data Input 1");
|
||||
I2S2_IN_D2: (114, "I2S2 Serial Data Input 2");
|
||||
I2S2_IN_D3: (115, "I2S2 Serial Data Input 3");
|
||||
I2S2_OUT_D0: (116, "I2S2 Serial Data Output 0");
|
||||
I2S2_OUT_D1: (117, "I2S2 Serial Data Output 1");
|
||||
I2S2_OUT_D2: (118, "I2S2 Serial Data Output 2");
|
||||
I2S2_OUT_D3: (119, "I2S2 Serial Data Output 3");
|
||||
RESV0: (120, "Reserved function");
|
||||
RESV1: (121, "Reserved function");
|
||||
RESV2: (122, "Reserved function");
|
||||
RESV3: (123, "Reserved function");
|
||||
RESV4: (124, "Reserved function");
|
||||
RESV5: (125, "Reserved function");
|
||||
I2C0_SCLK: (126, "I2C0 Serial Clock");
|
||||
I2C0_SDA: (127, "I2C0 Serial Data");
|
||||
I2C1_SCLK: (128, "I2C1 Serial Clock");
|
||||
I2C1_SDA: (129, "I2C1 Serial Data");
|
||||
I2C2_SCLK: (130, "I2C2 Serial Clock");
|
||||
I2C2_SDA: (131, "I2C2 Serial Data");
|
||||
CMOS_XCLK: (132, "DVP System Clock");
|
||||
CMOS_RST: (133, "DVP System Reset");
|
||||
CMOS_PWDN: (134, "DVP Power Down Mode");
|
||||
CMOS_VSYNC: (135, "DVP Vertical Sync");
|
||||
CMOS_HREF: (136, "DVP Horizontal Reference output");
|
||||
CMOS_PCLK: (137, "Pixel Clock");
|
||||
CMOS_D0: (138, "Data Bit 0");
|
||||
CMOS_D1: (139, "Data Bit 1");
|
||||
CMOS_D2: (140, "Data Bit 2");
|
||||
CMOS_D3: (141, "Data Bit 3");
|
||||
CMOS_D4: (142, "Data Bit 4");
|
||||
CMOS_D5: (143, "Data Bit 5");
|
||||
CMOS_D6: (144, "Data Bit 6");
|
||||
CMOS_D7: (145, "Data Bit 7");
|
||||
SCCB_SCLK: (146, "SCCB Serial Clock");
|
||||
SCCB_SDA: (147, "SCCB Serial Data");
|
||||
UART1_CTS: (148, "UART1 Clear To Send");
|
||||
UART1_DSR: (149, "UART1 Data Set Ready");
|
||||
UART1_DCD: (150, "UART1 Data Carrier Detect");
|
||||
UART1_RI: (151, "UART1 Ring Indicator");
|
||||
UART1_SIR_IN: (152, "UART1 Serial Infrared Input");
|
||||
UART1_DTR: (153, "UART1 Data Terminal Ready");
|
||||
UART1_RTS: (154, "UART1 Request To Send");
|
||||
UART1_OUT2: (155, "UART1 User-designated Output 2");
|
||||
UART1_OUT1: (156, "UART1 User-designated Output 1");
|
||||
UART1_SIR_OUT: (157, "UART1 Serial Infrared Output");
|
||||
UART1_BAUD: (158, "UART1 Transmit Clock Output");
|
||||
UART1_RE: (159, "UART1 Receiver Output Enable");
|
||||
UART1_DE: (160, "UART1 Driver Output Enable");
|
||||
UART1_RS485_EN: (161, "UART1 RS485 Enable");
|
||||
UART2_CTS: (162, "UART2 Clear To Send");
|
||||
UART2_DSR: (163, "UART2 Data Set Ready");
|
||||
UART2_DCD: (164, "UART2 Data Carrier Detect");
|
||||
UART2_RI: (165, "UART2 Ring Indicator");
|
||||
UART2_SIR_IN: (166, "UART2 Serial Infrared Input");
|
||||
UART2_DTR: (167, "UART2 Data Terminal Ready");
|
||||
UART2_RTS: (168, "UART2 Request To Send");
|
||||
UART2_OUT2: (169, "UART2 User-designated Output 2");
|
||||
UART2_OUT1: (170, "UART2 User-designated Output 1");
|
||||
UART2_SIR_OUT: (171, "UART2 Serial Infrared Output");
|
||||
UART2_BAUD: (172, "UART2 Transmit Clock Output");
|
||||
UART2_RE: (173, "UART2 Receiver Output Enable");
|
||||
UART2_DE: (174, "UART2 Driver Output Enable");
|
||||
UART2_RS485_EN: (175, "UART2 RS485 Enable");
|
||||
UART3_CTS: (176, "UART3 Clear To Send");
|
||||
UART3_DSR: (177, "UART3 Data Set Ready");
|
||||
UART3_DCD: (178, "UART3 Data Carrier Detect");
|
||||
UART3_RI: (179, "UART3 Ring Indicator");
|
||||
UART3_SIR_IN: (180, "UART3 Serial Infrared Input");
|
||||
UART3_DTR: (181, "UART3 Data Terminal Ready");
|
||||
UART3_RTS: (182, "UART3 Request To Send");
|
||||
UART3_OUT2: (183, "UART3 User-designated Output 2");
|
||||
UART3_OUT1: (184, "UART3 User-designated Output 1");
|
||||
UART3_SIR_OUT: (185, "UART3 Serial Infrared Output");
|
||||
UART3_BAUD: (186, "UART3 Transmit Clock Output");
|
||||
UART3_RE: (187, "UART3 Receiver Output Enable");
|
||||
UART3_DE: (188, "UART3 Driver Output Enable");
|
||||
UART3_RS485_EN: (189, "UART3 RS485 Enable");
|
||||
TIMER0_TOGGLE1: (190, "TIMER0 Toggle Output 1");
|
||||
TIMER0_TOGGLE2: (191, "TIMER0 Toggle Output 2");
|
||||
TIMER0_TOGGLE3: (192, "TIMER0 Toggle Output 3");
|
||||
TIMER0_TOGGLE4: (193, "TIMER0 Toggle Output 4");
|
||||
TIMER1_TOGGLE1: (194, "TIMER1 Toggle Output 1");
|
||||
TIMER1_TOGGLE2: (195, "TIMER1 Toggle Output 2");
|
||||
TIMER1_TOGGLE3: (196, "TIMER1 Toggle Output 3");
|
||||
TIMER1_TOGGLE4: (197, "TIMER1 Toggle Output 4");
|
||||
TIMER2_TOGGLE1: (198, "TIMER2 Toggle Output 1");
|
||||
TIMER2_TOGGLE2: (199, "TIMER2 Toggle Output 2");
|
||||
TIMER2_TOGGLE3: (200, "TIMER2 Toggle Output 3");
|
||||
TIMER2_TOGGLE4: (201, "TIMER2 Toggle Output 4");
|
||||
CLK_SPI2: (202, "Clock SPI2");
|
||||
CLK_I2C2: (203, "Clock I2C2");
|
||||
INTERNAL0: (204, "Internal function signal 0");
|
||||
INTERNAL1: (205, "Internal function signal 1");
|
||||
INTERNAL2: (206, "Internal function signal 2");
|
||||
INTERNAL3: (207, "Internal function signal 3");
|
||||
INTERNAL4: (208, "Internal function signal 4");
|
||||
INTERNAL5: (209, "Internal function signal 5");
|
||||
INTERNAL6: (210, "Internal function signal 6");
|
||||
INTERNAL7: (211, "Internal function signal 7");
|
||||
INTERNAL8: (212, "Internal function signal 8");
|
||||
INTERNAL9: (213, "Internal function signal 9");
|
||||
INTERNAL10: (214, "Internal function signal 10");
|
||||
INTERNAL11: (215, "Internal function signal 11");
|
||||
INTERNAL12: (216, "Internal function signal 12");
|
||||
INTERNAL13: (217, "Internal function signal 13");
|
||||
INTERNAL14: (218, "Internal function signal 14");
|
||||
INTERNAL15: (219, "Internal function signal 15");
|
||||
INTERNAL16: (220, "Internal function signal 16");
|
||||
INTERNAL17: (221, "Internal function signal 17");
|
||||
CONSTANT: (222, "Constant function");
|
||||
INTERNAL18: (223, "Internal function signal 18");
|
||||
DEBUG0: (224, "Debug function 0");
|
||||
DEBUG1: (225, "Debug function 1");
|
||||
DEBUG2: (226, "Debug function 2");
|
||||
DEBUG3: (227, "Debug function 3");
|
||||
DEBUG4: (228, "Debug function 4");
|
||||
DEBUG5: (229, "Debug function 5");
|
||||
DEBUG6: (230, "Debug function 6");
|
||||
DEBUG7: (231, "Debug function 7");
|
||||
DEBUG8: (232, "Debug function 8");
|
||||
DEBUG9: (233, "Debug function 9");
|
||||
DEBUG10: (234, "Debug function 10");
|
||||
DEBUG11: (235, "Debug function 11");
|
||||
DEBUG12: (236, "Debug function 12");
|
||||
DEBUG13: (237, "Debug function 13");
|
||||
DEBUG14: (238, "Debug function 14");
|
||||
DEBUG15: (239, "Debug function 15");
|
||||
DEBUG16: (240, "Debug function 16");
|
||||
DEBUG17: (241, "Debug function 17");
|
||||
DEBUG18: (242, "Debug function 18");
|
||||
DEBUG19: (243, "Debug function 19");
|
||||
DEBUG20: (244, "Debug function 20");
|
||||
DEBUG21: (245, "Debug function 21");
|
||||
DEBUG22: (246, "Debug function 22");
|
||||
DEBUG23: (247, "Debug function 23");
|
||||
DEBUG24: (248, "Debug function 24");
|
||||
DEBUG25: (249, "Debug function 25");
|
||||
DEBUG26: (250, "Debug function 26");
|
||||
DEBUG27: (251, "Debug function 27");
|
||||
DEBUG28: (252, "Debug function 28");
|
||||
DEBUG29: (253, "Debug function 29");
|
||||
DEBUG30: (254, "Debug function 30");
|
||||
DEBUG31: (255, "Debug function 31");
|
||||
}
|
||||
}
|
@ -0,0 +1,233 @@
|
||||
//! General Purpose Input/Output (GPIO)
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use crate::pac;
|
||||
use crate::sysctl::{self, APB0};
|
||||
use crate::fpioa::{IoPin, Pull, Mode};
|
||||
use crate::bit_utils::{u32_set_bit, u32_toggle_bit, u32_bit_is_set, u32_bit_is_clear};
|
||||
use embedded_hal::digital::{OutputPin, StatefulOutputPin, InputPin, ToggleableOutputPin};
|
||||
|
||||
/// Extension trait to split a GPIO peripheral into independent pins
|
||||
pub trait GpioExt {
|
||||
/// Split the GPIO peripheral into parts
|
||||
fn split(self, apb0: &mut APB0) -> Parts;
|
||||
}
|
||||
|
||||
macro_rules! def_gpio_pins {
|
||||
($($GPIOX: ident: ($num: expr, $gpiox: ident, $func: ident);)+) => {
|
||||
|
||||
impl GpioExt for pac::GPIO {
|
||||
fn split(self, apb0: &mut APB0) -> Parts {
|
||||
// enable APB0 bus
|
||||
apb0.enable();
|
||||
// enable sysctl peripheral
|
||||
sysctl::clk_en_peri().modify(|_r, w| w.gpio_clk_en().set_bit());
|
||||
// return ownership
|
||||
Parts {
|
||||
$( $gpiox: $GPIOX { _ownership: () }, )+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// GPIO peripheral parts
|
||||
pub struct Parts {
|
||||
$(
|
||||
/// GPIO pin
|
||||
pub $gpiox: $GPIOX,
|
||||
)+
|
||||
}
|
||||
|
||||
pub use gpio_pins::*;
|
||||
|
||||
/// All GPIO pins
|
||||
pub mod gpio_pins {
|
||||
use super::GpioIndex;
|
||||
$(
|
||||
/// GPIO pin
|
||||
pub struct $GPIOX {
|
||||
pub(crate) _ownership: ()
|
||||
}
|
||||
|
||||
impl GpioIndex for $GPIOX {
|
||||
type FUNC = crate::fpioa::functions::$func;
|
||||
const INDEX: u8 = $num;
|
||||
}
|
||||
)+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
def_gpio_pins! {
|
||||
GPIO0: (0, gpio0, GPIO0);
|
||||
GPIO1: (1, gpio1, GPIO1);
|
||||
GPIO2: (2, gpio2, GPIO2);
|
||||
GPIO3: (3, gpio3, GPIO3);
|
||||
GPIO4: (4, gpio4, GPIO4);
|
||||
GPIO5: (5, gpio5, GPIO5);
|
||||
GPIO6: (6, gpio6, GPIO6);
|
||||
GPIO7: (7, gpio7, GPIO7);
|
||||
}
|
||||
|
||||
/// GPIO Index
|
||||
pub trait GpioIndex {
|
||||
type FUNC;
|
||||
const INDEX: u8;
|
||||
}
|
||||
|
||||
/// Unknown mode (type state)
|
||||
pub struct Unknown;
|
||||
|
||||
/// Input mode (type state)
|
||||
pub struct Input<MODE>(MODE);
|
||||
|
||||
/// Floating input (type state)
|
||||
pub struct Floating;
|
||||
|
||||
/// Pull down input (type state)
|
||||
pub struct PullDown;
|
||||
|
||||
/// Pull up input (type state)
|
||||
pub struct PullUp;
|
||||
|
||||
/// Output mode (type state)
|
||||
pub struct Output;
|
||||
|
||||
/// Marker trait for active states
|
||||
pub trait Active {}
|
||||
|
||||
impl Active for Unknown {}
|
||||
|
||||
impl Active for Input<Floating> {}
|
||||
|
||||
impl Active for Input<PullUp> {}
|
||||
|
||||
impl Active for Input<PullDown> {}
|
||||
|
||||
impl Active for Output {}
|
||||
|
||||
/// GPIO wrapper struct
|
||||
pub struct Gpio<GPIO, PIN, MODE> {
|
||||
gpio: GPIO,
|
||||
pin: PIN,
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN: Mode<GPIO::FUNC>> Gpio<GPIO, PIN, Unknown> {
|
||||
pub fn new(gpio: GPIO, pin: PIN) -> Gpio<GPIO, PIN, Unknown> {
|
||||
Gpio { gpio, pin, _mode: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO, PIN, MODE> Gpio<GPIO, PIN, MODE> {
|
||||
pub fn free(self) -> (GPIO, PIN) {
|
||||
(self.gpio, self.pin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN: IoPin, MODE: Active> Gpio<GPIO, PIN, MODE> {
|
||||
pub fn into_floating_input(mut self) -> Gpio<GPIO, PIN, Input<Floating>> {
|
||||
self.pin.set_io_pull(Pull::None);
|
||||
self.direction_in();
|
||||
Gpio { gpio: self.gpio, pin: self.pin, _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_up_input(mut self) -> Gpio<GPIO, PIN, Input<PullUp>> {
|
||||
self.pin.set_io_pull(Pull::Up);
|
||||
self.direction_in();
|
||||
Gpio { gpio: self.gpio, pin: self.pin, _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_down_input(mut self) -> Gpio<GPIO, PIN, Input<PullDown>> {
|
||||
self.pin.set_io_pull(Pull::Down);
|
||||
self.direction_in();
|
||||
Gpio { gpio: self.gpio, pin: self.pin, _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_push_pull_output(mut self) -> Gpio<GPIO, PIN, Output> {
|
||||
self.pin.set_io_pull(Pull::Down);
|
||||
self.direction_out();
|
||||
Gpio { gpio: self.gpio, pin: self.pin, _mode: PhantomData }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn direction_in(&mut self) {
|
||||
unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).direction as *const _ as *mut _;
|
||||
u32_set_bit(p, false, GPIO::INDEX as usize);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn direction_out(&mut self) {
|
||||
unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).direction as *const _ as *mut _;
|
||||
u32_set_bit(p, true, GPIO::INDEX as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN, MODE> InputPin for Gpio<GPIO, PIN, Input<MODE>> {
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn try_is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_input as *const _ as *const _;
|
||||
u32_bit_is_set(p, GPIO::INDEX as usize)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_input as *const _ as *const _;
|
||||
u32_bit_is_clear(p, GPIO::INDEX as usize)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN> OutputPin for Gpio<GPIO, PIN, Output> {
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn try_set_high(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_output as *const _ as *mut _;
|
||||
u32_set_bit(p, true, GPIO::INDEX as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_set_low(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_output as *const _ as *mut _;
|
||||
u32_set_bit(p, false, GPIO::INDEX as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN> StatefulOutputPin for Gpio<GPIO, PIN, Output> {
|
||||
fn try_is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_output as *const _ as *const _;
|
||||
u32_bit_is_set(p, GPIO::INDEX as usize)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_output as *const _ as *const _;
|
||||
u32_bit_is_clear(p, GPIO::INDEX as usize)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<GPIO: GpioIndex, PIN> ToggleableOutputPin for Gpio<GPIO, PIN, Output> {
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn try_toggle(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
let p = &(*pac::GPIO::ptr()).data_output as *const _ as *mut _;
|
||||
u32_toggle_bit(p, GPIO::INDEX as usize);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,346 @@
|
||||
//! High-speed GPIO peripheral (GPIOHS)
|
||||
|
||||
use crate::pac::GPIOHS;
|
||||
use core::marker::PhantomData;
|
||||
use crate::bit_utils::{u32_set_bit, u32_toggle_bit, u32_bit_is_set, u32_bit_is_clear};
|
||||
use embedded_hal::digital::{InputPin, OutputPin};
|
||||
|
||||
// todo: verify
|
||||
|
||||
/// Floating mode (type state)
|
||||
pub struct Floating;
|
||||
|
||||
/// PullUp mode (type state)
|
||||
pub struct PullUp;
|
||||
|
||||
/// Input mode (type state)
|
||||
pub struct Input<MODE>(MODE);
|
||||
|
||||
/// Output mode (type state)
|
||||
pub struct Output<MODE>(MODE);
|
||||
|
||||
pub trait GpiohsExt {
|
||||
fn split(self) -> Parts;
|
||||
}
|
||||
|
||||
impl GpiohsExt for GPIOHS {
|
||||
fn split(self) -> Parts {
|
||||
Parts {
|
||||
gpiohs0: Gpiohs0 { _mode: PhantomData },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Parts {
|
||||
pub gpiohs0: Gpiohs0<Input<Floating>>,
|
||||
}
|
||||
|
||||
pub struct Gpiohs0<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE> Gpiohs0<MODE> {
|
||||
pub fn into_pull_up_input(self) -> Gpiohs0<Input<PullUp>> {
|
||||
GPIOHS::set_output_en(0, false);
|
||||
GPIOHS::set_input_en(0, true);
|
||||
GPIOHS::set_pullup_en(0, true);
|
||||
Gpiohs0 { _mode: PhantomData }
|
||||
}
|
||||
|
||||
// todo: all modes
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct Edge: u8 {
|
||||
const RISING = 0b00000001;
|
||||
const FALLING = 0b00000010;
|
||||
const HIGH = 0b00000100;
|
||||
const LOW = 0b00001000;
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> Gpiohs0<MODE> {
|
||||
pub fn trigger_on_edge(&mut self, edge: Edge) {
|
||||
// clear all pending bits
|
||||
GPIOHS::clear_rise_ip(0);
|
||||
GPIOHS::clear_fall_ip(0);
|
||||
GPIOHS::clear_high_ip(0);
|
||||
GPIOHS::clear_low_ip(0);
|
||||
// enable interrupts according to flags
|
||||
GPIOHS::set_rise_ie(0, edge.contains(Edge::RISING));
|
||||
GPIOHS::set_fall_ie(0, edge.contains(Edge::FALLING));
|
||||
GPIOHS::set_high_ie(0, edge.contains(Edge::HIGH));
|
||||
GPIOHS::set_low_ie(0, edge.contains(Edge::LOW));
|
||||
}
|
||||
|
||||
pub fn check_edges(&self) -> Edge {
|
||||
let mut ans = Edge::empty();
|
||||
if GPIOHS::has_rise_ip(0) {
|
||||
ans |= Edge::RISING;
|
||||
}
|
||||
if GPIOHS::has_fall_ip(0) {
|
||||
ans |= Edge::FALLING;
|
||||
}
|
||||
if GPIOHS::has_high_ip(0) {
|
||||
ans |= Edge::HIGH;
|
||||
}
|
||||
if GPIOHS::has_low_ip(0) {
|
||||
ans |= Edge::LOW;
|
||||
}
|
||||
ans
|
||||
}
|
||||
|
||||
pub fn clear_interrupt_pending_bits(&mut self) {
|
||||
if GPIOHS::has_rise_ie(0) {
|
||||
GPIOHS::set_rise_ie(0, false);
|
||||
GPIOHS::clear_rise_ip(0);
|
||||
GPIOHS::set_rise_ie(0, true);
|
||||
}
|
||||
if GPIOHS::has_fall_ie(0) {
|
||||
GPIOHS::set_fall_ie(0, false);
|
||||
GPIOHS::clear_fall_ip(0);
|
||||
GPIOHS::set_fall_ie(0, true);
|
||||
}
|
||||
if GPIOHS::has_high_ie(0) {
|
||||
GPIOHS::set_high_ie(0, false);
|
||||
GPIOHS::clear_high_ip(0);
|
||||
GPIOHS::set_high_ie(0, true);
|
||||
}
|
||||
if GPIOHS::has_low_ie(0) {
|
||||
GPIOHS::set_low_ie(0, false);
|
||||
GPIOHS::clear_low_ip(0);
|
||||
GPIOHS::set_low_ie(0, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> InputPin for Gpiohs0<Input<MODE>> {
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn try_is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*GPIOHS::ptr()).input_val as *const _ as *const _;
|
||||
u32_bit_is_set(p, 0)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe {
|
||||
let p = &(*GPIOHS::ptr()).input_val as *const _ as *const _;
|
||||
u32_bit_is_clear(p, 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> OutputPin for Gpiohs0<Output<MODE>> {
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn try_set_high(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
let p = &(*GPIOHS::ptr()).output_val as *const _ as *mut _;
|
||||
u32_set_bit(p, true, 0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_set_low(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
let p = &(*GPIOHS::ptr()).output_val as *const _ as *mut _;
|
||||
u32_set_bit(p, false, 0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
trait GpiohsAccess {
|
||||
fn peripheral() -> &'static mut crate::pac::gpiohs::RegisterBlock;
|
||||
|
||||
fn set_drive(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().drive as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn input_value(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().input_val as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_input_en(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().input_en as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_iof_en(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().iof_en as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_iof_sel(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().iof_sel as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_output_en(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().output_en as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_output_value(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().output_val as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_output_xor(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().output_xor as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_pin(index: usize) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().output_val as *mut _ as *mut _;
|
||||
u32_toggle_bit(p, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_pullup_en(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().pullup_en as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_rise_ie(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().rise_ie as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_rise_ip(index: usize) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().rise_ip as *mut _ as *mut _;
|
||||
u32_set_bit(p, true, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fall_ie(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().fall_ie as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_fall_ip(index: usize) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().fall_ip as *mut _ as *mut _;
|
||||
u32_set_bit(p, true, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_high_ie(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().high_ie as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_high_ip(index: usize,) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().high_ip as *mut _ as *mut _;
|
||||
u32_set_bit(p, true, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_low_ie(index: usize, bit: bool) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().low_ie as *mut _ as *mut _;
|
||||
u32_set_bit(p, bit, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_low_ip(index: usize) {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().low_ip as *mut _ as *mut _;
|
||||
u32_set_bit(p, true, index);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_rise_ie(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().rise_ie as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_fall_ie(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().fall_ie as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_high_ie(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().high_ie as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_low_ie(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().low_ie as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_rise_ip(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().rise_ip as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_fall_ip(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().fall_ip as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_high_ip(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().high_ip as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_low_ip(index: usize) -> bool {
|
||||
unsafe {
|
||||
let p = &mut Self::peripheral().low_ip as *mut _ as *mut _;
|
||||
u32_bit_is_set(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl GpiohsAccess for GPIOHS {
|
||||
fn peripheral() -> &'static mut crate::pac::gpiohs::RegisterBlock {
|
||||
unsafe { &mut *(GPIOHS::ptr() as *mut _) }
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
//! HAL for the K210 SoC
|
||||
//!
|
||||
//! This is an implementation of the [`embedded-hal`] traits for the K210 SoC
|
||||
|
||||
// #![deny(missing_docs)] // uncomment for every releases
|
||||
#![no_std]
|
||||
|
||||
pub use k210_pac as pac;
|
||||
|
||||
pub mod aes;
|
||||
pub mod apu;
|
||||
pub mod cache;
|
||||
pub mod clint;
|
||||
pub mod clock;
|
||||
pub mod dmac;
|
||||
pub mod fft;
|
||||
pub mod fpioa;
|
||||
pub mod gpio;
|
||||
pub mod gpiohs;
|
||||
pub mod plic;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
pub mod sha256;
|
||||
pub mod stdout;
|
||||
pub mod sysctl;
|
||||
pub mod time;
|
||||
|
||||
/// Prelude
|
||||
pub mod prelude {
|
||||
pub use embedded_hal::prelude::*;
|
||||
pub use crate::serial::SerialExt as _k210_hal_serial_SerialExt;
|
||||
pub use crate::stdout::Write as _k210_hal_stdout_Write;
|
||||
pub use crate::time::U32Ext as _k210_hal_time_U32Ext;
|
||||
pub use crate::fpioa::FpioaExt as _k210_hal_fpioa_FpioaExt;
|
||||
pub use crate::sysctl::SysctlExt as _k210_hal_sysctl_SysctlExt;
|
||||
pub use crate::gpio::GpioExt as _k210_hal_gpio_GpioExt;
|
||||
pub use crate::gpiohs::GpiohsExt as _k210_hal_gpiohs_GpiohsExt;
|
||||
pub use crate::plic::PlicExt as _k210_hal_plic_PlicExt;
|
||||
}
|
||||
|
||||
mod bit_utils {
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn u32_set_bit(p: *mut u32, bit: bool, index: usize) {
|
||||
let mask = 1 << index;
|
||||
if bit {
|
||||
*p |= mask;
|
||||
} else {
|
||||
*p &= !mask;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn u32_toggle_bit(p: *mut u32, index: usize) {
|
||||
let mask = 1 << index;
|
||||
*p ^= mask;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn u32_bit_is_set(r: *const u32, index: usize) -> bool {
|
||||
(*r & 1 << index) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) unsafe fn u32_bit_is_clear(r: *const u32, index: usize) -> bool {
|
||||
(*r & 1 << index) == 0
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
//! Platform-Level Interrupt Controller (PLIC)
|
||||
|
||||
// I don't know if this part should be implemented in runtime
|
||||
// keep by now, may be replaced on further designs
|
||||
// #[doc(hidden)]
|
||||
// #[link_name = "MachineExternal"]
|
||||
// pub extern fn machine_external() {
|
||||
|
||||
// }
|
||||
|
||||
use crate::pac::{PLIC, Interrupt};
|
||||
|
||||
/// Extension trait for PLIC interrupt controller peripheral
|
||||
pub trait PlicExt {
|
||||
/// Interrupt wrapper type
|
||||
type Interrupt: Nr;
|
||||
/// Is this M-Mode interrupt enabled on given hart?
|
||||
fn is_enabled(hart_id: usize, interrupt: Self::Interrupt) -> bool;
|
||||
/// Enable an interrupt for a given hart
|
||||
unsafe fn unmask(hart_id: usize, interrupt: Self::Interrupt);
|
||||
/// Disable an interrupt for a given hart
|
||||
fn mask(hart_id: usize, interrupt: Self::Interrupt);
|
||||
/// Get global priority for one interrupt
|
||||
fn get_priority(interrupt: Self::Interrupt) -> Priority;
|
||||
/// Globally set priority for one interrupt
|
||||
unsafe fn set_priority(interrupt: Self::Interrupt, prio: Priority);
|
||||
/// Get priority threshold for a given hart
|
||||
fn get_threshold(hart_id: usize) -> Priority;
|
||||
/// Set the priority threshold for a given hart
|
||||
unsafe fn set_threshold(hart_id: usize, threshold: Priority);
|
||||
/// Mark that given hart have claimed to handle this interrupt
|
||||
fn claim(hart_id: usize) -> Option<Self::Interrupt>;
|
||||
/// Mark that given hart have completed handling this interrupt
|
||||
fn complete(hart_id: usize, interrupt: Self::Interrupt);
|
||||
/// Is this interrupt claimed and under procceeding?
|
||||
fn is_pending(interrupt: Self::Interrupt) -> bool;
|
||||
}
|
||||
|
||||
impl PlicExt for PLIC {
|
||||
type Interrupt = Interrupt;
|
||||
fn is_enabled(hart_id: usize, interrupt: Interrupt) -> bool {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
unsafe {
|
||||
(*PLIC::ptr()).target_enables[hart_id].enable[irq_number / 32]
|
||||
.read().bits() & 1 << (irq_number % 32) != 0
|
||||
}
|
||||
}
|
||||
unsafe fn unmask(hart_id: usize, interrupt: Interrupt) {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
(*PLIC::ptr()).target_enables[hart_id].enable[irq_number / 32]
|
||||
.modify(|r, w|
|
||||
w.bits(r.bits() | 1 << (irq_number % 32)));
|
||||
}
|
||||
fn mask(hart_id: usize, interrupt: Interrupt) {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
unsafe {
|
||||
(*PLIC::ptr()).target_enables[hart_id].enable[irq_number / 32]
|
||||
.modify(|r, w|
|
||||
w.bits(r.bits() & !(1 << (irq_number % 32))));
|
||||
}
|
||||
}
|
||||
fn get_priority(interrupt: Interrupt) -> Priority {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
let bits = unsafe {
|
||||
(*PLIC::ptr()).priority[irq_number].read().bits()
|
||||
};
|
||||
Priority::from_bits(bits)
|
||||
}
|
||||
unsafe fn set_priority(interrupt: Interrupt, prio: Priority) {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
(*PLIC::ptr()).priority[irq_number].write(
|
||||
|w|
|
||||
w.bits(prio.into_bits()));
|
||||
}
|
||||
fn get_threshold(hart_id: usize) -> Priority {
|
||||
let bits = unsafe {
|
||||
(*PLIC::ptr()).targets[hart_id].threshold.read().bits()
|
||||
};
|
||||
Priority::from_bits(bits)
|
||||
}
|
||||
unsafe fn set_threshold(hart_id: usize, threshold: Priority) {
|
||||
(*PLIC::ptr()).targets[hart_id].threshold.write(
|
||||
|w|
|
||||
w.bits(threshold.into_bits()));
|
||||
}
|
||||
fn claim(hart_id: usize) -> Option<Interrupt> {
|
||||
let bits = unsafe {
|
||||
(*PLIC::ptr()).targets[hart_id].claim.read().bits()
|
||||
};
|
||||
Nr::from_bits(bits)
|
||||
}
|
||||
fn complete(hart_id: usize, interrupt: Interrupt) {
|
||||
unsafe {
|
||||
(*PLIC::ptr()).targets[hart_id].claim.write(
|
||||
|w|
|
||||
w.bits(interrupt.into_bits()));
|
||||
}
|
||||
}
|
||||
fn is_pending(interrupt: Interrupt) -> bool {
|
||||
let irq_number = interrupt.into_bits() as usize;
|
||||
unsafe {
|
||||
(*PLIC::ptr()).pending[irq_number / 32]
|
||||
.read().bits() & 1 << (irq_number % 32) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Nr for Interrupt {
|
||||
fn into_bits(&self) -> u32 {
|
||||
*self as u8 as u32
|
||||
}
|
||||
fn from_bits(bits: u32) -> Option<Self> {
|
||||
use Interrupt::*;
|
||||
match bits {
|
||||
0 => None,
|
||||
1 => Some(SPI0),
|
||||
2 => Some(SPI1),
|
||||
3 => Some(SPI_SLAVE),
|
||||
4 => Some(SPI3),
|
||||
5 => Some(I2S0),
|
||||
6 => Some(I2S1),
|
||||
7 => Some(I2S2),
|
||||
8 => Some(I2C0),
|
||||
9 => Some(I2C1),
|
||||
10 => Some(I2C2),
|
||||
11 => Some(UART1),
|
||||
12 => Some(UART2),
|
||||
13 => Some(UART3),
|
||||
14 => Some(TIMER0A),
|
||||
15 => Some(TIMER0B),
|
||||
16 => Some(TIMER1A),
|
||||
17 => Some(TIMER1B),
|
||||
18 => Some(TIMER2A),
|
||||
19 => Some(TIMER2B),
|
||||
20 => Some(RTC),
|
||||
21 => Some(WDT0),
|
||||
22 => Some(WDT1),
|
||||
23 => Some(APB_GPIO),
|
||||
24 => Some(DVP),
|
||||
25 => Some(KPU),
|
||||
26 => Some(FFT),
|
||||
27 => Some(DMA0),
|
||||
28 => Some(DMA1),
|
||||
29 => Some(DMA2),
|
||||
30 => Some(DMA3),
|
||||
31 => Some(DMA4),
|
||||
32 => Some(DMA5),
|
||||
33 => Some(UARTHS),
|
||||
34 => Some(GPIOHS0),
|
||||
35 => Some(GPIOHS1),
|
||||
36 => Some(GPIOHS2),
|
||||
37 => Some(GPIOHS3),
|
||||
38 => Some(GPIOHS4),
|
||||
39 => Some(GPIOHS5),
|
||||
40 => Some(GPIOHS6),
|
||||
41 => Some(GPIOHS7),
|
||||
42 => Some(GPIOHS8),
|
||||
43 => Some(GPIOHS9),
|
||||
44 => Some(GPIOHS10),
|
||||
45 => Some(GPIOHS11),
|
||||
46 => Some(GPIOHS12),
|
||||
47 => Some(GPIOHS13),
|
||||
48 => Some(GPIOHS14),
|
||||
49 => Some(GPIOHS15),
|
||||
50 => Some(GPIOHS16),
|
||||
51 => Some(GPIOHS17),
|
||||
52 => Some(GPIOHS18),
|
||||
53 => Some(GPIOHS19),
|
||||
54 => Some(GPIOHS20),
|
||||
55 => Some(GPIOHS21),
|
||||
56 => Some(GPIOHS22),
|
||||
57 => Some(GPIOHS23),
|
||||
58 => Some(GPIOHS24),
|
||||
59 => Some(GPIOHS25),
|
||||
60 => Some(GPIOHS26),
|
||||
61 => Some(GPIOHS27),
|
||||
62 => Some(GPIOHS28),
|
||||
63 => Some(GPIOHS29),
|
||||
64 => Some(GPIOHS30),
|
||||
65 => Some(GPIOHS31),
|
||||
_ => panic!("invalid interrupt bits")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Nr: Sized {
|
||||
fn into_bits(&self) -> u32;
|
||||
fn from_bits(bits: u32) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// Priority of an interrupt
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Priority {
|
||||
/// Priority 0: Never interrupt
|
||||
P0,
|
||||
/// Priority 1: Lowest active priority
|
||||
P1,
|
||||
/// Priority 2
|
||||
P2,
|
||||
/// Priority 3
|
||||
P3,
|
||||
/// Priority 4
|
||||
P4,
|
||||
/// Priority 5
|
||||
P5,
|
||||
/// Priority 6
|
||||
P6,
|
||||
/// Priority 7: Highest priority
|
||||
P7,
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
fn into_bits(self) -> u32 {
|
||||
match self {
|
||||
Priority::P0 => 0,
|
||||
Priority::P1 => 1,
|
||||
Priority::P2 => 2,
|
||||
Priority::P3 => 3,
|
||||
Priority::P4 => 4,
|
||||
Priority::P5 => 5,
|
||||
Priority::P6 => 6,
|
||||
Priority::P7 => 7,
|
||||
}
|
||||
}
|
||||
fn from_bits(prio: u32) -> Priority {
|
||||
match prio {
|
||||
0 => Priority::P0,
|
||||
1 => Priority::P1,
|
||||
2 => Priority::P2,
|
||||
3 => Priority::P3,
|
||||
4 => Priority::P4,
|
||||
5 => Priority::P5,
|
||||
6 => Priority::P6,
|
||||
7 => Priority::P7,
|
||||
_ => panic!("Invalid priority"),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
//! Serial interface
|
||||
//!
|
||||
//! You can use the `Serial` interface with these UART instances:
|
||||
//! * [`UARTHS`](crate::pac::UARTHS)
|
||||
//! * [`UART1`](crate::pac::UART1)
|
||||
//! * [`UART2`](crate::pac::UART2)
|
||||
//! * [`UART3`](crate::pac::UART3)
|
||||
|
||||
use core::mem;
|
||||
use core::convert::Infallible;
|
||||
|
||||
use embedded_hal::serial;
|
||||
|
||||
use crate::pac::{UARTHS,uart1,UART1,UART2,UART3};
|
||||
use crate::clock::Clocks;
|
||||
use crate::time::Bps;
|
||||
|
||||
/// Extension trait that constrains UART peripherals
|
||||
pub trait SerialExt: Sized {
|
||||
/// Configures a UART peripheral to provide serial communication
|
||||
fn configure(self, baud_rate: Bps, clocks: &Clocks) -> Serial<Self>;
|
||||
}
|
||||
|
||||
/// Serial abstraction
|
||||
pub struct Serial<UART> {
|
||||
uart: UART,
|
||||
}
|
||||
|
||||
impl<UART> Serial<UART> {
|
||||
/// Splits the `Serial` abstraction into a transmitter and a
|
||||
/// receiver half
|
||||
pub fn split(self) -> (Tx<UART>, Rx<UART>) {
|
||||
(
|
||||
Tx {
|
||||
uart: self.uart,
|
||||
},
|
||||
Rx {
|
||||
uart: unsafe { mem::MaybeUninit::uninit().assume_init() },
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Forms `Serial` abstraction from a transmitter and a
|
||||
/// receiver half
|
||||
pub fn join(tx: Tx<UART>, rx: Rx<UART>) -> Self {
|
||||
let _ = rx; // note(discard): Zero-sized typestate struct
|
||||
Serial { uart: tx.uart }
|
||||
}
|
||||
|
||||
/// Releases the UART peripheral
|
||||
pub fn free(self) -> UART {
|
||||
// todo: power down this UART
|
||||
self.uart
|
||||
}
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct Tx<UART> {
|
||||
uart: UART,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<UART> {
|
||||
uart: UART,
|
||||
}
|
||||
|
||||
impl SerialExt for UARTHS {
|
||||
fn configure(self, baud_rate: Bps, clocks: &Clocks) -> Serial<UARTHS>
|
||||
{
|
||||
let uart = self;
|
||||
|
||||
let div = clocks.cpu().0 / baud_rate.0 - 1;
|
||||
unsafe {
|
||||
uart.div.write(|w| w.bits(div));
|
||||
}
|
||||
|
||||
uart.txctrl.write(|w| w.txen().bit(true));
|
||||
uart.rxctrl.write(|w| w.rxen().bit(true));
|
||||
|
||||
Serial { uart }
|
||||
}
|
||||
}
|
||||
|
||||
impl Serial<UARTHS> {
|
||||
/// Starts listening for an interrupt event
|
||||
pub fn listen(self) -> Self {
|
||||
self.uart.ie.write(|w| w.txwm().bit(false).rxwm().bit(true));
|
||||
self
|
||||
}
|
||||
|
||||
/// Stops listening for an interrupt event
|
||||
pub fn unlisten(self) -> Self {
|
||||
self.uart
|
||||
.ie
|
||||
.write(|w| w.txwm().bit(false).rxwm().bit(false));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl serial::Read<u8> for Rx<UARTHS> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_read(&mut self) -> nb::Result<u8, Infallible> {
|
||||
let rxdata = self.uart.rxdata.read();
|
||||
|
||||
if rxdata.empty().bit_is_set() {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(rxdata.data().bits() as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serial::Write<u8> for Tx<UARTHS> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_write(&mut self, byte: u8) -> nb::Result<(), Infallible> {
|
||||
let txdata = self.uart.txdata.read();
|
||||
|
||||
if txdata.full().bit_is_set() {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
unsafe {
|
||||
(*UARTHS::ptr()).txdata.write(|w| w.data().bits(byte));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_flush(&mut self) -> nb::Result<(), Infallible> {
|
||||
let txdata = self.uart.txdata.read();
|
||||
|
||||
if txdata.full().bit_is_set() {
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod closed_trait {
|
||||
use core::ops::Deref;
|
||||
/// Trait to be able to generalize over UART1/UART2/UART3
|
||||
pub trait UartX: Deref<Target = super::uart1::RegisterBlock> {
|
||||
const INDEX: u8;
|
||||
}
|
||||
}
|
||||
use closed_trait::UartX;
|
||||
|
||||
impl UartX for UART1 { const INDEX: u8 = 1; }
|
||||
impl UartX for UART2 { const INDEX: u8 = 2; }
|
||||
impl UartX for UART3 { const INDEX: u8 = 3; }
|
||||
|
||||
const UART_RECEIVE_FIFO_1: u32 = 0;
|
||||
const UART_SEND_FIFO_8: u32 = 3;
|
||||
|
||||
impl<UART: UartX> SerialExt for UART {
|
||||
fn configure(self, baud_rate: Bps, clocks: &Clocks) -> Serial<UART> {
|
||||
let uart = self;
|
||||
|
||||
// Hardcode these for now:
|
||||
let data_width = 8; // 8 data bits
|
||||
let stopbit_val = 0; // 1 stop bit
|
||||
let parity_val = 0; // No parity
|
||||
// Note: need to make sure that UARTx clock is enabled through sysctl before here
|
||||
let divisor = clocks.apb0().0 / baud_rate.0;
|
||||
let dlh = ((divisor >> 12) & 0xff) as u8;
|
||||
let dll = ((divisor >> 4) & 0xff) as u8;
|
||||
let dlf = (divisor & 0xf) as u8;
|
||||
unsafe {
|
||||
// Set Divisor Latch Access Bit (enables DLL DLH) to set baudrate
|
||||
uart.lcr.write(|w| w.bits(1 << 7));
|
||||
uart.dlh_ier.write(|w| w.bits(dlh.into()));
|
||||
uart.rbr_dll_thr.write(|w| w.bits(dll.into()));
|
||||
uart.dlf.write(|w| w.bits(dlf.into()));
|
||||
// Clear Divisor Latch Access Bit after setting baudrate
|
||||
uart.lcr.write(|w| w.bits((data_width - 5) | (stopbit_val << 2) | (parity_val << 3)));
|
||||
// Write IER
|
||||
uart.dlh_ier.write(|w| w.bits(0x80)); /* THRE */
|
||||
// Write FCT
|
||||
uart.fcr_iir.write(|w| w.bits(UART_RECEIVE_FIFO_1 << 6 | UART_SEND_FIFO_8 << 4 | 0x1 << 3 | 0x1));
|
||||
}
|
||||
|
||||
Serial { uart }
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: UartX> Serial<UART> {
|
||||
/// Starts listening for an interrupt event
|
||||
pub fn listen(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
/// Stops listening for an interrupt event
|
||||
pub fn unlisten(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: UartX> serial::Read<u8> for Rx<UART> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_read(&mut self) -> nb::Result<u8, Infallible> {
|
||||
let lsr = self.uart.lsr.read();
|
||||
|
||||
if (lsr.bits() & (1<<0)) == 0 { // Data Ready bit
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
let rbr = self.uart.rbr_dll_thr.read();
|
||||
Ok((rbr.bits() & 0xff) as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<UART: UartX> serial::Write<u8> for Tx<UART> {
|
||||
type Error = Infallible;
|
||||
|
||||
fn try_write(&mut self, byte: u8) -> nb::Result<(), Infallible> {
|
||||
let lsr = self.uart.lsr.read();
|
||||
|
||||
if (lsr.bits() & (1<<5)) != 0 { // Transmit Holding Register Empty bit
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
unsafe {
|
||||
self.uart.rbr_dll_thr.write(|w| w.bits(byte.into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn try_flush(&mut self) -> nb::Result<(), Infallible> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
//! Secure Hash Algorithm-256 (SHA256)
|
||||
|
||||
use crate::pac::SHA256;
|
||||
use crate::sysctl::{self, APB0};
|
||||
|
||||
/// SHA256 module abstraction
|
||||
pub struct Sha256 {
|
||||
sha256: SHA256,
|
||||
}
|
||||
|
||||
impl Sha256 {
|
||||
pub fn sha256(sha256: SHA256, apb0: &mut APB0) -> Sha256 {
|
||||
apb0.enable();
|
||||
sysctl::clk_en_peri().modify(|_r, w|
|
||||
w.sha_clk_en().set_bit());
|
||||
sysctl::peri_reset().modify(|_r, w|
|
||||
w.sha_reset().set_bit());
|
||||
sysctl::peri_reset().modify(|_r, w|
|
||||
w.sha_reset().clear_bit());
|
||||
Sha256 { sha256 }
|
||||
}
|
||||
|
||||
pub fn new_digest(self) -> Digest {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn release(self) -> SHA256 {
|
||||
sysctl::clk_en_peri().modify(|_r, w|
|
||||
w.sha_clk_en().clear_bit());
|
||||
self.sha256
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Digest {
|
||||
sha256: SHA256,
|
||||
}
|
||||
|
||||
impl Digest {
|
||||
pub fn write_u32(&mut self, n: u32) {
|
||||
let _todo = n;
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn finish(&self, out: &mut [u8; 32]) {
|
||||
let _todo = out;
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn free(self) -> Sha256 {
|
||||
Sha256 { sha256: self.sha256 }
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
//! (TODO) Serial Peripheral Interface (SPI)
|
||||
|
||||
use crate::pac::SPI0;
|
||||
use crate::clock::Clocks;
|
||||
use crate::sysctl::{self, APB0};
|
||||
pub use embedded_hal::spi::{Mode, Polarity, Phase};
|
||||
use core::convert::Infallible;
|
||||
|
||||
///
|
||||
pub struct Spi<SPI> {
|
||||
spi: SPI
|
||||
}
|
||||
|
||||
impl Spi<SPI0> {
|
||||
pub fn spi0(
|
||||
spi: SPI0,
|
||||
mode: Mode,
|
||||
frame_format: FrameFormat,
|
||||
endian: Endian,
|
||||
clock: &Clocks,
|
||||
apb0: &mut APB0
|
||||
) -> Self {
|
||||
let work_mode = hal_mode_to_pac(mode);
|
||||
let frame_format = frame_format_to_pac(frame_format);
|
||||
let tmod = crate::pac::spi0::ctrlr0::TMOD_A::TRANS_RECV; // todo other modes
|
||||
let endian = endian as u32;
|
||||
let data_bit_length = 8; // todo more length
|
||||
let _ = clock; // todo
|
||||
unsafe {
|
||||
// no interrupts for now
|
||||
spi.imr.write(|w| w.bits(0x00));
|
||||
// no dma for now
|
||||
spi.dmacr.write(|w| w.bits(0x00));
|
||||
spi.dmatdlr.write(|w| w.bits(0x10));
|
||||
spi.dmardlr.write(|w| w.bits(0x00));
|
||||
// no slave access for now
|
||||
spi.ser.write(|w| w.bits(0x00));
|
||||
spi.ssienr.write(|w| w.bits(0x00));
|
||||
// set control registers
|
||||
spi.ctrlr0.write(|w| {
|
||||
w.work_mode()
|
||||
.variant(work_mode)
|
||||
.tmod()
|
||||
.variant(tmod)
|
||||
.frame_format()
|
||||
.variant(frame_format)
|
||||
.data_length()
|
||||
.bits(data_bit_length - 1)
|
||||
});
|
||||
spi.spi_ctrlr0.reset(); // standard
|
||||
spi.endian.write(|w| w.bits(endian));
|
||||
}
|
||||
// enable APB0 bus
|
||||
apb0.enable();
|
||||
// enable peripheral via sysctl
|
||||
sysctl::clk_en_peri().modify(|_r, w|
|
||||
w.spi0_clk_en().set_bit());
|
||||
Spi { spi }
|
||||
}
|
||||
|
||||
pub fn release(self) -> SPI0 {
|
||||
// power off
|
||||
sysctl::clk_en_peri().modify(|_r, w|
|
||||
w.spi0_clk_en().clear_bit());
|
||||
self.spi
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::spi::FullDuplex<u8> for Spi<SPI0> {
|
||||
/// An enumeration of SPI errors
|
||||
type Error = Infallible;
|
||||
|
||||
/// Reads the word stored in the shift register
|
||||
///
|
||||
/// **NOTE** A word must be sent to the slave before attempting to call this
|
||||
/// method.
|
||||
fn try_read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Sends a word to the slave
|
||||
fn try_send(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
todo!("{}", word)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FrameFormat {
|
||||
Standard,
|
||||
Dual,
|
||||
Quad,
|
||||
Octal,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum Endian {
|
||||
Little = 0,
|
||||
Big = 1,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hal_mode_to_pac(mode: Mode) -> crate::pac::spi0::ctrlr0::WORK_MODE_A {
|
||||
use crate::pac::spi0::ctrlr0::WORK_MODE_A;
|
||||
use {Polarity::*, Phase::*};
|
||||
match (mode.polarity, mode.phase) {
|
||||
(IdleLow, CaptureOnFirstTransition) => WORK_MODE_A::MODE0,
|
||||
(IdleLow, CaptureOnSecondTransition) => WORK_MODE_A::MODE1,
|
||||
(IdleHigh, CaptureOnFirstTransition) => WORK_MODE_A::MODE2,
|
||||
(IdleHigh, CaptureOnSecondTransition) => WORK_MODE_A::MODE3,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn frame_format_to_pac(frame_format: FrameFormat) -> crate::pac::spi0::ctrlr0::FRAME_FORMAT_A {
|
||||
use crate::pac::spi0::ctrlr0::FRAME_FORMAT_A;
|
||||
match frame_format {
|
||||
FrameFormat::Standard => FRAME_FORMAT_A::STANDARD,
|
||||
FrameFormat::Dual => FRAME_FORMAT_A::DUAL,
|
||||
FrameFormat::Quad => FRAME_FORMAT_A::QUAD,
|
||||
FrameFormat::Octal => FRAME_FORMAT_A::OCTAL,
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
//! Stdout
|
||||
pub use core::fmt::Write;
|
||||
use nb::block;
|
||||
|
||||
/// Stdout implements the core::fmt::Write trait for hal::serial::Write
|
||||
/// implementations.
|
||||
pub struct Stdout<'p, T>(pub &'p mut T);
|
||||
|
||||
impl<'p, T> Write for Stdout<'p, T>
|
||||
where
|
||||
T: embedded_hal::serial::Write<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for byte in s.as_bytes() {
|
||||
if *byte == b'\n' {
|
||||
let res = block!(self.0.try_write(b'\r'));
|
||||
|
||||
if res.is_err() {
|
||||
return Err(core::fmt::Error);
|
||||
}
|
||||
}
|
||||
|
||||
let res = block!(self.0.try_write(*byte));
|
||||
|
||||
if res.is_err() {
|
||||
return Err(core::fmt::Error);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
//! (TODO) System Controller (SYSCTL)
|
||||
|
||||
use crate::pac::{sysctl, SYSCTL};
|
||||
|
||||
pub(crate) fn sysctl<'a>() -> &'a sysctl::RegisterBlock {
|
||||
unsafe { &*(SYSCTL::ptr()) }
|
||||
}
|
||||
|
||||
pub(crate) fn clk_en_cent<'a>() -> &'a sysctl::CLK_EN_CENT {
|
||||
&sysctl().clk_en_cent
|
||||
}
|
||||
|
||||
pub(crate) fn clk_en_peri<'a>() -> &'a sysctl::CLK_EN_PERI {
|
||||
&sysctl().clk_en_peri
|
||||
}
|
||||
|
||||
pub(crate) fn peri_reset<'a>() -> &'a sysctl::PERI_RESET {
|
||||
&sysctl().peri_reset
|
||||
}
|
||||
|
||||
pub trait SysctlExt {
|
||||
fn constrain(self) -> Parts;
|
||||
}
|
||||
|
||||
impl SysctlExt for SYSCTL {
|
||||
fn constrain(self) -> Parts {
|
||||
Parts {
|
||||
apb0: APB0 { _ownership: () },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ref: sysctl.c
|
||||
pub struct Parts {
|
||||
// todo: PLL0, PLL1, PLL2
|
||||
// todo: CPU, SRAM, APB-bus, ROM, DMA, AI
|
||||
pub apb0: APB0,
|
||||
// pub apb1: APB1,
|
||||
// pub apb2: APB2,
|
||||
}
|
||||
|
||||
pub struct APB0 {
|
||||
_ownership: ()
|
||||
}
|
||||
|
||||
impl APB0 {
|
||||
pub(crate) fn enable(&mut self) {
|
||||
clk_en_cent().modify(
|
||||
|_r, w|
|
||||
w.apb0_clk_en().set_bit()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// pub struct APB1 {
|
||||
// _ownership: ()
|
||||
// }
|
||||
|
||||
// pub struct APB2 {
|
||||
// _ownership: ()
|
||||
// }
|
@ -0,0 +1,68 @@
|
||||
//! Time units
|
||||
|
||||
/// Bits per second
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Bps(pub u32);
|
||||
|
||||
/// Hertz
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Hertz(pub u32);
|
||||
|
||||
/// KiloHertz
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct KiloHertz(pub u32);
|
||||
|
||||
/// MegaHertz
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MegaHertz(pub u32);
|
||||
|
||||
/// Extension trait that adds convenience methods to the `u32` type
|
||||
pub trait U32Ext {
|
||||
/// Wrap in `Bps`
|
||||
fn bps(self) -> Bps;
|
||||
|
||||
/// Wrap in `Hertz`
|
||||
fn hz(self) -> Hertz;
|
||||
|
||||
/// Wrap in `KiloHertz`
|
||||
fn khz(self) -> KiloHertz;
|
||||
|
||||
/// Wrap in `MegaHertz`
|
||||
fn mhz(self) -> MegaHertz;
|
||||
}
|
||||
|
||||
impl U32Ext for u32 {
|
||||
fn bps(self) -> Bps {
|
||||
Bps(self)
|
||||
}
|
||||
|
||||
fn hz(self) -> Hertz {
|
||||
Hertz(self)
|
||||
}
|
||||
|
||||
fn khz(self) -> KiloHertz {
|
||||
KiloHertz(self)
|
||||
}
|
||||
|
||||
fn mhz(self) -> MegaHertz {
|
||||
MegaHertz(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hertz> for KiloHertz {
|
||||
fn into(self) -> Hertz {
|
||||
Hertz(self.0 * 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hertz> for MegaHertz {
|
||||
fn into(self) -> Hertz {
|
||||
Hertz(self.0 * 1_000_000)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<KiloHertz> for MegaHertz {
|
||||
fn into(self) -> KiloHertz {
|
||||
KiloHertz(self.0 * 1_000)
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
@ -0,0 +1,28 @@
|
||||
language: rust
|
||||
|
||||
env:
|
||||
- TARGET=x86_64-unknown-linux-gnu
|
||||
- TARGET=riscv64gc-unknown-none-elf
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- stable
|
||||
|
||||
if: (branch = staging OR branch = trying OR branch = master) OR (type = pull_request AND branch = master)
|
||||
|
||||
|
||||
install:
|
||||
- ci/install.sh
|
||||
|
||||
script:
|
||||
- ci/script.sh
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
@ -0,0 +1,37 @@
|
||||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [RISC-V team](https://github.com/rust-embedded/wg#the-riscv-team)
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
|
||||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
|
||||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
|
||||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [RISC-V team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
|
||||
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
|
||||
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
|
||||
|
||||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||
|
||||
The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org).
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-riscv-team
|
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "k210-pac"
|
||||
version = "0.2.0"
|
||||
authors = ["The RISC-V Team <risc-v@teams.rust-embedded.org>"]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
description = "Peripheral access API for K210 SoC"
|
||||
repository = "https://github.com/riscv-rust/k210-pac"
|
||||
keywords = ["riscv", "k210", "register", "peripheral"]
|
||||
license = "ISC"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bare-metal = "0.2.4"
|
||||
riscv = "0.6.0"
|
||||
riscv-rt = { version = "0.8", optional = true }
|
||||
vcell = "0.1.2"
|
||||
|
||||
[features]
|
||||
rt = ["riscv-rt"]
|
@ -0,0 +1,36 @@
|
||||
[](https://crates.io/crates/k210-pac)
|
||||
[](https://crates.io/crates/k210-pac)
|
||||
[](https://travis-ci.org/riscv-rust/k210-pac)
|
||||
|
||||
# `k210-pac`
|
||||
|
||||
> Peripheral access API for K210 SoC
|
||||
|
||||
This project is developed and maintained by the [RISC-V team][team].
|
||||
|
||||
## [Documentation](https://docs.rs/crate/k210-pac)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2019 [RISC-V team][team]
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-risc-v-team
|
@ -0,0 +1,15 @@
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
fn main() {
|
||||
// Put the linker script somewhere the linker can find it
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||
|
||||
fs::File::create(out_dir.join("memory-k210.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory-k210.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rerun-if-changed=memory-k210.x");
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
rustup target add $TARGET
|
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
cargo check --target $TARGET
|
||||
cargo check --target $TARGET --features rt
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
_max_hart_id = 1;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
SRAM : ORIGIN = 0x80000000, LENGTH = 6M
|
||||
AI_SRAM : ORIGIN = 0x80600000, LENGTH = 2M
|
||||
SRAM_NOCACHE : ORIGIN = 0x40000000, LENGTH = 6M
|
||||
AI_SRAM_NOCACHE : ORIGIN = 0x40600000, LENGTH = 2M
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
set -e
|
||||
|
||||
rm -rf src
|
||||
mkdir src
|
||||
svd2rust --target riscv -i k210.svd
|
||||
mv lib.rs src/
|
||||
cargo fmt
|
@ -0,0 +1,3 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
.idea/
|
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "k210-soc"
|
||||
version = "0.1.0"
|
||||
authors = ["Yifan Wu <shinbokuow@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
k210-hal = { path = "../k210-hal" }
|
||||
k210-pac = { path = "../k210-pac" }
|
||||
riscv = { path = "../riscv", features = ["inline-asm"] }
|
@ -0,0 +1 @@
|
||||
From [here](https://github.com/laanwj/k210-sdk-stuff/tree/master/rust/k210-shared/src/soc).
|
@ -0,0 +1,637 @@
|
||||
#![allow(unused)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
//! DMAC peripheral
|
||||
//use k210_hal::pac;
|
||||
use k210_pac as pac;
|
||||
use pac::dmac::channel::cfg::{TT_FC_A,HS_SEL_SRC_A};
|
||||
use pac::dmac::channel::ctl::{SMS_A};
|
||||
|
||||
use super::sysctl;
|
||||
|
||||
/** Extension trait for adding configure() to DMAC peripheral */
|
||||
pub trait DMACExt: Sized {
|
||||
/// Constrains DVP peripheral
|
||||
fn configure(self) -> DMAC;
|
||||
}
|
||||
|
||||
impl DMACExt for pac::DMAC {
|
||||
fn configure(self) -> DMAC { DMAC::new(self) }
|
||||
}
|
||||
|
||||
/** DMAC peripheral abstraction */
|
||||
pub struct DMAC {
|
||||
dmac: pac::DMAC,
|
||||
}
|
||||
|
||||
/*
|
||||
typedef struct _dmac_context
|
||||
{
|
||||
dmac_channel_number_t dmac_channel;
|
||||
plic_irq_callback_t callback;
|
||||
void *ctx;
|
||||
} dmac_context_t;
|
||||
|
||||
dmac_context_t dmac_context[6];
|
||||
*/
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum src_dst_select {
|
||||
SRC = 0x1,
|
||||
DST = 0x2,
|
||||
SRC_DST = 0x3,
|
||||
}
|
||||
|
||||
pub use super::sysctl::dma_channel;
|
||||
pub type master_number = pac::dmac::channel::ctl::SMS_A;
|
||||
pub type address_increment = pac::dmac::channel::ctl::SINC_A;
|
||||
pub type burst_length = pac::dmac::channel::ctl::SRC_MSIZE_A;
|
||||
pub type transfer_width = pac::dmac::channel::ctl::SRC_TR_WIDTH_A;
|
||||
|
||||
/** Return whether a specific address considered considered memory or peripheral */
|
||||
fn is_memory(address: u64) -> bool {
|
||||
let mem_len = 6 * 1024 * 1024;
|
||||
let mem_no_cache_len = 8 * 1024 * 1024;
|
||||
// Note: This comes from the Kendryte SDK as-is. No, I have no idea why the AES accelerator
|
||||
// input address 0x50450040 is considered memory, either.
|
||||
((address >= 0x80000000) && (address < 0x80000000 + mem_len))
|
||||
|| ((address >= 0x40000000) && (address < 0x40000000 + mem_no_cache_len))
|
||||
|| (address == 0x50450040)
|
||||
}
|
||||
|
||||
impl DMAC {
|
||||
fn new(dmac: pac::DMAC) -> Self {
|
||||
let rv = Self { dmac };
|
||||
rv.init();
|
||||
rv
|
||||
}
|
||||
|
||||
/** Get DMAC ID */
|
||||
pub fn read_id(&self) -> u64 {
|
||||
return self.dmac.id.read().bits();
|
||||
}
|
||||
|
||||
/** Get DMAC version */
|
||||
pub fn read_version(&self) -> u64 {
|
||||
return self.dmac.compver.read().bits();
|
||||
}
|
||||
|
||||
/** Get AXI ID for channel */
|
||||
pub fn read_channel_id(&self, channel_num: dma_channel) -> u64 {
|
||||
return self.dmac.channel[channel_num.idx()].axi_id.read().bits();
|
||||
}
|
||||
|
||||
/** Enable DMAC peripheral. */
|
||||
fn enable(&self) {
|
||||
self.dmac.cfg.modify(|_,w| w.dmac_en().set_bit()
|
||||
.int_en().set_bit());
|
||||
}
|
||||
|
||||
/** Disable DMAC peripheral. */
|
||||
pub fn disable(&self) {
|
||||
self.dmac.cfg.modify(|_,w| w.dmac_en().clear_bit()
|
||||
.int_en().clear_bit());
|
||||
}
|
||||
|
||||
pub fn src_transaction_complete_int_enable(&self, channel_num: dma_channel) {
|
||||
self.dmac.channel[channel_num.idx()].intstatus_en.modify(
|
||||
|_,w| w.src_transcomp().set_bit());
|
||||
}
|
||||
|
||||
/** Enable a DMA channel. */
|
||||
pub fn channel_enable(&self, channel_num: dma_channel) {
|
||||
use dma_channel::*;
|
||||
// Note: chX bit names start counting from 1, while channels start counting from 0
|
||||
match channel_num {
|
||||
CHANNEL0 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch1_en().set_bit()
|
||||
.ch1_en_we().set_bit());
|
||||
}
|
||||
CHANNEL1 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch2_en().set_bit()
|
||||
.ch2_en_we().set_bit());
|
||||
}
|
||||
CHANNEL2 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch3_en().set_bit()
|
||||
.ch3_en_we().set_bit());
|
||||
}
|
||||
CHANNEL3 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch4_en().set_bit()
|
||||
.ch4_en_we().set_bit());
|
||||
}
|
||||
CHANNEL4 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch5_en().set_bit()
|
||||
.ch5_en_we().set_bit());
|
||||
}
|
||||
CHANNEL5 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch6_en().set_bit()
|
||||
.ch6_en_we().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Disable a DMA channel. */
|
||||
pub fn channel_disable(&self, channel_num: dma_channel) {
|
||||
use dma_channel::*;
|
||||
// Note: chX bit names start counting from 1, while channels start counting from 0
|
||||
match channel_num {
|
||||
CHANNEL0 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch1_en().clear_bit()
|
||||
.ch1_en_we().set_bit());
|
||||
}
|
||||
CHANNEL1 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch2_en().clear_bit()
|
||||
.ch2_en_we().set_bit());
|
||||
}
|
||||
CHANNEL2 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch3_en().clear_bit()
|
||||
.ch3_en_we().set_bit());
|
||||
}
|
||||
CHANNEL3 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch4_en().clear_bit()
|
||||
.ch4_en_we().set_bit());
|
||||
}
|
||||
CHANNEL4 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch5_en().clear_bit()
|
||||
.ch5_en_we().set_bit());
|
||||
}
|
||||
CHANNEL5 => {
|
||||
self.dmac.chen.modify(|_,w| w.ch6_en().clear_bit()
|
||||
.ch6_en_we().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if a DMA channel is busy. */
|
||||
pub fn check_channel_busy(&self, channel_num: dma_channel) -> bool {
|
||||
use dma_channel::*;
|
||||
match channel_num {
|
||||
CHANNEL0 => self.dmac.chen.read().ch1_en().bit(),
|
||||
CHANNEL1 => self.dmac.chen.read().ch2_en().bit(),
|
||||
CHANNEL2 => self.dmac.chen.read().ch3_en().bit(),
|
||||
CHANNEL3 => self.dmac.chen.read().ch4_en().bit(),
|
||||
CHANNEL4 => self.dmac.chen.read().ch5_en().bit(),
|
||||
CHANNEL5 => self.dmac.chen.read().ch6_en().bit(),
|
||||
}
|
||||
// Note: Kendryte SDK writes back the value after reading it,
|
||||
// is this necessary? It seems not.
|
||||
}
|
||||
|
||||
pub fn set_list_master_select(&self, channel_num: dma_channel, sd_sel: src_dst_select, mst_num: master_number) -> Result<(),()> {
|
||||
if !self.check_channel_busy(channel_num) {
|
||||
use src_dst_select::*;
|
||||
self.dmac.channel[channel_num.idx()].ctl.modify(|_,w| {
|
||||
let w = if sd_sel == SRC || sd_sel == SRC_DST {
|
||||
w.sms().variant(mst_num)
|
||||
} else {
|
||||
w
|
||||
};
|
||||
if sd_sel == DST || sd_sel == SRC_DST {
|
||||
w.dms().variant(mst_num)
|
||||
} else {
|
||||
w
|
||||
}
|
||||
});
|
||||
// Note: there's some weird tmp|= line here in the Kendryte SDK
|
||||
// with the result going unused. I've decided to leave this out
|
||||
// because I assume it's another C UB workaround.
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_common_interrupt_status(&self) {
|
||||
self.dmac.com_intstatus_en.modify(|_,w|
|
||||
w.slvif_dec_err().set_bit()
|
||||
.slvif_wr2ro_err().set_bit()
|
||||
.slvif_rd2wo_err().set_bit()
|
||||
.slvif_wronhold_err().set_bit()
|
||||
.slvif_undefinedreg_dec_err().set_bit()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn enable_common_interrupt_signal(&self) {
|
||||
self.dmac.com_intsignal_en.modify(|_,w|
|
||||
w.slvif_dec_err().set_bit()
|
||||
.slvif_wr2ro_err().set_bit()
|
||||
.slvif_rd2wo_err().set_bit()
|
||||
.slvif_wronhold_err().set_bit()
|
||||
.slvif_undefinedreg_dec_err().set_bit()
|
||||
);
|
||||
}
|
||||
|
||||
fn enable_channel_interrupt(&self, channel_num: dma_channel) {
|
||||
unsafe {
|
||||
let ch = &self.dmac.channel[channel_num.idx()];
|
||||
ch.intclear.write(|w| w.bits(0xffffffff));
|
||||
ch.intstatus_en.write(|w| w.bits(0x2));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_channel_interrupt(&self, channel_num: dma_channel) {
|
||||
unsafe {
|
||||
self.dmac.channel[channel_num.idx()].intstatus_en.write(
|
||||
|w| w.bits(0x0));
|
||||
}
|
||||
}
|
||||
|
||||
fn channel_interrupt_clear(&self, channel_num: dma_channel) {
|
||||
unsafe {
|
||||
self.dmac.channel[channel_num.idx()].intclear.write(
|
||||
|w| w.bits(0xffffffff));
|
||||
}
|
||||
}
|
||||
|
||||
/** Set DMA channel parameters. */
|
||||
pub fn set_channel_param(&self, channel_num: dma_channel,
|
||||
src: u64, dest: u64, src_inc: address_increment, dest_inc: address_increment,
|
||||
burst_size: burst_length,
|
||||
trans_width: transfer_width,
|
||||
block_size: u32) {
|
||||
unsafe {
|
||||
let ch = &self.dmac.channel[channel_num.idx()];
|
||||
let src_is_mem = is_memory(src);
|
||||
let dest_is_mem = is_memory(dest);
|
||||
let flow_control = match (src_is_mem, dest_is_mem) {
|
||||
(false, false) => TT_FC_A::PRF2PRF_DMA,
|
||||
(true, false) => TT_FC_A::MEM2PRF_DMA,
|
||||
(false, true) => TT_FC_A::PRF2MEM_DMA,
|
||||
(true, true) => TT_FC_A::MEM2MEM_DMA,
|
||||
};
|
||||
|
||||
/*
|
||||
* cfg register must configure before ts_block and
|
||||
* sar dar register
|
||||
*/
|
||||
ch.cfg.modify(|_,w|
|
||||
w.tt_fc().variant(flow_control)
|
||||
.hs_sel_src().variant(if src_is_mem { HS_SEL_SRC_A::SOFTWARE } else { HS_SEL_SRC_A::HARDWARE } )
|
||||
.hs_sel_dst().variant(if dest_is_mem { HS_SEL_SRC_A::SOFTWARE } else { HS_SEL_SRC_A::HARDWARE } )
|
||||
// Note: from SVD: "Assign a hardware handshaking interface to source of channel",
|
||||
// these are set using sysctl::dma_select; this configuration seems to indicate
|
||||
// that in principle, it's possible to use a different source and destination
|
||||
// handshaking interface for a channel, but that would sacrifice the interface of
|
||||
// another channel.
|
||||
.src_per().bits(channel_num as u8)
|
||||
.dst_per().bits(channel_num as u8)
|
||||
.src_multblk_type().bits(0)
|
||||
.dst_multblk_type().bits(0)
|
||||
);
|
||||
|
||||
ch.sar.write(|w| w.bits(src));
|
||||
ch.dar.write(|w| w.bits(dest));
|
||||
|
||||
ch.ctl.modify(|_,w|
|
||||
w.sms().variant(SMS_A::AXI_MASTER_1)
|
||||
.dms().variant(SMS_A::AXI_MASTER_2)
|
||||
/* master select */
|
||||
.sinc().variant(src_inc)
|
||||
.dinc().variant(dest_inc)
|
||||
/* address incrememt */
|
||||
.src_tr_width().variant(trans_width)
|
||||
.dst_tr_width().variant(trans_width)
|
||||
/* transfer width */
|
||||
.src_msize().variant(burst_size)
|
||||
.dst_msize().variant(burst_size)
|
||||
);
|
||||
|
||||
ch.block_ts.write(|w| w.block_ts().bits(block_size - 1));
|
||||
/*the number of (blcok_ts +1) data of width SRC_TR_WIDTF to be */
|
||||
/* transferred in a dma block transfer */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub void set_address(&self, dmac_channel_number_t channel_num, uint64_t src_addr,
|
||||
uint64_t dst_addr) {
|
||||
writeq(src_addr, &dmac->channel[channel_num].sar);
|
||||
writeq(dst_addr, &dmac->channel[channel_num].dar);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_block_ts(&self, dmac_channel_number_t channel_num,
|
||||
uint32_t block_size) {
|
||||
uint32_t block_ts;
|
||||
|
||||
block_ts = block_size & 0x3fffff;
|
||||
writeq(block_ts, &dmac->channel[channel_num].block_ts);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void source_control(&self, dmac_channel_number_t channel_num,
|
||||
dmac_master_number_t master_select,
|
||||
dmac_address_increment_t address_mode,
|
||||
dmac_transfer_width_t tr_width,
|
||||
dmac_burst_length_t burst_length) {
|
||||
dmac_ch_ctl_u_t ctl_u;
|
||||
|
||||
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
|
||||
ctl_u.ch_ctl.sms = master_select;
|
||||
ctl_u.ch_ctl.sinc = address_mode;
|
||||
ctl_u.ch_ctl.src_tr_width = tr_width;
|
||||
ctl_u.ch_ctl.src_msize = burst_length;
|
||||
|
||||
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void master_control(&self, dmac_channel_number_t channel_num,
|
||||
dmac_master_number_t master_select,
|
||||
dmac_address_increment_t address_mode,
|
||||
dmac_transfer_width_t tr_width,
|
||||
dmac_burst_length_t burst_length) {
|
||||
dmac_ch_ctl_u_t ctl_u;
|
||||
|
||||
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
|
||||
ctl_u.ch_ctl.dms = master_select;
|
||||
ctl_u.ch_ctl.dinc = address_mode;
|
||||
ctl_u.ch_ctl.dst_tr_width = tr_width;
|
||||
ctl_u.ch_ctl.dst_msize = burst_length;
|
||||
|
||||
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_source_transfer_control(&self, dmac_channel_number_t channel_num,
|
||||
dmac_multiblk_transfer_type_t transfer_type,
|
||||
dmac_sw_hw_hs_select_t handshak_select) {
|
||||
dmac_ch_cfg_u_t cfg_u;
|
||||
|
||||
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
|
||||
cfg_u.ch_cfg.src_multblk_type = transfer_type;
|
||||
cfg_u.ch_cfg.hs_sel_src = handshak_select;
|
||||
|
||||
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_destination_transfer_control(&self, dmac_channel_number_t channel_num,
|
||||
dmac_multiblk_transfer_type_t transfer_type,
|
||||
dmac_sw_hw_hs_select_t handshak_select) {
|
||||
dmac_ch_cfg_u_t cfg_u;
|
||||
|
||||
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
|
||||
cfg_u.ch_cfg.dst_multblk_type = transfer_type;
|
||||
cfg_u.ch_cfg.hs_sel_dst = handshak_select;
|
||||
|
||||
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_flow_control(&self, dmac_channel_number_t channel_num,
|
||||
dmac_transfer_flow_t flow_control) {
|
||||
dmac_ch_cfg_u_t cfg_u;
|
||||
|
||||
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
|
||||
cfg_u.ch_cfg.tt_fc = flow_control;
|
||||
|
||||
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_linked_list_addr_point(&self, dmac_channel_number_t channel_num,
|
||||
uint64_t *addr) {
|
||||
dmac_ch_llp_u_t llp_u;
|
||||
|
||||
llp_u.data = readq(&dmac->channel[channel_num].llp);
|
||||
/* Cast pointer to uint64_t */
|
||||
llp_u.llp.loc = (uint64_t)addr;
|
||||
writeq(llp_u.data, &dmac->channel[channel_num].llp);
|
||||
}
|
||||
*/
|
||||
|
||||
/** Initialize DMA controller */
|
||||
pub fn init(&self) {
|
||||
sysctl::clock_enable(sysctl::clock::DMA);
|
||||
|
||||
/* reset dmac */
|
||||
self.dmac.reset.modify(|_,w| w.rst().set_bit());
|
||||
while self.dmac.reset.read().rst().bit() {
|
||||
// IDLE
|
||||
}
|
||||
|
||||
/* clear common register interrupt */
|
||||
self.dmac.com_intclear.modify(|_,w|
|
||||
w.slvif_dec_err().set_bit()
|
||||
.slvif_wr2ro_err().set_bit()
|
||||
.slvif_rd2wo_err().set_bit()
|
||||
.slvif_wronhold_err().set_bit()
|
||||
.slvif_undefinedreg_dec_err().set_bit()
|
||||
);
|
||||
|
||||
/* disable dmac and disable interrupt */
|
||||
self.dmac.cfg.modify(|_,w|
|
||||
w.dmac_en().clear_bit()
|
||||
.int_en().clear_bit()
|
||||
);
|
||||
|
||||
while self.dmac.cfg.read().bits() != 0 {
|
||||
// IDLE
|
||||
}
|
||||
/* disable all channel before configure */
|
||||
/* Note: changed from the SDK code, which doesn't clear channel 4 and 5,
|
||||
* and doesn't set associated _we bits */
|
||||
self.dmac.chen.modify(|_,w|
|
||||
w.ch1_en().clear_bit()
|
||||
.ch1_en_we().set_bit()
|
||||
.ch2_en().clear_bit()
|
||||
.ch2_en_we().set_bit()
|
||||
.ch3_en().clear_bit()
|
||||
.ch3_en_we().set_bit()
|
||||
.ch4_en().clear_bit()
|
||||
.ch4_en_we().set_bit()
|
||||
.ch5_en().clear_bit()
|
||||
.ch5_en_we().set_bit()
|
||||
);
|
||||
|
||||
self.enable();
|
||||
}
|
||||
|
||||
// TODO: list (scatter/gather) functionality
|
||||
|
||||
/*
|
||||
static void list_add(struct list_head_t *new, struct list_head_t *prev,
|
||||
struct list_head_t *next) {
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void list_add_tail(struct list_head_t *new, struct list_head_t *head) {
|
||||
list_add(new, head->prev, head);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void INIT_LIST_HEAD(struct list_head_t *list) {
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void link_list_item(dmac_channel_number_t channel_num,
|
||||
uint8_t LLI_row_num, int8_t LLI_last_row,
|
||||
dmac_lli_item_t *lli_item,
|
||||
dmac_channel_config_t *cfg_param) {
|
||||
dmac_ch_ctl_u_t ctl;
|
||||
dmac_ch_llp_u_t llp_u;
|
||||
|
||||
lli_item[LLI_row_num].sar = cfg_param->sar;
|
||||
lli_item[LLI_row_num].dar = cfg_param->dar;
|
||||
|
||||
ctl.data = readq(&dmac->channel[channel_num].ctl);
|
||||
ctl.ch_ctl.sms = cfg_param->ctl_sms;
|
||||
ctl.ch_ctl.dms = cfg_param->ctl_dms;
|
||||
ctl.ch_ctl.sinc = cfg_param->ctl_sinc;
|
||||
ctl.ch_ctl.dinc = cfg_param->ctl_dinc;
|
||||
ctl.ch_ctl.src_tr_width = cfg_param->ctl_src_tr_width;
|
||||
ctl.ch_ctl.dst_tr_width = cfg_param->ctl_dst_tr_width;
|
||||
ctl.ch_ctl.src_msize = cfg_param->ctl_src_msize;
|
||||
ctl.ch_ctl.dst_msize = cfg_param->ctl_drc_msize;
|
||||
ctl.ch_ctl.src_stat_en = cfg_param->ctl_src_stat_en;
|
||||
ctl.ch_ctl.dst_stat_en = cfg_param->ctl_dst_stat_en;
|
||||
|
||||
if(LLI_last_row != LAST_ROW)
|
||||
{
|
||||
ctl.ch_ctl.shadowreg_or_lli_valid = 1;
|
||||
ctl.ch_ctl.shadowreg_or_lli_last = 0;
|
||||
} else
|
||||
{
|
||||
ctl.ch_ctl.shadowreg_or_lli_valid = 1;
|
||||
ctl.ch_ctl.shadowreg_or_lli_last = 1;
|
||||
}
|
||||
|
||||
lli_item[LLI_row_num].ctl = ctl.data;
|
||||
|
||||
lli_item[LLI_row_num].ch_block_ts = cfg_param->ctl_block_ts;
|
||||
lli_item[LLI_row_num].sstat = 0;
|
||||
lli_item[LLI_row_num].dstat = 0;
|
||||
|
||||
llp_u.data = readq(&dmac->channel[channel_num].llp);
|
||||
|
||||
if(LLI_last_row != LAST_ROW)
|
||||
llp_u.llp.loc = ((uint64_t)&lli_item[LLI_row_num + 1]) >> 6;
|
||||
else
|
||||
llp_u.llp.loc = 0;
|
||||
|
||||
lli_item[LLI_row_num].llp = llp_u.data;
|
||||
}
|
||||
|
||||
pub void update_shadow_register(&self, dmac_channel_number_t channel_num,
|
||||
int8_t last_block, dmac_channel_config_t *cfg_param) {
|
||||
dmac_ch_ctl_u_t ctl_u;
|
||||
|
||||
do
|
||||
{
|
||||
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
|
||||
} while(ctl_u.ch_ctl.shadowreg_or_lli_valid);
|
||||
|
||||
writeq(cfg_param->sar, &dmac->channel[channel_num].sar);
|
||||
writeq(cfg_param->dar, &dmac->channel[channel_num].dar);
|
||||
writeq(cfg_param->ctl_block_ts, &dmac->channel[channel_num].block_ts);
|
||||
|
||||
ctl_u.ch_ctl.sms = cfg_param->ctl_sms;
|
||||
ctl_u.ch_ctl.dms = cfg_param->ctl_dms;
|
||||
ctl_u.ch_ctl.sinc = cfg_param->ctl_sinc;
|
||||
ctl_u.ch_ctl.dinc = cfg_param->ctl_dinc;
|
||||
ctl_u.ch_ctl.src_tr_width = cfg_param->ctl_src_tr_width;
|
||||
ctl_u.ch_ctl.dst_tr_width = cfg_param->ctl_dst_tr_width;
|
||||
ctl_u.ch_ctl.src_msize = cfg_param->ctl_src_msize;
|
||||
ctl_u.ch_ctl.dst_msize = cfg_param->ctl_drc_msize;
|
||||
ctl_u.ch_ctl.src_stat_en = cfg_param->ctl_src_stat_en;
|
||||
ctl_u.ch_ctl.dst_stat_en = cfg_param->ctl_dst_stat_en;
|
||||
if(last_block != LAST_ROW)
|
||||
{
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_last = 0;
|
||||
} else
|
||||
{
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_last = 1;
|
||||
}
|
||||
|
||||
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
|
||||
writeq(0, &dmac->channel[channel_num].blk_tfr);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub void set_shadow_invalid_flag(&self, dmac_channel_number_t channel_num) {
|
||||
dmac_ch_ctl_u_t ctl_u;
|
||||
|
||||
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
|
||||
ctl_u.ch_ctl.shadowreg_or_lli_last = 0;
|
||||
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
|
||||
}
|
||||
*/
|
||||
|
||||
/** Start a single DMA transfer. */
|
||||
pub fn set_single_mode(&self, channel_num: dma_channel,
|
||||
src: u64, dest: u64, src_inc: address_increment,
|
||||
dest_inc: address_increment,
|
||||
burst_size: burst_length,
|
||||
trans_width: transfer_width,
|
||||
block_size: u32) {
|
||||
self.channel_interrupt_clear(channel_num);
|
||||
self.channel_disable(channel_num);
|
||||
self.wait_idle(channel_num);
|
||||
self.set_channel_param(channel_num, src, dest, src_inc, dest_inc,
|
||||
burst_size, trans_width, block_size);
|
||||
self.enable();
|
||||
self.channel_enable(channel_num);
|
||||
}
|
||||
|
||||
/*
|
||||
pub int is_done(&self, dmac_channel_number_t channel_num) {
|
||||
if(readq(&dmac->channel[channel_num].intstatus) & 0x2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/** Wait for dmac work done. */
|
||||
pub fn wait_done(&self, channel_num: dma_channel) {
|
||||
self.wait_idle(channel_num);
|
||||
}
|
||||
|
||||
/** Determine if a DMA channel is idle or not. */
|
||||
pub fn is_idle(&self, channel_num: dma_channel) -> bool {
|
||||
!self.check_channel_busy(channel_num)
|
||||
}
|
||||
|
||||
/** Wait for a DMA channel to be idle. */
|
||||
pub fn wait_idle(&self, channel_num: dma_channel) {
|
||||
while !self.is_idle(channel_num) {
|
||||
}
|
||||
self.channel_interrupt_clear(channel_num); /* clear interrupt */
|
||||
}
|
||||
|
||||
/*
|
||||
pub void set_src_dest_length(&self, dmac_channel_number_t channel_num, const void *src, void *dest, size_t len) {
|
||||
if(src != NULL)
|
||||
dmac->channel[channel_num].sar = (uint64_t)src;
|
||||
if(dest != NULL)
|
||||
dmac->channel[channel_num].dar = (uint64_t)dest;
|
||||
if(len > 0)
|
||||
dmac_set_block_ts(channel_num, len - 1);
|
||||
dmac_channel_enable(channel_num);
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: completion IRQ functionality
|
||||
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
//! FPIOA peripheral
|
||||
|
||||
#![allow(unused)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
//use k210_hal::pac;
|
||||
use k210_pac as pac;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum function {
|
||||
JTAG_TCLK = 0, /* JTAG Test Clock */
|
||||
JTAG_TDI = 1, /* JTAG Test Data In */
|
||||
JTAG_TMS = 2, /* JTAG Test Mode Select */
|
||||
JTAG_TDO = 3, /* JTAG Test Data Out */
|
||||
SPI0_D0 = 4, /* SPI0 Data 0 */
|
||||
SPI0_D1 = 5, /* SPI0 Data 1 */
|
||||
SPI0_D2 = 6, /* SPI0 Data 2 */
|
||||
SPI0_D3 = 7, /* SPI0 Data 3 */
|
||||
SPI0_D4 = 8, /* SPI0 Data 4 */
|
||||
SPI0_D5 = 9, /* SPI0 Data 5 */
|
||||
SPI0_D6 = 10, /* SPI0 Data 6 */
|
||||
SPI0_D7 = 11, /* SPI0 Data 7 */
|
||||
SPI0_SS0 = 12, /* SPI0 Chip Select 0 */
|
||||
SPI0_SS1 = 13, /* SPI0 Chip Select 1 */
|
||||
SPI0_SS2 = 14, /* SPI0 Chip Select 2 */
|
||||
SPI0_SS3 = 15, /* SPI0 Chip Select 3 */
|
||||
SPI0_ARB = 16, /* SPI0 Arbitration */
|
||||
SPI0_SCLK = 17, /* SPI0 Serial Clock */
|
||||
UARTHS_RX = 18, /* UART High speed Receiver */
|
||||
UARTHS_TX = 19, /* UART High speed Transmitter */
|
||||
RESV6 = 20, /* Reserved function */
|
||||
RESV7 = 21, /* Reserved function */
|
||||
CLK_SPI1 = 22, /* Clock SPI1 */
|
||||
CLK_I2C1 = 23, /* Clock I2C1 */
|
||||
GPIOHS0 = 24, /* GPIO High speed 0 */
|
||||
GPIOHS1 = 25, /* GPIO High speed 1 */
|
||||
GPIOHS2 = 26, /* GPIO High speed 2 */
|
||||
GPIOHS3 = 27, /* GPIO High speed 3 */
|
||||
GPIOHS4 = 28, /* GPIO High speed 4 */
|
||||
GPIOHS5 = 29, /* GPIO High speed 5 */
|
||||
GPIOHS6 = 30, /* GPIO High speed 6 */
|
||||
GPIOHS7 = 31, /* GPIO High speed 7 */
|
||||
GPIOHS8 = 32, /* GPIO High speed 8 */
|
||||
GPIOHS9 = 33, /* GPIO High speed 9 */
|
||||
GPIOHS10 = 34, /* GPIO High speed 10 */
|
||||
GPIOHS11 = 35, /* GPIO High speed 11 */
|
||||
GPIOHS12 = 36, /* GPIO High speed 12 */
|
||||
GPIOHS13 = 37, /* GPIO High speed 13 */
|
||||
GPIOHS14 = 38, /* GPIO High speed 14 */
|
||||
GPIOHS15 = 39, /* GPIO High speed 15 */
|
||||
GPIOHS16 = 40, /* GPIO High speed 16 */
|
||||
GPIOHS17 = 41, /* GPIO High speed 17 */
|
||||
GPIOHS18 = 42, /* GPIO High speed 18 */
|
||||
GPIOHS19 = 43, /* GPIO High speed 19 */
|
||||
GPIOHS20 = 44, /* GPIO High speed 20 */
|
||||
GPIOHS21 = 45, /* GPIO High speed 21 */
|
||||
GPIOHS22 = 46, /* GPIO High speed 22 */
|
||||
GPIOHS23 = 47, /* GPIO High speed 23 */
|
||||
GPIOHS24 = 48, /* GPIO High speed 24 */
|
||||
GPIOHS25 = 49, /* GPIO High speed 25 */
|
||||
GPIOHS26 = 50, /* GPIO High speed 26 */
|
||||
GPIOHS27 = 51, /* GPIO High speed 27 */
|
||||
GPIOHS28 = 52, /* GPIO High speed 28 */
|
||||
GPIOHS29 = 53, /* GPIO High speed 29 */
|
||||
GPIOHS30 = 54, /* GPIO High speed 30 */
|
||||
GPIOHS31 = 55, /* GPIO High speed 31 */
|
||||
GPIO0 = 56, /* GPIO pin 0 */
|
||||
GPIO1 = 57, /* GPIO pin 1 */
|
||||
GPIO2 = 58, /* GPIO pin 2 */
|
||||
GPIO3 = 59, /* GPIO pin 3 */
|
||||
GPIO4 = 60, /* GPIO pin 4 */
|
||||
GPIO5 = 61, /* GPIO pin 5 */
|
||||
GPIO6 = 62, /* GPIO pin 6 */
|
||||
GPIO7 = 63, /* GPIO pin 7 */
|
||||
UART1_RX = 64, /* UART1 Receiver */
|
||||
UART1_TX = 65, /* UART1 Transmitter */
|
||||
UART2_RX = 66, /* UART2 Receiver */
|
||||
UART2_TX = 67, /* UART2 Transmitter */
|
||||
UART3_RX = 68, /* UART3 Receiver */
|
||||
UART3_TX = 69, /* UART3 Transmitter */
|
||||
SPI1_D0 = 70, /* SPI1 Data 0 */
|
||||
SPI1_D1 = 71, /* SPI1 Data 1 */
|
||||
SPI1_D2 = 72, /* SPI1 Data 2 */
|
||||
SPI1_D3 = 73, /* SPI1 Data 3 */
|
||||
SPI1_D4 = 74, /* SPI1 Data 4 */
|
||||
SPI1_D5 = 75, /* SPI1 Data 5 */
|
||||
SPI1_D6 = 76, /* SPI1 Data 6 */
|
||||
SPI1_D7 = 77, /* SPI1 Data 7 */
|
||||
SPI1_SS0 = 78, /* SPI1 Chip Select 0 */
|
||||
SPI1_SS1 = 79, /* SPI1 Chip Select 1 */
|
||||
SPI1_SS2 = 80, /* SPI1 Chip Select 2 */
|
||||
SPI1_SS3 = 81, /* SPI1 Chip Select 3 */
|
||||
SPI1_ARB = 82, /* SPI1 Arbitration */
|
||||
SPI1_SCLK = 83, /* SPI1 Serial Clock */
|
||||
SPI_SLAVE_D0 = 84, /* SPI Slave Data 0 */
|
||||
SPI_SLAVE_SS = 85, /* SPI Slave Select */
|
||||
SPI_SLAVE_SCLK = 86, /* SPI Slave Serial Clock */
|
||||
I2S0_MCLK = 87, /* I2S0 Master Clock */
|
||||
I2S0_SCLK = 88, /* I2S0 Serial Clock(BCLK) */
|
||||
I2S0_WS = 89, /* I2S0 Word Select(LRCLK) */
|
||||
I2S0_IN_D0 = 90, /* I2S0 Serial Data Input 0 */
|
||||
I2S0_IN_D1 = 91, /* I2S0 Serial Data Input 1 */
|
||||
I2S0_IN_D2 = 92, /* I2S0 Serial Data Input 2 */
|
||||
I2S0_IN_D3 = 93, /* I2S0 Serial Data Input 3 */
|
||||
I2S0_OUT_D0 = 94, /* I2S0 Serial Data Output 0 */
|
||||
I2S0_OUT_D1 = 95, /* I2S0 Serial Data Output 1 */
|
||||
I2S0_OUT_D2 = 96, /* I2S0 Serial Data Output 2 */
|
||||
I2S0_OUT_D3 = 97, /* I2S0 Serial Data Output 3 */
|
||||
I2S1_MCLK = 98, /* I2S1 Master Clock */
|
||||
I2S1_SCLK = 99, /* I2S1 Serial Clock(BCLK) */
|
||||
I2S1_WS = 100, /* I2S1 Word Select(LRCLK) */
|
||||
I2S1_IN_D0 = 101, /* I2S1 Serial Data Input 0 */
|
||||
I2S1_IN_D1 = 102, /* I2S1 Serial Data Input 1 */
|
||||
I2S1_IN_D2 = 103, /* I2S1 Serial Data Input 2 */
|
||||
I2S1_IN_D3 = 104, /* I2S1 Serial Data Input 3 */
|
||||
I2S1_OUT_D0 = 105, /* I2S1 Serial Data Output 0 */
|
||||
I2S1_OUT_D1 = 106, /* I2S1 Serial Data Output 1 */
|
||||
I2S1_OUT_D2 = 107, /* I2S1 Serial Data Output 2 */
|
||||
I2S1_OUT_D3 = 108, /* I2S1 Serial Data Output 3 */
|
||||
I2S2_MCLK = 109, /* I2S2 Master Clock */
|
||||
I2S2_SCLK = 110, /* I2S2 Serial Clock(BCLK) */
|
||||
I2S2_WS = 111, /* I2S2 Word Select(LRCLK) */
|
||||
I2S2_IN_D0 = 112, /* I2S2 Serial Data Input 0 */
|
||||
I2S2_IN_D1 = 113, /* I2S2 Serial Data Input 1 */
|
||||
I2S2_IN_D2 = 114, /* I2S2 Serial Data Input 2 */
|
||||
I2S2_IN_D3 = 115, /* I2S2 Serial Data Input 3 */
|
||||
I2S2_OUT_D0 = 116, /* I2S2 Serial Data Output 0 */
|
||||
I2S2_OUT_D1 = 117, /* I2S2 Serial Data Output 1 */
|
||||
I2S2_OUT_D2 = 118, /* I2S2 Serial Data Output 2 */
|
||||
I2S2_OUT_D3 = 119, /* I2S2 Serial Data Output 3 */
|
||||
RESV0 = 120, /* Reserved function */
|
||||
RESV1 = 121, /* Reserved function */
|
||||
RESV2 = 122, /* Reserved function */
|
||||
RESV3 = 123, /* Reserved function */
|
||||
RESV4 = 124, /* Reserved function */
|
||||
RESV5 = 125, /* Reserved function */
|
||||
I2C0_SCLK = 126, /* I2C0 Serial Clock */
|
||||
I2C0_SDA = 127, /* I2C0 Serial Data */
|
||||
I2C1_SCLK = 128, /* I2C1 Serial Clock */
|
||||
I2C1_SDA = 129, /* I2C1 Serial Data */
|
||||
I2C2_SCLK = 130, /* I2C2 Serial Clock */
|
||||
I2C2_SDA = 131, /* I2C2 Serial Data */
|
||||
CMOS_XCLK = 132, /* DVP System Clock */
|
||||
CMOS_RST = 133, /* DVP System Reset */
|
||||
CMOS_PWDN = 134, /* DVP Power Down Mode */
|
||||
CMOS_VSYNC = 135, /* DVP Vertical Sync */
|
||||
CMOS_HREF = 136, /* DVP Horizontal Reference output */
|
||||
CMOS_PCLK = 137, /* Pixel Clock */
|
||||
CMOS_D0 = 138, /* Data Bit 0 */
|
||||
CMOS_D1 = 139, /* Data Bit 1 */
|
||||
CMOS_D2 = 140, /* Data Bit 2 */
|
||||
CMOS_D3 = 141, /* Data Bit 3 */
|
||||
CMOS_D4 = 142, /* Data Bit 4 */
|
||||
CMOS_D5 = 143, /* Data Bit 5 */
|
||||
CMOS_D6 = 144, /* Data Bit 6 */
|
||||
CMOS_D7 = 145, /* Data Bit 7 */
|
||||
SCCB_SCLK = 146, /* SCCB Serial Clock */
|
||||
SCCB_SDA = 147, /* SCCB Serial Data */
|
||||
UART1_CTS = 148, /* UART1 Clear To Send */
|
||||
UART1_DSR = 149, /* UART1 Data Set Ready */
|
||||
UART1_DCD = 150, /* UART1 Data Carrier Detect */
|
||||
UART1_RI = 151, /* UART1 Ring Indicator */
|
||||
UART1_SIR_IN = 152, /* UART1 Serial Infrared Input */
|
||||
UART1_DTR = 153, /* UART1 Data Terminal Ready */
|
||||
UART1_RTS = 154, /* UART1 Request To Send */
|
||||
UART1_OUT2 = 155, /* UART1 User-designated Output 2 */
|
||||
UART1_OUT1 = 156, /* UART1 User-designated Output 1 */
|
||||
UART1_SIR_OUT = 157, /* UART1 Serial Infrared Output */
|
||||
UART1_BAUD = 158, /* UART1 Transmit Clock Output */
|
||||
UART1_RE = 159, /* UART1 Receiver Output Enable */
|
||||
UART1_DE = 160, /* UART1 Driver Output Enable */
|
||||
UART1_RS485_EN = 161, /* UART1 RS485 Enable */
|
||||
UART2_CTS = 162, /* UART2 Clear To Send */
|
||||
UART2_DSR = 163, /* UART2 Data Set Ready */
|
||||
UART2_DCD = 164, /* UART2 Data Carrier Detect */
|
||||
UART2_RI = 165, /* UART2 Ring Indicator */
|
||||
UART2_SIR_IN = 166, /* UART2 Serial Infrared Input */
|
||||
UART2_DTR = 167, /* UART2 Data Terminal Ready */
|
||||
UART2_RTS = 168, /* UART2 Request To Send */
|
||||
UART2_OUT2 = 169, /* UART2 User-designated Output 2 */
|
||||
UART2_OUT1 = 170, /* UART2 User-designated Output 1 */
|
||||
UART2_SIR_OUT = 171, /* UART2 Serial Infrared Output */
|
||||
UART2_BAUD = 172, /* UART2 Transmit Clock Output */
|
||||
UART2_RE = 173, /* UART2 Receiver Output Enable */
|
||||
UART2_DE = 174, /* UART2 Driver Output Enable */
|
||||
UART2_RS485_EN = 175, /* UART2 RS485 Enable */
|
||||
UART3_CTS = 176, /* UART3 Clear To Send */
|
||||
UART3_DSR = 177, /* UART3 Data Set Ready */
|
||||
UART3_DCD = 178, /* UART3 Data Carrier Detect */
|
||||
UART3_RI = 179, /* UART3 Ring Indicator */
|
||||
UART3_SIR_IN = 180, /* UART3 Serial Infrared Input */
|
||||
UART3_DTR = 181, /* UART3 Data Terminal Ready */
|
||||
UART3_RTS = 182, /* UART3 Request To Send */
|
||||
UART3_OUT2 = 183, /* UART3 User-designated Output 2 */
|
||||
UART3_OUT1 = 184, /* UART3 User-designated Output 1 */
|
||||
UART3_SIR_OUT = 185, /* UART3 Serial Infrared Output */
|
||||
UART3_BAUD = 186, /* UART3 Transmit Clock Output */
|
||||
UART3_RE = 187, /* UART3 Receiver Output Enable */
|
||||
UART3_DE = 188, /* UART3 Driver Output Enable */
|
||||
UART3_RS485_EN = 189, /* UART3 RS485 Enable */
|
||||
TIMER0_TOGGLE1 = 190, /* TIMER0 Toggle Output 1 */
|
||||
TIMER0_TOGGLE2 = 191, /* TIMER0 Toggle Output 2 */
|
||||
TIMER0_TOGGLE3 = 192, /* TIMER0 Toggle Output 3 */
|
||||
TIMER0_TOGGLE4 = 193, /* TIMER0 Toggle Output 4 */
|
||||
TIMER1_TOGGLE1 = 194, /* TIMER1 Toggle Output 1 */
|
||||
TIMER1_TOGGLE2 = 195, /* TIMER1 Toggle Output 2 */
|
||||
TIMER1_TOGGLE3 = 196, /* TIMER1 Toggle Output 3 */
|
||||
TIMER1_TOGGLE4 = 197, /* TIMER1 Toggle Output 4 */
|
||||
TIMER2_TOGGLE1 = 198, /* TIMER2 Toggle Output 1 */
|
||||
TIMER2_TOGGLE2 = 199, /* TIMER2 Toggle Output 2 */
|
||||
TIMER2_TOGGLE3 = 200, /* TIMER2 Toggle Output 3 */
|
||||
TIMER2_TOGGLE4 = 201, /* TIMER2 Toggle Output 4 */
|
||||
CLK_SPI2 = 202, /* Clock SPI2 */
|
||||
CLK_I2C2 = 203, /* Clock I2C2 */
|
||||
INTERNAL0 = 204, /* Internal function signal 0 */
|
||||
INTERNAL1 = 205, /* Internal function signal 1 */
|
||||
INTERNAL2 = 206, /* Internal function signal 2 */
|
||||
INTERNAL3 = 207, /* Internal function signal 3 */
|
||||
INTERNAL4 = 208, /* Internal function signal 4 */
|
||||
INTERNAL5 = 209, /* Internal function signal 5 */
|
||||
INTERNAL6 = 210, /* Internal function signal 6 */
|
||||
INTERNAL7 = 211, /* Internal function signal 7 */
|
||||
INTERNAL8 = 212, /* Internal function signal 8 */
|
||||
INTERNAL9 = 213, /* Internal function signal 9 */
|
||||
INTERNAL10 = 214, /* Internal function signal 10 */
|
||||
INTERNAL11 = 215, /* Internal function signal 11 */
|
||||
INTERNAL12 = 216, /* Internal function signal 12 */
|
||||
INTERNAL13 = 217, /* Internal function signal 13 */
|
||||
INTERNAL14 = 218, /* Internal function signal 14 */
|
||||
INTERNAL15 = 219, /* Internal function signal 15 */
|
||||
INTERNAL16 = 220, /* Internal function signal 16 */
|
||||
INTERNAL17 = 221, /* Internal function signal 17 */
|
||||
CONSTANT = 222, /* Constant function */
|
||||
INTERNAL18 = 223, /* Internal function signal 18 */
|
||||
DEBUG0 = 224, /* Debug function 0 */
|
||||
DEBUG1 = 225, /* Debug function 1 */
|
||||
DEBUG2 = 226, /* Debug function 2 */
|
||||
DEBUG3 = 227, /* Debug function 3 */
|
||||
DEBUG4 = 228, /* Debug function 4 */
|
||||
DEBUG5 = 229, /* Debug function 5 */
|
||||
DEBUG6 = 230, /* Debug function 6 */
|
||||
DEBUG7 = 231, /* Debug function 7 */
|
||||
DEBUG8 = 232, /* Debug function 8 */
|
||||
DEBUG9 = 233, /* Debug function 9 */
|
||||
DEBUG10 = 234, /* Debug function 10 */
|
||||
DEBUG11 = 235, /* Debug function 11 */
|
||||
DEBUG12 = 236, /* Debug function 12 */
|
||||
DEBUG13 = 237, /* Debug function 13 */
|
||||
DEBUG14 = 238, /* Debug function 14 */
|
||||
DEBUG15 = 239, /* Debug function 15 */
|
||||
DEBUG16 = 240, /* Debug function 16 */
|
||||
DEBUG17 = 241, /* Debug function 17 */
|
||||
DEBUG18 = 242, /* Debug function 18 */
|
||||
DEBUG19 = 243, /* Debug function 19 */
|
||||
DEBUG20 = 244, /* Debug function 20 */
|
||||
DEBUG21 = 245, /* Debug function 21 */
|
||||
DEBUG22 = 246, /* Debug function 22 */
|
||||
DEBUG23 = 247, /* Debug function 23 */
|
||||
DEBUG24 = 248, /* Debug function 24 */
|
||||
DEBUG25 = 249, /* Debug function 25 */
|
||||
DEBUG26 = 250, /* Debug function 26 */
|
||||
DEBUG27 = 251, /* Debug function 27 */
|
||||
DEBUG28 = 252, /* Debug function 28 */
|
||||
DEBUG29 = 253, /* Debug function 29 */
|
||||
DEBUG30 = 254, /* Debug function 30 */
|
||||
DEBUG31 = 255, /* Debug function 31 */
|
||||
}
|
||||
|
||||
impl function {
|
||||
/** GPIOHS pin to function */
|
||||
pub fn gpiohs(num: u8) -> function {
|
||||
use function::*;
|
||||
match num {
|
||||
0 => GPIOHS0,
|
||||
1 => GPIOHS1,
|
||||
2 => GPIOHS2,
|
||||
3 => GPIOHS3,
|
||||
4 => GPIOHS4,
|
||||
5 => GPIOHS5,
|
||||
6 => GPIOHS6,
|
||||
7 => GPIOHS7,
|
||||
8 => GPIOHS8,
|
||||
9 => GPIOHS9,
|
||||
10 => GPIOHS10,
|
||||
11 => GPIOHS11,
|
||||
12 => GPIOHS12,
|
||||
13 => GPIOHS13,
|
||||
14 => GPIOHS14,
|
||||
15 => GPIOHS15,
|
||||
16 => GPIOHS16,
|
||||
17 => GPIOHS17,
|
||||
18 => GPIOHS18,
|
||||
19 => GPIOHS19,
|
||||
20 => GPIOHS20,
|
||||
21 => GPIOHS21,
|
||||
22 => GPIOHS22,
|
||||
23 => GPIOHS23,
|
||||
24 => GPIOHS24,
|
||||
25 => GPIOHS25,
|
||||
26 => GPIOHS26,
|
||||
27 => GPIOHS27,
|
||||
28 => GPIOHS28,
|
||||
29 => GPIOHS29,
|
||||
30 => GPIOHS30,
|
||||
31 => GPIOHS31,
|
||||
_ => panic!("no such GPIO pin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum pull {
|
||||
/** No Pull */
|
||||
NONE,
|
||||
/** Pull Down */
|
||||
DOWN,
|
||||
/** Pull Up */
|
||||
UP,
|
||||
}
|
||||
|
||||
/** Defaults per function (from Kendryte fpioa.c) */
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
static FUNCTION_DEFAULTS: &[u32] = &[
|
||||
0x00900000, 0x00900001, 0x00900002, 0x00001f03, 0x00b03f04, 0x00b03f05, 0x00b03f06, 0x00b03f07,
|
||||
0x00b03f08, 0x00b03f09, 0x00b03f0a, 0x00b03f0b, 0x00001f0c, 0x00001f0d, 0x00001f0e, 0x00001f0f,
|
||||
0x03900010, 0x00001f11, 0x00900012, 0x00001f13, 0x00900014, 0x00900015, 0x00001f16, 0x00001f17,
|
||||
0x00901f18, 0x00901f19, 0x00901f1a, 0x00901f1b, 0x00901f1c, 0x00901f1d, 0x00901f1e, 0x00901f1f,
|
||||
0x00901f20, 0x00901f21, 0x00901f22, 0x00901f23, 0x00901f24, 0x00901f25, 0x00901f26, 0x00901f27,
|
||||
0x00901f28, 0x00901f29, 0x00901f2a, 0x00901f2b, 0x00901f2c, 0x00901f2d, 0x00901f2e, 0x00901f2f,
|
||||
0x00901f30, 0x00901f31, 0x00901f32, 0x00901f33, 0x00901f34, 0x00901f35, 0x00901f36, 0x00901f37,
|
||||
0x00901f38, 0x00901f39, 0x00901f3a, 0x00901f3b, 0x00901f3c, 0x00901f3d, 0x00901f3e, 0x00901f3f,
|
||||
0x00900040, 0x00001f41, 0x00900042, 0x00001f43, 0x00900044, 0x00001f45, 0x00b03f46, 0x00b03f47,
|
||||
0x00b03f48, 0x00b03f49, 0x00b03f4a, 0x00b03f4b, 0x00b03f4c, 0x00b03f4d, 0x00001f4e, 0x00001f4f,
|
||||
0x00001f50, 0x00001f51, 0x03900052, 0x00001f53, 0x00b03f54, 0x00900055, 0x00900056, 0x00001f57,
|
||||
0x00001f58, 0x00001f59, 0x0090005a, 0x0090005b, 0x0090005c, 0x0090005d, 0x00001f5e, 0x00001f5f,
|
||||
0x00001f60, 0x00001f61, 0x00001f62, 0x00001f63, 0x00001f64, 0x00900065, 0x00900066, 0x00900067,
|
||||
0x00900068, 0x00001f69, 0x00001f6a, 0x00001f6b, 0x00001f6c, 0x00001f6d, 0x00001f6e, 0x00001f6f,
|
||||
0x00900070, 0x00900071, 0x00900072, 0x00900073, 0x00001f74, 0x00001f75, 0x00001f76, 0x00001f77,
|
||||
0x00000078, 0x00000079, 0x0000007a, 0x0000007b, 0x0000007c, 0x0000007d, 0x0099107e, 0x0099107f,
|
||||
0x00991080, 0x00991081, 0x00991082, 0x00991083, 0x00001f84, 0x00001f85, 0x00001f86, 0x00900087,
|
||||
0x00900088, 0x00900089, 0x0090008a, 0x0090008b, 0x0090008c, 0x0090008d, 0x0090008e, 0x0090008f,
|
||||
0x00900090, 0x00900091, 0x00993092, 0x00993093, 0x00900094, 0x00900095, 0x00900096, 0x00900097,
|
||||
0x00900098, 0x00001f99, 0x00001f9a, 0x00001f9b, 0x00001f9c, 0x00001f9d, 0x00001f9e, 0x00001f9f,
|
||||
0x00001fa0, 0x00001fa1, 0x009000a2, 0x009000a3, 0x009000a4, 0x009000a5, 0x009000a6, 0x00001fa7,
|
||||
0x00001fa8, 0x00001fa9, 0x00001faa, 0x00001fab, 0x00001fac, 0x00001fad, 0x00001fae, 0x00001faf,
|
||||
0x009000b0, 0x009000b1, 0x009000b2, 0x009000b3, 0x009000b4, 0x00001fb5, 0x00001fb6, 0x00001fb7,
|
||||
0x00001fb8, 0x00001fb9, 0x00001fba, 0x00001fbb, 0x00001fbc, 0x00001fbd, 0x00001fbe, 0x00001fbf,
|
||||
0x00001fc0, 0x00001fc1, 0x00001fc2, 0x00001fc3, 0x00001fc4, 0x00001fc5, 0x00001fc6, 0x00001fc7,
|
||||
0x00001fc8, 0x00001fc9, 0x00001fca, 0x00001fcb, 0x00001fcc, 0x00001fcd, 0x00001fce, 0x00001fcf,
|
||||
0x00001fd0, 0x00001fd1, 0x00001fd2, 0x00001fd3, 0x00001fd4, 0x009000d5, 0x009000d6, 0x009000d7,
|
||||
0x009000d8, 0x009100d9, 0x00991fda, 0x009000db, 0x009000dc, 0x009000dd, 0x000000de, 0x009000df,
|
||||
0x00001fe0, 0x00001fe1, 0x00001fe2, 0x00001fe3, 0x00001fe4, 0x00001fe5, 0x00001fe6, 0x00001fe7,
|
||||
0x00001fe8, 0x00001fe9, 0x00001fea, 0x00001feb, 0x00001fec, 0x00001fed, 0x00001fee, 0x00001fef,
|
||||
0x00001ff0, 0x00001ff1, 0x00001ff2, 0x00001ff3, 0x00001ff4, 0x00001ff5, 0x00001ff6, 0x00001ff7,
|
||||
0x00001ff8, 0x00001ff9, 0x00001ffa, 0x00001ffb, 0x00001ffc, 0x00001ffd, 0x00001ffe, 0x00001fff,
|
||||
];
|
||||
|
||||
pub fn set_function<N: Into<usize>>(number: N, function: function) {
|
||||
// TODO: check for overlapping assignments and assign to RESV0 as the Kendryte SDK does?
|
||||
unsafe {
|
||||
let ptr = pac::FPIOA::ptr();
|
||||
(*ptr).io[number.into()].write(|w| w.bits(FUNCTION_DEFAULTS[function as usize]));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_io_pull<N: Into<usize>>(number: N, pull: pull) {
|
||||
unsafe {
|
||||
(*pac::FPIOA::ptr()).io[number.into()].modify(|_, w| match pull {
|
||||
pull::NONE => w.pu().bit(false).pd().bit(false),
|
||||
pull::DOWN => w.pu().bit(false).pd().bit(true),
|
||||
pull::UP => w.pu().bit(true).pd().bit(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** I/O pins for FPIOA */
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum io {
|
||||
/** JTAG TCK */
|
||||
JTAG_TCK = 0,
|
||||
/** JTAG TDI */
|
||||
JTAG_TDI = 1,
|
||||
/** JTAG TMS */
|
||||
JTAG_TMS = 2,
|
||||
/** JTAG TDO */
|
||||
JTAG_TDO = 3,
|
||||
/** Host RX (from STM32F103C8) */
|
||||
ISP_RX = 4,
|
||||
/** Host TX (to STM32F103C8) */
|
||||
ISP_TX = 5,
|
||||
/** WIFI serial TX (from perspective of ESP8285, so our RX) */
|
||||
WIFI_TX = 6,
|
||||
/** WIFI serial RX (from perspective of ESP8285, so our TX) */
|
||||
WIFI_RX = 7,
|
||||
/** WIFI enable (to ESP8285) */
|
||||
WIFI_EN = 8,
|
||||
/** Unused */
|
||||
BPSK_P = 9,
|
||||
/** Unused */
|
||||
BPSK_N = 10,
|
||||
/** General purpose I/O pin */
|
||||
IO11 = 11,
|
||||
/** Blue led (output) */
|
||||
LED_B = 12,
|
||||
/** Green led (output) */
|
||||
LED_G = 13,
|
||||
/** Red led (output) */
|
||||
LED_R = 14,
|
||||
/** Key direction 1 press (input) */
|
||||
KEY1 = 15,
|
||||
/** Key center press (input) */
|
||||
BOOT_KEY0 = 16,
|
||||
/** Key direction 2 press (input) */
|
||||
KEY2 = 17,
|
||||
/** Microphone I2S BCK */
|
||||
MIC_BCK = 18,
|
||||
/** Microphone I2S WS */
|
||||
MIC_WS = 19,
|
||||
/** Microphone I2S DAT3 */
|
||||
MIC_DAT3 = 20,
|
||||
/** Microphone I2S DAT2 */
|
||||
MIC_DAT2 = 21,
|
||||
/** Microphone I2S DAT1 */
|
||||
MIC_DAT1 = 22,
|
||||
/** Microphone I2S DAT0 */
|
||||
MIC_DAT0 = 23,
|
||||
/** Microphone LED DAT */
|
||||
MIC_LED_DAT = 24,
|
||||
/** Microphone LED CLK */
|
||||
MIC_LED_CLK = 25,
|
||||
/** SDCARD SPI MISO */
|
||||
SPI0_MISO = 26,
|
||||
/** SDCARD SPI SCLK */
|
||||
SPI0_SCLK = 27,
|
||||
/** SDCARD SPI MOSI */
|
||||
SPI0_MOSI = 28,
|
||||
/** SDCARD SPI CS */
|
||||
SPI0_CS0 = 29,
|
||||
/** I2C bus 1 SCLK (NS2009, MSA300) */
|
||||
I2C1_SCL = 30,
|
||||
/** I2C bus 2 SDA (NS2009, MSA300) */
|
||||
I2C1_SDA = 31,
|
||||
/** General purpose I/O pin */
|
||||
IO32 = 32,
|
||||
/** DAC I2S WS */
|
||||
I2S_WS = 33,
|
||||
/** DAC I2S DA */
|
||||
I2S_DA = 34,
|
||||
/** DAC I2S BCK */
|
||||
I2S_BCK = 35,
|
||||
/** LCD chip select (output) */
|
||||
LCD_CS = 36,
|
||||
/** LCD reset (output) */
|
||||
LCD_RST = 37,
|
||||
/** LCD Data/Command */
|
||||
LCD_DC = 38,
|
||||
/** LCD SPI SCLK */
|
||||
LCD_WR = 39,
|
||||
/** Camera DVP SDA */
|
||||
DVP_SDA = 40,
|
||||
/** Camera DVP SCL */
|
||||
DVP_SCL = 41,
|
||||
/** Camera DVP RST */
|
||||
DVP_RST = 42,
|
||||
/** Camera DVP VSYNC */
|
||||
DVP_VSYNC = 43,
|
||||
/** Camera DVP PWDN */
|
||||
DVP_PWDN = 44,
|
||||
/** Camera DVP HSYNC */
|
||||
DVP_HSYNC = 45,
|
||||
/** Camera DVP XCLK */
|
||||
DVP_XCLK = 46,
|
||||
/** Camera DVP PCLK */
|
||||
DVP_PCLK = 47,
|
||||
}
|
||||
|
||||
impl From<io> for usize {
|
||||
fn from(io: io) -> Self {
|
||||
io as usize
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
//! GPIO peripheral
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum direction {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
}
|
||||
|
||||
// TODO
|
@ -0,0 +1,41 @@
|
||||
#![allow(unused)]
|
||||
|
||||
//! GPIOHS peripheral
|
||||
//use k210_hal::pac;
|
||||
use k210_pac as pac;
|
||||
|
||||
use super::gpio;
|
||||
use super::utils::{set_bit,get_bit};
|
||||
|
||||
// TODO embedded-hal::digital::v2::{InputPin, OutputPin}
|
||||
|
||||
/** Set input/output direction for a GPIOHS pin */
|
||||
pub fn set_direction(pin: u8, direction: gpio::direction) {
|
||||
unsafe {
|
||||
let ptr = pac::GPIOHS::ptr();
|
||||
(*ptr)
|
||||
.output_en
|
||||
.modify(|r, w| w.bits(set_bit(r.bits(), pin, direction == gpio::direction::OUTPUT)));
|
||||
(*ptr)
|
||||
.input_en
|
||||
.modify(|r, w| w.bits(set_bit(r.bits(), pin, direction == gpio::direction::INPUT)));
|
||||
}
|
||||
}
|
||||
|
||||
/** Set output value for a GPIOHS pin */
|
||||
pub fn set_pin(pin: u8, value: bool) {
|
||||
unsafe {
|
||||
let ptr = pac::GPIOHS::ptr();
|
||||
(*ptr)
|
||||
.output_val
|
||||
.modify(|r, w| w.bits(set_bit(r.bits(), pin, value)));
|
||||
}
|
||||
}
|
||||
|
||||
/** Get input value for a GPIOHS pin */
|
||||
pub fn get_pin(pin: u8) -> bool {
|
||||
unsafe {
|
||||
let ptr = pac::GPIOHS::ptr();
|
||||
get_bit((*ptr).input_val.read().bits(), pin)
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod dmac;
|
||||
pub mod fpioa;
|
||||
pub mod gpio;
|
||||
pub mod gpiohs;
|
||||
pub mod sleep;
|
||||
pub mod spi;
|
||||
pub mod sysctl;
|
||||
pub mod utils;
|
@ -0,0 +1,12 @@
|
||||
use super::sysctl;
|
||||
use riscv::register::time;
|
||||
|
||||
pub fn time_sleep(n: usize) {
|
||||
let start = time::read();
|
||||
while time::read() < start + n {}
|
||||
}
|
||||
|
||||
pub fn usleep(n: usize) {
|
||||
let freq = sysctl::clock_get_freq(sysctl::clock::CPU) as usize / 62;
|
||||
time_sleep(freq * n / 1000000);
|
||||
}
|
@ -0,0 +1,316 @@
|
||||
//! SPI peripherals handling
|
||||
use core::cmp;
|
||||
use core::convert::TryInto;
|
||||
use core::ops::Deref;
|
||||
//use k210_hal::pac;
|
||||
use k210_pac as pac;
|
||||
use pac::{SPI0,SPI1,spi0};
|
||||
use pac::spi0::ctrlr0;
|
||||
use pac::spi0::spi_ctrlr0;
|
||||
|
||||
//use super::sysctl;
|
||||
use super::sysctl::{dma_channel, self};
|
||||
use super::dmac::{DMAC, address_increment, burst_length, transfer_width};
|
||||
|
||||
/// Extension trait that constrains SPI peripherals
|
||||
pub trait SPIExt: Sized {
|
||||
/// Constrains SPI peripheral so it plays nicely with the other abstractions
|
||||
fn constrain(self) -> SPIImpl<Self>;
|
||||
}
|
||||
|
||||
/// Trait for generalizing over SPI0 and SPI1 (SPI2 is slave-only and SPI3 is !!!special!!!)
|
||||
pub trait SPI01: Deref<Target = spi0::RegisterBlock> {
|
||||
#[doc(hidden)]
|
||||
const CLK: sysctl::clock;
|
||||
#[doc(hidden)]
|
||||
const DIV: sysctl::threshold;
|
||||
#[doc(hidden)]
|
||||
const DMA_RX: sysctl::dma_select;
|
||||
#[doc(hidden)]
|
||||
const DMA_TX: sysctl::dma_select;
|
||||
}
|
||||
|
||||
impl SPI01 for SPI0 {
|
||||
const CLK: sysctl::clock = sysctl::clock::SPI0;
|
||||
const DIV: sysctl::threshold = sysctl::threshold::SPI0;
|
||||
const DMA_RX: sysctl::dma_select = sysctl::dma_select::SSI0_RX_REQ;
|
||||
const DMA_TX: sysctl::dma_select = sysctl::dma_select::SSI0_TX_REQ;
|
||||
}
|
||||
impl SPI01 for SPI1 {
|
||||
const CLK: sysctl::clock = sysctl::clock::SPI1;
|
||||
const DIV: sysctl::threshold = sysctl::threshold::SPI1;
|
||||
const DMA_RX: sysctl::dma_select = sysctl::dma_select::SSI1_RX_REQ;
|
||||
const DMA_TX: sysctl::dma_select = sysctl::dma_select::SSI1_TX_REQ;
|
||||
}
|
||||
|
||||
impl<SPI: SPI01> SPIExt for SPI {
|
||||
fn constrain(self) -> SPIImpl<SPI> {
|
||||
SPIImpl::<SPI>::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SPIImpl<IF> {
|
||||
spi: IF,
|
||||
}
|
||||
|
||||
/** Borrow work mode from pac */
|
||||
pub use ctrlr0::WORK_MODE_A as work_mode;
|
||||
/** Borrow frame format from pac */
|
||||
pub use ctrlr0::FRAME_FORMAT_A as frame_format;
|
||||
/** Borrow aitm from pac */
|
||||
pub use spi_ctrlr0::AITM_A as aitm;
|
||||
/** Borrow tmod from pac */
|
||||
pub use ctrlr0::TMOD_A as tmod;
|
||||
|
||||
pub trait SPI {
|
||||
fn configure(
|
||||
&self,
|
||||
work_mode: work_mode,
|
||||
frame_format: frame_format,
|
||||
data_bit_length: u8,
|
||||
endian: u32,
|
||||
instruction_length: u8,
|
||||
address_length: u8,
|
||||
wait_cycles: u8,
|
||||
instruction_address_trans_mode: aitm,
|
||||
tmod: tmod,
|
||||
);
|
||||
fn set_clk_rate(&self, spi_clk: u32) -> u32;
|
||||
fn recv_data<X: TruncU32>(&self, chip_select: u32, rx: &mut [X]);
|
||||
fn recv_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, rx: &mut [u32]);
|
||||
fn send_data<X: Into<u32> + Copy>(&self, chip_select: u32, tx: &[X]);
|
||||
fn send_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, tx: &[u32]);
|
||||
fn fill_data(&self, chip_select: u32, value: u32, tx_len: usize);
|
||||
fn fill_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, value: u32, tx_len: usize);
|
||||
}
|
||||
|
||||
impl<IF: SPI01> SPIImpl<IF> {
|
||||
pub fn new(spi: IF) -> Self {
|
||||
Self { spi }
|
||||
}
|
||||
}
|
||||
|
||||
/** Trait for trunction of a SPI frame from u32 register to other unsigned integer types. */
|
||||
pub trait TruncU32 {
|
||||
fn trunc(val: u32)-> Self;
|
||||
}
|
||||
impl TruncU32 for u32 { fn trunc(val: u32) -> u32 { return val; } }
|
||||
impl TruncU32 for u16 { fn trunc(val: u32) -> u16 { return (val & 0xffff) as u16; } }
|
||||
impl TruncU32 for u8 { fn trunc(val: u32) -> u8 { return (val & 0xff) as u8; } }
|
||||
|
||||
impl<IF: SPI01> SPI for SPIImpl<IF> {
|
||||
/// Configure SPI transaction
|
||||
fn configure(
|
||||
&self,
|
||||
work_mode: work_mode,
|
||||
frame_format: frame_format,
|
||||
data_bit_length: u8,
|
||||
endian: u32,
|
||||
instruction_length: u8,
|
||||
address_length: u8,
|
||||
wait_cycles: u8,
|
||||
instruction_address_trans_mode: aitm,
|
||||
tmod: tmod,
|
||||
) {
|
||||
assert!(data_bit_length >= 4 && data_bit_length <= 32);
|
||||
assert!(wait_cycles < (1 << 5));
|
||||
let inst_l: u8 = match instruction_length {
|
||||
0 => 0,
|
||||
4 => 1,
|
||||
8 => 2,
|
||||
16 => 3,
|
||||
_ => panic!("unhandled intruction length"),
|
||||
};
|
||||
|
||||
assert!(address_length % 4 == 0 && address_length <= 60);
|
||||
let addr_l: u8 = address_length / 4;
|
||||
|
||||
unsafe {
|
||||
self.spi.imr.write(|w| w.bits(0x00));
|
||||
self.spi.dmacr.write(|w| w.bits(0x00));
|
||||
self.spi.dmatdlr.write(|w| w.bits(0x10));
|
||||
self.spi.dmardlr.write(|w| w.bits(0x00));
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
self.spi.ctrlr0.write(|w| {
|
||||
w.work_mode()
|
||||
.variant(work_mode)
|
||||
.tmod()
|
||||
.variant(tmod)
|
||||
.frame_format()
|
||||
.variant(frame_format)
|
||||
.data_length()
|
||||
.bits(data_bit_length - 1)
|
||||
});
|
||||
self.spi.spi_ctrlr0.write(|w| {
|
||||
w.aitm()
|
||||
.variant(instruction_address_trans_mode)
|
||||
.addr_length()
|
||||
.bits(addr_l)
|
||||
.inst_length()
|
||||
.bits(inst_l)
|
||||
.wait_cycles()
|
||||
.bits(wait_cycles)
|
||||
});
|
||||
self.spi.endian.write(|w| w.bits(endian));
|
||||
}
|
||||
}
|
||||
|
||||
/// Set SPI clock rate
|
||||
fn set_clk_rate(&self, spi_clk: u32) -> u32 {
|
||||
sysctl::clock_enable(IF::CLK);
|
||||
sysctl::clock_set_threshold(IF::DIV, 0);
|
||||
let clock_freq: u32 = sysctl::clock_get_freq(sysctl::clock::SPI0);
|
||||
let spi_baudr = clock_freq / spi_clk;
|
||||
// Clamp baudrate divider to valid range
|
||||
let spi_baudr = cmp::min(cmp::max(spi_baudr, 2), 65534);
|
||||
unsafe {
|
||||
self.spi.baudr.write(|w| w.bits(spi_baudr));
|
||||
}
|
||||
clock_freq / spi_baudr
|
||||
}
|
||||
|
||||
/// Receive arbitrary data
|
||||
// make sure to set tmod to tmod::RECV
|
||||
fn recv_data<X: TruncU32>(&self, chip_select: u32, rx: &mut [X]) {
|
||||
if rx.len() == 0 {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
self.spi.ctrlr1.write(|w| w.bits((rx.len() - 1).try_into().unwrap()));
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
self.spi.dr[0].write(|w| w.bits(0xffffffff));
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
|
||||
let mut fifo_len = 0;
|
||||
for val in rx.iter_mut() {
|
||||
while fifo_len == 0 {
|
||||
fifo_len = self.spi.rxflr.read().bits();
|
||||
}
|
||||
*val = X::trunc(self.spi.dr[0].read().bits());
|
||||
fifo_len -= 1;
|
||||
}
|
||||
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive 32-bit data using DMA.
|
||||
// make sure to set tmod to tmod::RECV
|
||||
fn recv_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, rx: &mut [u32]) {
|
||||
if rx.len() == 0 {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
self.spi.ctrlr1.write(|w| w.bits((rx.len() - 1).try_into().unwrap()));
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
self.spi.dmacr.write(|w| w.bits(0x3)); /*enable dma receive */
|
||||
|
||||
sysctl::dma_select(channel_num, IF::DMA_RX);
|
||||
dmac.set_single_mode(channel_num, self.spi.dr.as_ptr() as u64, rx.as_ptr() as u64,
|
||||
address_increment::NOCHANGE, address_increment::INCREMENT,
|
||||
burst_length::LENGTH_1, transfer_width::WIDTH_32, rx.len() as u32);
|
||||
self.spi.dr[0].write(|w| w.bits(0xffffffff));
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
dmac.wait_done(channel_num);
|
||||
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
|
||||
/// Send arbitrary data
|
||||
fn send_data<X: Into<u32> + Copy>(&self, chip_select: u32, tx: &[X]) {
|
||||
unsafe {
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
|
||||
let mut fifo_len = 0;
|
||||
for &val in tx {
|
||||
while fifo_len == 0 {
|
||||
fifo_len = 32 - self.spi.txflr.read().bits();
|
||||
}
|
||||
self.spi.dr[0].write(|f| f.bits(val.into()));
|
||||
fifo_len -= 1;
|
||||
}
|
||||
|
||||
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
|
||||
// IDLE
|
||||
}
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
|
||||
/// Send 32-bit data using DMA.
|
||||
/// If you want to use this function to send 8-bit or 16-bit data, you need to wrap each
|
||||
/// data unit in a 32-bit word.
|
||||
/// This is intentionally left to the caller: the difficulty here is to avoid the need for alloc/freeing()
|
||||
/// buffers every time as the SDK does because this is highly undesirable!
|
||||
fn send_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, tx: &[u32]) {
|
||||
unsafe {
|
||||
self.spi.dmacr.write(|w| w.bits(0x2)); /*enable dma transmit*/
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
|
||||
sysctl::dma_select(channel_num, IF::DMA_TX);
|
||||
dmac.set_single_mode(channel_num, tx.as_ptr() as u64, self.spi.dr.as_ptr() as u64,
|
||||
address_increment::INCREMENT, address_increment::NOCHANGE,
|
||||
burst_length::LENGTH_4, transfer_width::WIDTH_32, tx.len() as u32);
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
dmac.wait_done(channel_num);
|
||||
|
||||
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
|
||||
// IDLE
|
||||
}
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
|
||||
/// Send repeated data
|
||||
fn fill_data(&self, chip_select: u32, value: u32, mut tx_len: usize) {
|
||||
unsafe {
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
|
||||
while tx_len != 0 {
|
||||
let fifo_len = (32 - self.spi.txflr.read().bits()) as usize;
|
||||
let fifo_len = cmp::min(fifo_len, tx_len);
|
||||
for _ in 0..fifo_len {
|
||||
self.spi.dr[0].write(|f| f.bits(value));
|
||||
}
|
||||
tx_len -= fifo_len;
|
||||
}
|
||||
|
||||
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
|
||||
// IDLE
|
||||
}
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
|
||||
/// Send repeated data (using DMA)
|
||||
fn fill_data_dma(&self, dmac: &DMAC, channel_num: dma_channel, chip_select: u32, value: u32, tx_len: usize) {
|
||||
unsafe {
|
||||
self.spi.dmacr.write(|w| w.bits(0x2)); /*enable dma transmit*/
|
||||
self.spi.ssienr.write(|w| w.bits(0x01));
|
||||
|
||||
sysctl::dma_select(channel_num, IF::DMA_TX);
|
||||
let val = [value];
|
||||
// simple trick to repeating a value: don't increment the source address
|
||||
dmac.set_single_mode(channel_num, val.as_ptr() as u64, self.spi.dr.as_ptr() as u64,
|
||||
address_increment::NOCHANGE, address_increment::NOCHANGE,
|
||||
burst_length::LENGTH_4, transfer_width::WIDTH_32, tx_len.try_into().unwrap());
|
||||
self.spi.ser.write(|w| w.bits(1 << chip_select));
|
||||
dmac.wait_done(channel_num);
|
||||
|
||||
while (self.spi.sr.read().bits() & 0x05) != 0x04 {
|
||||
// IDLE
|
||||
}
|
||||
self.spi.ser.write(|w| w.bits(0x00));
|
||||
self.spi.ssienr.write(|w| w.bits(0x00));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,854 @@
|
||||
#![allow(unused)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
//! SYSCTL peripheral
|
||||
//use k210_hal::pac;
|
||||
use k210_pac as pac;
|
||||
|
||||
use core::convert::TryInto;
|
||||
|
||||
use super::utils::set_bit;
|
||||
use super::sleep::usleep;
|
||||
|
||||
mod pll_compute;
|
||||
|
||||
const SYSCTRL_CLOCK_FREQ_IN0: u32 = 26000000;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum pll {
|
||||
/** PLL0 can usually be selected as alternative to IN0, for example the CPU
|
||||
* clock speed. It can be used as source for PLL2. */
|
||||
PLL0,
|
||||
/** PLL1 is used for the KPU clock, and can be used as source for PLL2. */
|
||||
PLL1,
|
||||
/** PLL2 is used for I2S clocks. */
|
||||
PLL2,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum clock_source {
|
||||
IN0,
|
||||
PLL0,
|
||||
PLL1,
|
||||
PLL2,
|
||||
ACLK,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum clock {
|
||||
PLL0,
|
||||
PLL1,
|
||||
PLL2,
|
||||
CPU,
|
||||
SRAM0,
|
||||
SRAM1,
|
||||
APB0,
|
||||
APB1,
|
||||
APB2,
|
||||
ROM,
|
||||
DMA,
|
||||
AI,
|
||||
DVP,
|
||||
FFT,
|
||||
GPIO,
|
||||
SPI0,
|
||||
SPI1,
|
||||
SPI2,
|
||||
SPI3,
|
||||
I2S0,
|
||||
I2S1,
|
||||
I2S2,
|
||||
I2C0,
|
||||
I2C1,
|
||||
I2C2,
|
||||
UART1,
|
||||
UART2,
|
||||
UART3,
|
||||
AES,
|
||||
FPIOA,
|
||||
TIMER0,
|
||||
TIMER1,
|
||||
TIMER2,
|
||||
WDT0,
|
||||
WDT1,
|
||||
SHA,
|
||||
OTP,
|
||||
RTC,
|
||||
ACLK,
|
||||
HCLK,
|
||||
IN0,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum threshold {
|
||||
ACLK,
|
||||
APB0,
|
||||
APB1,
|
||||
APB2,
|
||||
SRAM0,
|
||||
SRAM1,
|
||||
AI,
|
||||
DVP,
|
||||
ROM,
|
||||
SPI0,
|
||||
SPI1,
|
||||
SPI2,
|
||||
SPI3,
|
||||
TIMER0,
|
||||
TIMER1,
|
||||
TIMER2,
|
||||
I2S0,
|
||||
I2S1,
|
||||
I2S2,
|
||||
I2S0_M,
|
||||
I2S1_M,
|
||||
I2S2_M,
|
||||
I2C0,
|
||||
I2C1,
|
||||
I2C2,
|
||||
WDT0,
|
||||
WDT1,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum clock_select {
|
||||
PLL0_BYPASS,
|
||||
PLL1_BYPASS,
|
||||
PLL2_BYPASS,
|
||||
PLL2,
|
||||
ACLK,
|
||||
SPI3,
|
||||
TIMER0,
|
||||
TIMER1,
|
||||
TIMER2,
|
||||
SPI3_SAMPLE,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum io_power_mode {
|
||||
V33,
|
||||
V18,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum power_bank {
|
||||
BANK0 = 0,
|
||||
BANK1,
|
||||
BANK2,
|
||||
BANK3,
|
||||
BANK4,
|
||||
BANK5,
|
||||
BANK6,
|
||||
BANK7,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum reset {
|
||||
SOC,
|
||||
ROM,
|
||||
DMA,
|
||||
AI,
|
||||
DVP,
|
||||
FFT,
|
||||
GPIO,
|
||||
SPI0,
|
||||
SPI1,
|
||||
SPI2,
|
||||
SPI3,
|
||||
I2S0,
|
||||
I2S1,
|
||||
I2S2,
|
||||
I2C0,
|
||||
I2C1,
|
||||
I2C2,
|
||||
UART1,
|
||||
UART2,
|
||||
UART3,
|
||||
AES,
|
||||
FPIOA,
|
||||
TIMER0,
|
||||
TIMER1,
|
||||
TIMER2,
|
||||
WDT0,
|
||||
WDT1,
|
||||
SHA,
|
||||
RTC,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum dma_channel {
|
||||
CHANNEL0 = 0,
|
||||
CHANNEL1 = 1,
|
||||
CHANNEL2 = 2,
|
||||
CHANNEL3 = 3,
|
||||
CHANNEL4 = 4,
|
||||
CHANNEL5 = 5,
|
||||
}
|
||||
|
||||
impl dma_channel {
|
||||
pub fn idx(self) -> usize { self as usize }
|
||||
}
|
||||
|
||||
pub type dma_select = pac::sysctl::dma_sel0::DMA_SEL0_A;
|
||||
|
||||
fn clock_bus_en(clock: clock, en: bool) {
|
||||
/*
|
||||
* The timer is under APB0, to prevent apb0_clk_en1 and apb0_clk_en0
|
||||
* on same register, we split it to peripheral and central two
|
||||
* registers, to protect CPU close apb0 clock accidentally.
|
||||
*
|
||||
* The apb0_clk_en0 and apb0_clk_en1 have same function,
|
||||
* one of them set, the APB0 clock enable.
|
||||
*/
|
||||
|
||||
/* The APB clock should carefully disable */
|
||||
if en {
|
||||
match clock {
|
||||
/*
|
||||
* These peripheral devices are under APB0
|
||||
* GPIO, UART1, UART2, UART3, SPI_SLAVE, I2S0, I2S1,
|
||||
* I2S2, I2C0, I2C1, I2C2, FPIOA, SHA256, TIMER0,
|
||||
* TIMER1, TIMER2
|
||||
*/
|
||||
clock::GPIO
|
||||
| clock::SPI2
|
||||
| clock::I2S0
|
||||
| clock::I2S1
|
||||
| clock::I2S2
|
||||
| clock::I2C0
|
||||
| clock::I2C1
|
||||
| clock::I2C2
|
||||
| clock::UART1
|
||||
| clock::UART2
|
||||
| clock::UART3
|
||||
| clock::FPIOA
|
||||
| clock::TIMER0
|
||||
| clock::TIMER1
|
||||
| clock::TIMER2
|
||||
| clock::SHA => unsafe {
|
||||
(*pac::SYSCTL::ptr())
|
||||
.clk_en_cent
|
||||
.modify(|_, w| w.apb0_clk_en().bit(en));
|
||||
},
|
||||
|
||||
/*
|
||||
* These peripheral devices are under APB1
|
||||
* WDT, AES, OTP, DVP, SYSCTL
|
||||
*/
|
||||
clock::AES | clock::WDT0 | clock::WDT1 | clock::OTP | clock::RTC => unsafe {
|
||||
(*pac::SYSCTL::ptr())
|
||||
.clk_en_cent
|
||||
.modify(|_, w| w.apb1_clk_en().bit(en));
|
||||
},
|
||||
|
||||
/*
|
||||
* These peripheral devices are under APB2
|
||||
* SPI0, SPI1
|
||||
*/
|
||||
clock::SPI0 | clock::SPI1 => unsafe {
|
||||
(*pac::SYSCTL::ptr())
|
||||
.clk_en_cent
|
||||
.modify(|_, w| w.apb2_clk_en().bit(en));
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clock_device_en(clock: clock, en: bool) {
|
||||
unsafe {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
match clock {
|
||||
clock::PLL0 => (*ptr).pll0.modify(|_, w| w.out_en().bit(en)),
|
||||
clock::PLL1 => (*ptr).pll1.modify(|_, w| w.out_en().bit(en)),
|
||||
clock::PLL2 => (*ptr).pll2.modify(|_, w| w.out_en().bit(en)),
|
||||
clock::CPU => (*ptr).clk_en_cent.modify(|_, w| w.cpu_clk_en().bit(en)),
|
||||
clock::SRAM0 => (*ptr).clk_en_cent.modify(|_, w| w.sram0_clk_en().bit(en)),
|
||||
clock::SRAM1 => (*ptr).clk_en_cent.modify(|_, w| w.sram1_clk_en().bit(en)),
|
||||
clock::APB0 => (*ptr).clk_en_cent.modify(|_, w| w.apb0_clk_en().bit(en)),
|
||||
clock::APB1 => (*ptr).clk_en_cent.modify(|_, w| w.apb1_clk_en().bit(en)),
|
||||
clock::APB2 => (*ptr).clk_en_cent.modify(|_, w| w.apb2_clk_en().bit(en)),
|
||||
clock::ROM => (*ptr).clk_en_peri.modify(|_, w| w.rom_clk_en().bit(en)),
|
||||
clock::DMA => (*ptr).clk_en_peri.modify(|_, w| w.dma_clk_en().bit(en)),
|
||||
clock::AI => (*ptr).clk_en_peri.modify(|_, w| w.ai_clk_en().bit(en)),
|
||||
clock::DVP => (*ptr).clk_en_peri.modify(|_, w| w.dvp_clk_en().bit(en)),
|
||||
clock::FFT => (*ptr).clk_en_peri.modify(|_, w| w.fft_clk_en().bit(en)),
|
||||
clock::SPI3 => (*ptr).clk_en_peri.modify(|_, w| w.spi3_clk_en().bit(en)),
|
||||
clock::GPIO => (*ptr).clk_en_peri.modify(|_, w| w.gpio_clk_en().bit(en)),
|
||||
clock::SPI2 => (*ptr).clk_en_peri.modify(|_, w| w.spi2_clk_en().bit(en)),
|
||||
clock::I2S0 => (*ptr).clk_en_peri.modify(|_, w| w.i2s0_clk_en().bit(en)),
|
||||
clock::I2S1 => (*ptr).clk_en_peri.modify(|_, w| w.i2s1_clk_en().bit(en)),
|
||||
clock::I2S2 => (*ptr).clk_en_peri.modify(|_, w| w.i2s2_clk_en().bit(en)),
|
||||
clock::I2C0 => (*ptr).clk_en_peri.modify(|_, w| w.i2c0_clk_en().bit(en)),
|
||||
clock::I2C1 => (*ptr).clk_en_peri.modify(|_, w| w.i2c1_clk_en().bit(en)),
|
||||
clock::I2C2 => (*ptr).clk_en_peri.modify(|_, w| w.i2c2_clk_en().bit(en)),
|
||||
clock::UART1 => (*ptr).clk_en_peri.modify(|_, w| w.uart1_clk_en().bit(en)),
|
||||
clock::UART2 => (*ptr).clk_en_peri.modify(|_, w| w.uart2_clk_en().bit(en)),
|
||||
clock::UART3 => (*ptr).clk_en_peri.modify(|_, w| w.uart3_clk_en().bit(en)),
|
||||
clock::FPIOA => (*ptr).clk_en_peri.modify(|_, w| w.fpioa_clk_en().bit(en)),
|
||||
clock::TIMER0 => (*ptr).clk_en_peri.modify(|_, w| w.timer0_clk_en().bit(en)),
|
||||
clock::TIMER1 => (*ptr).clk_en_peri.modify(|_, w| w.timer1_clk_en().bit(en)),
|
||||
clock::TIMER2 => (*ptr).clk_en_peri.modify(|_, w| w.timer2_clk_en().bit(en)),
|
||||
clock::SHA => (*ptr).clk_en_peri.modify(|_, w| w.sha_clk_en().bit(en)),
|
||||
clock::AES => (*ptr).clk_en_peri.modify(|_, w| w.aes_clk_en().bit(en)),
|
||||
clock::WDT0 => (*ptr).clk_en_peri.modify(|_, w| w.wdt0_clk_en().bit(en)),
|
||||
clock::WDT1 => (*ptr).clk_en_peri.modify(|_, w| w.wdt1_clk_en().bit(en)),
|
||||
clock::OTP => (*ptr).clk_en_peri.modify(|_, w| w.otp_clk_en().bit(en)),
|
||||
clock::RTC => (*ptr).clk_en_peri.modify(|_, w| w.rtc_clk_en().bit(en)),
|
||||
clock::SPI0 => (*ptr).clk_en_peri.modify(|_, w| w.spi0_clk_en().bit(en)),
|
||||
clock::SPI1 => (*ptr).clk_en_peri.modify(|_, w| w.spi1_clk_en().bit(en)),
|
||||
clock::ACLK | clock::HCLK | clock::IN0 => { /* no separate enables */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_enable(clock: clock) {
|
||||
clock_bus_en(clock, true);
|
||||
clock_device_en(clock, true);
|
||||
}
|
||||
|
||||
pub fn sysctl_clock_disable(clock: clock) {
|
||||
clock_bus_en(clock, false);
|
||||
clock_device_en(clock, false);
|
||||
}
|
||||
|
||||
/// Set clock divider
|
||||
pub fn clock_set_threshold(which: threshold, threshold: u32) {
|
||||
// TODO: this should take a multiplier directly, not a peripheral specific value
|
||||
unsafe {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
match which {
|
||||
/* 2 bit wide */
|
||||
threshold::ACLK => (*ptr).clk_sel0.modify(|_, w| w.aclk_divider_sel().bits(threshold as u8)),
|
||||
|
||||
/* 3 bit wide */
|
||||
threshold::APB0 => (*ptr).clk_sel0.modify(|_, w| w.apb0_clk_sel().bits(threshold as u8)),
|
||||
threshold::APB1 => (*ptr).clk_sel0.modify(|_, w| w.apb1_clk_sel().bits(threshold as u8)),
|
||||
threshold::APB2 => (*ptr).clk_sel0.modify(|_, w| w.apb2_clk_sel().bits(threshold as u8)),
|
||||
|
||||
/* 4 bit wide */
|
||||
threshold::SRAM0 => (*ptr).clk_th0.modify(|_, w| w.sram0_gclk().bits(threshold as u8)),
|
||||
threshold::SRAM1 => (*ptr).clk_th0.modify(|_, w| w.sram1_gclk().bits(threshold as u8)),
|
||||
threshold::AI => (*ptr).clk_th0.modify(|_, w| w.ai_gclk().bits(threshold as u8)),
|
||||
threshold::DVP => (*ptr).clk_th0.modify(|_, w| w.dvp_gclk().bits(threshold as u8)),
|
||||
threshold::ROM => (*ptr).clk_th0.modify(|_, w| w.rom_gclk().bits(threshold as u8)),
|
||||
|
||||
/* 8 bit wide */
|
||||
threshold::SPI0 => (*ptr).clk_th1.modify(|_, w| w.spi0_clk().bits(threshold as u8)),
|
||||
threshold::SPI1 => (*ptr).clk_th1.modify(|_, w| w.spi1_clk().bits(threshold as u8)),
|
||||
threshold::SPI2 => (*ptr).clk_th1.modify(|_, w| w.spi2_clk().bits(threshold as u8)),
|
||||
threshold::SPI3 => (*ptr).clk_th1.modify(|_, w| w.spi3_clk().bits(threshold as u8)),
|
||||
threshold::TIMER0 => (*ptr).clk_th2.modify(|_, w| w.timer0_clk().bits(threshold as u8)),
|
||||
threshold::TIMER1 => (*ptr).clk_th2.modify(|_, w| w.timer1_clk().bits(threshold as u8)),
|
||||
threshold::TIMER2 => (*ptr).clk_th2.modify(|_, w| w.timer2_clk().bits(threshold as u8)),
|
||||
threshold::I2S0_M => (*ptr).clk_th4.modify(|_, w| w.i2s0_mclk().bits(threshold as u8)),
|
||||
threshold::I2S1_M => (*ptr).clk_th4.modify(|_, w| w.i2s1_mclk().bits(threshold as u8)),
|
||||
threshold::I2S2_M => (*ptr).clk_th5.modify(|_, w| w.i2s2_mclk().bits(threshold as u8)),
|
||||
threshold::I2C0 => (*ptr).clk_th5.modify(|_, w| w.i2c0_clk().bits(threshold as u8)),
|
||||
threshold::I2C1 => (*ptr).clk_th5.modify(|_, w| w.i2c1_clk().bits(threshold as u8)),
|
||||
threshold::I2C2 => (*ptr).clk_th5.modify(|_, w| w.i2c2_clk().bits(threshold as u8)),
|
||||
threshold::WDT0 => (*ptr).clk_th6.modify(|_, w| w.wdt0_clk().bits(threshold as u8)),
|
||||
threshold::WDT1 => (*ptr).clk_th6.modify(|_, w| w.wdt1_clk().bits(threshold as u8)),
|
||||
|
||||
/* 16 bit wide */
|
||||
threshold::I2S0 => (*ptr).clk_th3.modify(|_, w| w.i2s0_clk().bits(threshold as u16)),
|
||||
threshold::I2S1 => (*ptr).clk_th3.modify(|_, w| w.i2s1_clk().bits(threshold as u16)),
|
||||
threshold::I2S2 => (*ptr).clk_th4.modify(|_, w| w.i2s2_clk().bits(threshold as u16)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get clock divider
|
||||
pub fn clock_get_threshold(which: threshold) -> u32 {
|
||||
unsafe {
|
||||
// TODO: this should return a multiplier directly, not a peripheral specific value
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
match which {
|
||||
/* 2 bit wide */
|
||||
threshold::ACLK => (*ptr).clk_sel0.read().aclk_divider_sel().bits().into(),
|
||||
|
||||
/* 3 bit wide */
|
||||
threshold::APB0 => (*ptr).clk_sel0.read().apb0_clk_sel().bits().into(),
|
||||
threshold::APB1 => (*ptr).clk_sel0.read().apb1_clk_sel().bits().into(),
|
||||
threshold::APB2 => (*ptr).clk_sel0.read().apb2_clk_sel().bits().into(),
|
||||
|
||||
/* 4 bit wide */
|
||||
threshold::SRAM0 => (*ptr).clk_th0.read().sram0_gclk().bits().into(),
|
||||
threshold::SRAM1 => (*ptr).clk_th0.read().sram1_gclk().bits().into(),
|
||||
threshold::AI => (*ptr).clk_th0.read().ai_gclk().bits().into(),
|
||||
threshold::DVP => (*ptr).clk_th0.read().dvp_gclk().bits().into(),
|
||||
threshold::ROM => (*ptr).clk_th0.read().rom_gclk().bits().into(),
|
||||
|
||||
/* 8 bit wide */
|
||||
threshold::SPI0 => (*ptr).clk_th1.read().spi0_clk().bits().into(),
|
||||
threshold::SPI1 => (*ptr).clk_th1.read().spi1_clk().bits().into(),
|
||||
threshold::SPI2 => (*ptr).clk_th1.read().spi2_clk().bits().into(),
|
||||
threshold::SPI3 => (*ptr).clk_th1.read().spi3_clk().bits().into(),
|
||||
threshold::TIMER0 => (*ptr).clk_th2.read().timer0_clk().bits().into(),
|
||||
threshold::TIMER1 => (*ptr).clk_th2.read().timer1_clk().bits().into(),
|
||||
threshold::TIMER2 => (*ptr).clk_th2.read().timer2_clk().bits().into(),
|
||||
threshold::I2S0_M => (*ptr).clk_th4.read().i2s0_mclk().bits().into(),
|
||||
threshold::I2S1_M => (*ptr).clk_th4.read().i2s1_mclk().bits().into(),
|
||||
threshold::I2S2_M => (*ptr).clk_th5.read().i2s2_mclk().bits().into(),
|
||||
threshold::I2C0 => (*ptr).clk_th5.read().i2c0_clk().bits().into(),
|
||||
threshold::I2C1 => (*ptr).clk_th5.read().i2c1_clk().bits().into(),
|
||||
threshold::I2C2 => (*ptr).clk_th5.read().i2c2_clk().bits().into(),
|
||||
threshold::WDT0 => (*ptr).clk_th6.read().wdt0_clk().bits().into(),
|
||||
threshold::WDT1 => (*ptr).clk_th6.read().wdt1_clk().bits().into(),
|
||||
|
||||
/* 16 bit wide */
|
||||
threshold::I2S0 => (*ptr).clk_th3.read().i2s0_clk().bits().into(),
|
||||
threshold::I2S1 => (*ptr).clk_th3.read().i2s1_clk().bits().into(),
|
||||
threshold::I2S2 => (*ptr).clk_th4.read().i2s2_clk().bits().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_power_mode(power_bank: power_bank, mode: io_power_mode) {
|
||||
unsafe {
|
||||
(*pac::SYSCTL::ptr()).power_sel.modify(|r, w| {
|
||||
w.bits(set_bit(
|
||||
r.bits(),
|
||||
power_bank as u8,
|
||||
match mode {
|
||||
io_power_mode::V33 => false,
|
||||
io_power_mode::V18 => true,
|
||||
},
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Route SPI0_D0-D7 DVP_D0-D7 functions to SPI and DVP data pins (bypassing FPIOA). */
|
||||
pub fn set_spi0_dvp_data(status: bool) {
|
||||
unsafe {
|
||||
(*pac::SYSCTL::ptr())
|
||||
.misc
|
||||
.modify(|_, w| w.spi_dvp_data_enable().bit(status));
|
||||
}
|
||||
}
|
||||
|
||||
/** Map PLL2 cksel value to clock source */
|
||||
fn pll2_cksel_to_source(bits: u8) -> clock_source {
|
||||
match bits {
|
||||
0 => clock_source::IN0,
|
||||
1 => clock_source::PLL0,
|
||||
2 => clock_source::PLL1,
|
||||
_ => panic!("invalid value for PLL2 ckin_sel"),
|
||||
}
|
||||
}
|
||||
|
||||
/** Map clock source to PLL2 cksel value */
|
||||
fn pll2_source_to_cksel(source: clock_source) -> u8 {
|
||||
match source {
|
||||
clock_source::IN0 => 0,
|
||||
clock_source::PLL0 => 1,
|
||||
clock_source::PLL1 => 2,
|
||||
_ => panic!("unsupported clock source for PLL2"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pll_get_freq(pll: pll) -> u32 {
|
||||
let freq_in;
|
||||
let nr;
|
||||
let nf;
|
||||
let od;
|
||||
|
||||
match pll {
|
||||
pll::PLL0 => {
|
||||
freq_in = clock_source_get_freq(clock_source::IN0);
|
||||
unsafe {
|
||||
let val = (*pac::SYSCTL::ptr()).pll0.read();
|
||||
nr = val.clkr().bits() + 1;
|
||||
nf = val.clkf().bits() + 1;
|
||||
od = val.clkod().bits() + 1;
|
||||
}
|
||||
}
|
||||
pll::PLL1 => {
|
||||
freq_in = clock_source_get_freq(clock_source::IN0);
|
||||
unsafe {
|
||||
let val = (*pac::SYSCTL::ptr()).pll1.read();
|
||||
nr = val.clkr().bits() + 1;
|
||||
nf = val.clkf().bits() + 1;
|
||||
od = val.clkod().bits() + 1;
|
||||
}
|
||||
}
|
||||
pll::PLL2 => {
|
||||
/* Get input freq accrording to select register. */
|
||||
freq_in = clock_source_get_freq(pll2_cksel_to_source(clock_get_clock_select(clock_select::PLL2)));
|
||||
unsafe {
|
||||
let val = (*pac::SYSCTL::ptr()).pll2.read();
|
||||
nr = val.clkr().bits() + 1;
|
||||
nf = val.clkf().bits() + 1;
|
||||
od = val.clkod().bits() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get final PLL output freq
|
||||
* FOUT = FIN / NR * NF / OD
|
||||
* (rewritten as integer expression)
|
||||
*/
|
||||
((u64::from(freq_in) * u64::from(nf)) / (u64::from(nr) * u64::from(od))).try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn clock_source_get_freq(source: clock_source) -> u32 {
|
||||
match source {
|
||||
clock_source::IN0 => SYSCTRL_CLOCK_FREQ_IN0,
|
||||
clock_source::PLL0 => pll_get_freq(pll::PLL0),
|
||||
clock_source::PLL1 => pll_get_freq(pll::PLL1),
|
||||
clock_source::PLL2 => pll_get_freq(pll::PLL2),
|
||||
clock_source::ACLK => clock_get_freq(clock::ACLK),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_set_clock_select(which: clock_select, select: u8) {
|
||||
unsafe {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
// Seems that PLL2 is the only one that takes a non-boolean clock select
|
||||
// TODO: take a clock_source directly when we know the meanings of these bits
|
||||
match which {
|
||||
clock_select::PLL0_BYPASS => (*ptr).pll0.modify(|_, w| w.bypass().bit(select != 0)),
|
||||
clock_select::PLL1_BYPASS => (*ptr).pll1.modify(|_, w| w.bypass().bit(select != 0)),
|
||||
clock_select::PLL2_BYPASS => (*ptr).pll2.modify(|_, w| w.bypass().bit(select != 0)),
|
||||
clock_select::PLL2 => (*ptr).pll2.modify(|_, w| w.ckin_sel().bits(select)),
|
||||
clock_select::ACLK => (*ptr).clk_sel0.modify(|_, w| w.aclk_sel().bit(select != 0)),
|
||||
clock_select::SPI3 => (*ptr).clk_sel0.modify(|_, w| w.spi3_clk_sel().bit(select != 0)),
|
||||
clock_select::TIMER0 => (*ptr).clk_sel0.modify(|_, w| w.timer0_clk_sel().bit(select != 0)),
|
||||
clock_select::TIMER1 => (*ptr).clk_sel0.modify(|_, w| w.timer1_clk_sel().bit(select != 0)),
|
||||
clock_select::TIMER2 => (*ptr).clk_sel0.modify(|_, w| w.timer2_clk_sel().bit(select != 0)),
|
||||
clock_select::SPI3_SAMPLE => (*ptr).clk_sel1.modify(|_, w| w.spi3_sample_clk_sel().bit(select != 0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_get_clock_select(which: clock_select) -> u8 {
|
||||
unsafe {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
// Seems that PLL2 is the only one that has a non-boolean clock select
|
||||
// TODO: return a clock_source directly when we know the meanings of these bits
|
||||
// meaning seems to be usually:
|
||||
// 0 IN0
|
||||
// 1 PLL0
|
||||
// (2 PLL1)
|
||||
// it's likely different for _BYPASS, which, I suspect, wires the PLL output to the
|
||||
// input (IN0 for PLL0 and PLL1, selectable for PLL2)
|
||||
match which {
|
||||
clock_select::PLL0_BYPASS => (*ptr).pll0.read().bypass().bit().into(),
|
||||
clock_select::PLL1_BYPASS => (*ptr).pll1.read().bypass().bit().into(),
|
||||
clock_select::PLL2_BYPASS => (*ptr).pll2.read().bypass().bit().into(),
|
||||
clock_select::PLL2 => (*ptr).pll2.read().ckin_sel().bits().into(),
|
||||
clock_select::ACLK => (*ptr).clk_sel0.read().aclk_sel().bit().into(),
|
||||
clock_select::SPI3 => (*ptr).clk_sel0.read().spi3_clk_sel().bit().into(),
|
||||
clock_select::TIMER0 => (*ptr).clk_sel0.read().timer0_clk_sel().bit().into(),
|
||||
clock_select::TIMER1 => (*ptr).clk_sel0.read().timer1_clk_sel().bit().into(),
|
||||
clock_select::TIMER2 => (*ptr).clk_sel0.read().timer2_clk_sel().bit().into(),
|
||||
clock_select::SPI3_SAMPLE => (*ptr).clk_sel1.read().spi3_sample_clk_sel().bit().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_get_freq(clock: clock) -> u32 {
|
||||
// TODO: all of these are source / threshold, where source can depend on clock_select: generalize this
|
||||
// to some kind of clock tree
|
||||
// TODO: clock_source_get_freq(ACLK) calls back into here, don't do this
|
||||
match clock {
|
||||
clock::IN0 => clock_source_get_freq(clock_source::IN0),
|
||||
clock::PLL0 => clock_source_get_freq(clock_source::PLL0),
|
||||
clock::PLL1 => clock_source_get_freq(clock_source::PLL1),
|
||||
clock::PLL2 => clock_source_get_freq(clock_source::PLL2),
|
||||
clock::CPU | clock::DMA | clock::FFT | clock::ACLK | clock::HCLK => match clock_get_clock_select(clock_select::ACLK) {
|
||||
0 => clock_source_get_freq(clock_source::IN0),
|
||||
1 => {
|
||||
clock_source_get_freq(clock_source::PLL0)
|
||||
/ (2 << clock_get_threshold(threshold::ACLK))
|
||||
}
|
||||
_ => panic!("invalid cpu clock select"),
|
||||
},
|
||||
clock::SRAM0 => clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::SRAM0) + 1),
|
||||
clock::SRAM1 => clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::SRAM1) + 1),
|
||||
clock::ROM => clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::ROM) + 1),
|
||||
clock::DVP => clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::DVP) + 1),
|
||||
clock::APB0 | clock::GPIO | clock::UART1 | clock::UART2 | clock::UART3 | clock::FPIOA | clock::SHA =>
|
||||
clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::APB0) + 1),
|
||||
clock::APB1 | clock::AES | clock::OTP =>
|
||||
clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::APB1) + 1),
|
||||
clock::APB2 => clock_source_get_freq(clock_source::ACLK) / (clock_get_threshold(threshold::APB2) + 1),
|
||||
clock::AI => clock_source_get_freq(clock_source::PLL1) / (clock_get_threshold(threshold::AI) + 1),
|
||||
clock::I2S0 => clock_source_get_freq(clock_source::PLL2) / ((clock_get_threshold(threshold::I2S0) + 1) * 2),
|
||||
clock::I2S1 => clock_source_get_freq(clock_source::PLL2) / ((clock_get_threshold(threshold::I2S1) + 1) * 2),
|
||||
clock::I2S2 => clock_source_get_freq(clock_source::PLL2) / ((clock_get_threshold(threshold::I2S2) + 1) * 2),
|
||||
clock::WDT0 => clock_source_get_freq(clock_source::IN0) / ((clock_get_threshold(threshold::WDT0) + 1) * 2),
|
||||
clock::WDT1 => clock_source_get_freq(clock_source::IN0) / ((clock_get_threshold(threshold::WDT1) + 1) * 2),
|
||||
clock::SPI0 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::SPI0) + 1) * 2),
|
||||
clock::SPI1 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::SPI1) + 1) * 2),
|
||||
clock::SPI2 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::SPI2) + 1) * 2),
|
||||
clock::I2C0 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::I2C0) + 1) * 2),
|
||||
clock::I2C1 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::I2C1) + 1) * 2),
|
||||
clock::I2C2 => clock_source_get_freq(clock_source::PLL0) / ((clock_get_threshold(threshold::I2C2) + 1) * 2),
|
||||
clock::SPI3 => {
|
||||
let source = match clock_get_clock_select(clock_select::SPI3) {
|
||||
0 => clock_source_get_freq(clock_source::IN0),
|
||||
1 => clock_source_get_freq(clock_source::PLL0),
|
||||
_ => panic!("unimplemented clock source"),
|
||||
};
|
||||
source / ((clock_get_threshold(threshold::SPI3) + 1) * 2)
|
||||
}
|
||||
clock::TIMER0 => {
|
||||
let source = match clock_get_clock_select(clock_select::TIMER0) {
|
||||
0 => clock_source_get_freq(clock_source::IN0),
|
||||
1 => clock_source_get_freq(clock_source::PLL0),
|
||||
_ => panic!("unimplemented clock source"),
|
||||
};
|
||||
source / ((clock_get_threshold(threshold::TIMER0) + 1) * 2)
|
||||
}
|
||||
clock::TIMER1 => {
|
||||
let source = match clock_get_clock_select(clock_select::TIMER1) {
|
||||
0 => clock_source_get_freq(clock_source::IN0),
|
||||
1 => clock_source_get_freq(clock_source::PLL0),
|
||||
_ => panic!("unimplemented clock source"),
|
||||
};
|
||||
source / ((clock_get_threshold(threshold::TIMER1) + 1) * 2)
|
||||
}
|
||||
clock::TIMER2 => {
|
||||
let source = match clock_get_clock_select(clock_select::TIMER2) {
|
||||
0 => clock_source_get_freq(clock_source::IN0),
|
||||
1 => clock_source_get_freq(clock_source::PLL0),
|
||||
_ => panic!("unimplemented clock source"),
|
||||
};
|
||||
source / ((clock_get_threshold(threshold::TIMER2) + 1) * 2)
|
||||
}
|
||||
clock::RTC => clock_source_get_freq(clock_source::IN0),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_ctl(reset: reset, rst_value: bool) {
|
||||
unsafe {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
match reset {
|
||||
reset::SOC => (*ptr).soft_reset.modify(|_, w| w.soft_reset().bit(rst_value)),
|
||||
reset::ROM => (*ptr).peri_reset.modify(|_, w| w.rom_reset().bit(rst_value)),
|
||||
reset::DMA => (*ptr).peri_reset.modify(|_, w| w.dma_reset().bit(rst_value)),
|
||||
reset::AI => (*ptr).peri_reset.modify(|_, w| w.ai_reset().bit(rst_value)),
|
||||
reset::DVP => (*ptr).peri_reset.modify(|_, w| w.dvp_reset().bit(rst_value)),
|
||||
reset::FFT => (*ptr).peri_reset.modify(|_, w| w.fft_reset().bit(rst_value)),
|
||||
reset::GPIO => (*ptr).peri_reset.modify(|_, w| w.gpio_reset().bit(rst_value)),
|
||||
reset::SPI0 => (*ptr).peri_reset.modify(|_, w| w.spi0_reset().bit(rst_value)),
|
||||
reset::SPI1 => (*ptr).peri_reset.modify(|_, w| w.spi1_reset().bit(rst_value)),
|
||||
reset::SPI2 => (*ptr).peri_reset.modify(|_, w| w.spi2_reset().bit(rst_value)),
|
||||
reset::SPI3 => (*ptr).peri_reset.modify(|_, w| w.spi3_reset().bit(rst_value)),
|
||||
reset::I2S0 => (*ptr).peri_reset.modify(|_, w| w.i2s0_reset().bit(rst_value)),
|
||||
reset::I2S1 => (*ptr).peri_reset.modify(|_, w| w.i2s1_reset().bit(rst_value)),
|
||||
reset::I2S2 => (*ptr).peri_reset.modify(|_, w| w.i2s2_reset().bit(rst_value)),
|
||||
reset::I2C0 => (*ptr).peri_reset.modify(|_, w| w.i2c0_reset().bit(rst_value)),
|
||||
reset::I2C1 => (*ptr).peri_reset.modify(|_, w| w.i2c1_reset().bit(rst_value)),
|
||||
reset::I2C2 => (*ptr).peri_reset.modify(|_, w| w.i2c2_reset().bit(rst_value)),
|
||||
reset::UART1 => (*ptr).peri_reset.modify(|_, w| w.uart1_reset().bit(rst_value)),
|
||||
reset::UART2 => (*ptr).peri_reset.modify(|_, w| w.uart2_reset().bit(rst_value)),
|
||||
reset::UART3 => (*ptr).peri_reset.modify(|_, w| w.uart3_reset().bit(rst_value)),
|
||||
reset::AES => (*ptr).peri_reset.modify(|_, w| w.aes_reset().bit(rst_value)),
|
||||
reset::FPIOA => (*ptr).peri_reset.modify(|_, w| w.fpioa_reset().bit(rst_value)),
|
||||
reset::TIMER0 => (*ptr).peri_reset.modify(|_, w| w.timer0_reset().bit(rst_value)),
|
||||
reset::TIMER1 => (*ptr).peri_reset.modify(|_, w| w.timer1_reset().bit(rst_value)),
|
||||
reset::TIMER2 => (*ptr).peri_reset.modify(|_, w| w.timer2_reset().bit(rst_value)),
|
||||
reset::WDT0 => (*ptr).peri_reset.modify(|_, w| w.wdt0_reset().bit(rst_value)),
|
||||
reset::WDT1 => (*ptr).peri_reset.modify(|_, w| w.wdt1_reset().bit(rst_value)),
|
||||
reset::SHA => (*ptr).peri_reset.modify(|_, w| w.sha_reset().bit(rst_value)),
|
||||
reset::RTC => (*ptr).peri_reset.modify(|_, w| w.rtc_reset().bit(rst_value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(reset: reset) {
|
||||
reset_ctl(reset, true);
|
||||
usleep(10);
|
||||
reset_ctl(reset, false);
|
||||
}
|
||||
|
||||
/** Select DMA handshake for a channel */
|
||||
pub fn dma_select(channel: dma_channel, select: dma_select)
|
||||
{
|
||||
unsafe {
|
||||
use dma_channel::*;
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
match channel {
|
||||
CHANNEL0 => (*ptr).dma_sel0.modify(|_,w| w.dma_sel0().variant(select)),
|
||||
CHANNEL1 => (*ptr).dma_sel0.modify(|_,w| w.dma_sel1().variant(select)),
|
||||
CHANNEL2 => (*ptr).dma_sel0.modify(|_,w| w.dma_sel2().variant(select)),
|
||||
CHANNEL3 => (*ptr).dma_sel0.modify(|_,w| w.dma_sel3().variant(select)),
|
||||
CHANNEL4 => (*ptr).dma_sel0.modify(|_,w| w.dma_sel4().variant(select)),
|
||||
CHANNEL5 => (*ptr).dma_sel1.modify(|_,w| w.dma_sel5().variant(select)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Return whether the selected PLL has achieved lock */
|
||||
fn pll_is_lock(pll: pll) -> bool {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
let pll_lock = unsafe { (*ptr).pll_lock.read() };
|
||||
match pll {
|
||||
pll::PLL0 => pll_lock.pll_lock0().bits() == 3,
|
||||
pll::PLL1 => (pll_lock.pll_lock1().bits() & 1) == 1,
|
||||
pll::PLL2 => (pll_lock.pll_lock2().bits() & 1) == 1,
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear PLL slip, this is done repeatedly until lock is achieved */
|
||||
fn pll_clear_slip(pll: pll) -> bool {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
unsafe {
|
||||
(*ptr).pll_lock.modify(|_,w|
|
||||
match pll {
|
||||
pll::PLL0 => w.pll_slip_clear0().set_bit(),
|
||||
pll::PLL1 => w.pll_slip_clear1().set_bit(),
|
||||
pll::PLL2 => w.pll_slip_clear2().set_bit(),
|
||||
}
|
||||
);
|
||||
}
|
||||
pll_is_lock(pll)
|
||||
}
|
||||
|
||||
fn pll_source_set_freq(pll: pll, source: clock_source, freq: u32) -> Result<u32,()> {
|
||||
use pll::*;
|
||||
/* PLL0 and 1 can only source from IN0 */
|
||||
if (pll == PLL0 || pll == PLL1) && source != clock_source::IN0 {
|
||||
return Err(());
|
||||
}
|
||||
let freq_in = clock_source_get_freq(source);
|
||||
if freq_in == 0 {
|
||||
return Err(());
|
||||
}
|
||||
if let Some(found) = pll_compute::compute_params(freq_in, freq) {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => {
|
||||
(*ptr).pll0.modify(|_,w|
|
||||
w.clkr().bits(found.clkr)
|
||||
.clkf().bits(found.clkf)
|
||||
.clkod().bits(found.clkod)
|
||||
.bwadj().bits(found.bwadj)
|
||||
);
|
||||
}
|
||||
PLL1 => {
|
||||
(*ptr).pll1.modify(|_,w|
|
||||
w.clkr().bits(found.clkr)
|
||||
.clkf().bits(found.clkf)
|
||||
.clkod().bits(found.clkod)
|
||||
.bwadj().bits(found.bwadj)
|
||||
);
|
||||
}
|
||||
PLL2 => {
|
||||
(*ptr).pll2.modify(|_,w|
|
||||
w.ckin_sel().bits(pll2_source_to_cksel(source))
|
||||
.clkr().bits(found.clkr)
|
||||
.clkf().bits(found.clkf)
|
||||
.clkod().bits(found.clkod)
|
||||
.bwadj().bits(found.bwadj)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(pll_get_freq(pll))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init PLL freqency
|
||||
* @param[in] pll The PLL id
|
||||
* @param[in] pll_freq The desired frequency in Hz
|
||||
|
||||
*/
|
||||
pub fn pll_set_freq(pll: pll, freq: u32) -> Result<u32,()> {
|
||||
let ptr = pac::SYSCTL::ptr();
|
||||
use pll::*;
|
||||
|
||||
/* 1. Change CPU CLK to XTAL */
|
||||
if pll == PLL0 {
|
||||
clock_set_clock_select(clock_select::ACLK, 0 /* clock_source::IN0 */);
|
||||
}
|
||||
|
||||
/* 2. Disable PLL output */
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.out_en().clear_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.out_en().clear_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.out_en().clear_bit()),
|
||||
};
|
||||
}
|
||||
/* 3. Turn off PLL */
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.pwrd().clear_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.pwrd().clear_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.pwrd().clear_bit()),
|
||||
};
|
||||
}
|
||||
/* 4. Set PLL to new value */
|
||||
let result = if pll == PLL2 {
|
||||
pll_source_set_freq(pll, pll2_cksel_to_source(clock_get_clock_select(clock_select::PLL2)), freq)
|
||||
} else {
|
||||
pll_source_set_freq(pll, clock_source::IN0, freq)
|
||||
};
|
||||
|
||||
/* 5. Power on PLL */
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.pwrd().set_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.pwrd().set_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.pwrd().set_bit()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* wait >100ns */
|
||||
usleep(1);
|
||||
|
||||
/* 6. Reset PLL then Release Reset*/
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.reset().clear_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.reset().clear_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.reset().clear_bit()),
|
||||
};
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.reset().set_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.reset().set_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.reset().set_bit()),
|
||||
};
|
||||
}
|
||||
/* wait >100ns */
|
||||
usleep(1);
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.reset().clear_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.reset().clear_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.reset().clear_bit()),
|
||||
};
|
||||
}
|
||||
|
||||
/* 7. Get lock status, wait PLL stable */
|
||||
while !pll_is_lock(pll) {
|
||||
pll_clear_slip(pll);
|
||||
}
|
||||
|
||||
/* 8. Enable PLL output */
|
||||
unsafe {
|
||||
match pll {
|
||||
PLL0 => (*ptr).pll0.modify(|_,w| w.out_en().set_bit()),
|
||||
PLL1 => (*ptr).pll1.modify(|_,w| w.out_en().set_bit()),
|
||||
PLL2 => (*ptr).pll2.modify(|_,w| w.out_en().set_bit()),
|
||||
};
|
||||
}
|
||||
|
||||
/* 9. Change CPU CLK to PLL */
|
||||
if pll == PLL0 {
|
||||
clock_set_clock_select(clock_select::ACLK, 1 /*clock_source::PLL0*/);
|
||||
}
|
||||
result
|
||||
}
|
@ -0,0 +1,569 @@
|
||||
/** PLL configuration */
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Params {
|
||||
pub clkr: u8,
|
||||
pub clkf: u8,
|
||||
pub clkod: u8,
|
||||
pub bwadj: u8,
|
||||
}
|
||||
|
||||
/*
|
||||
* The PLL included with the Kendryte K210 appears to be a True Circuits, Inc.
|
||||
* General-Purpose PLL. The logical layout of the PLL with internal feedback is
|
||||
* approximately the following:
|
||||
*
|
||||
* +---------------+
|
||||
* |reference clock|
|
||||
* +---------------+
|
||||
* |
|
||||
* v
|
||||
* +--+
|
||||
* |/r|
|
||||
* +--+
|
||||
* |
|
||||
* v
|
||||
* +-------------+
|
||||
* |divided clock|
|
||||
* +-------------+
|
||||
* |
|
||||
* v
|
||||
* +--------------+
|
||||
* |phase detector|<---+
|
||||
* +--------------+ |
|
||||
* | |
|
||||
* v +--------------+
|
||||
* +---+ |feedback clock|
|
||||
* |VCO| +--------------+
|
||||
* +---+ ^
|
||||
* | +--+ |
|
||||
* +--->|/f|---+
|
||||
* | +--+
|
||||
* v
|
||||
* +---+
|
||||
* |/od|
|
||||
* +---+
|
||||
* |
|
||||
* v
|
||||
* +------+
|
||||
* |output|
|
||||
* +------+
|
||||
*
|
||||
* The k210 PLLs have three factors: r, f, and od. Because of the feedback mode,
|
||||
* the effect of the division by f is to multiply the input frequency. The
|
||||
* equation for the output rate is
|
||||
* rate = (rate_in * f) / (r * od).
|
||||
* Moving knowns to one side of the equation, we get
|
||||
* rate / rate_in = f / (r * od)
|
||||
* Rearranging slightly,
|
||||
* abs_error = abs((rate / rate_in) - (f / (r * od))).
|
||||
* To get relative, error, we divide by the expected ratio
|
||||
* error = abs((rate / rate_in) - (f / (r * od))) / (rate / rate_in).
|
||||
* Simplifying,
|
||||
* error = abs(1 - f / (r * od)) / (rate / rate_in)
|
||||
* error = abs(1 - (f * rate_in) / (r * od * rate))
|
||||
* Using the constants ratio = rate / rate_in and inv_ratio = rate_in / rate,
|
||||
* error = abs((f * inv_ratio) / (r * od) - 1)
|
||||
* This is the error used in evaluating parameters.
|
||||
*
|
||||
* r and od are four bits each, while f is six bits. Because r and od are
|
||||
* multiplied together, instead of the full 256 values possible if both bits
|
||||
* were used fully, there are only 97 distinct products. Combined with f, there
|
||||
* are 6208 theoretical settings for the PLL. However, most of these settings
|
||||
* can be ruled out immediately because they do not have the correct ratio.
|
||||
*
|
||||
* In addition to the constraint of approximating the desired ratio, parameters
|
||||
* must also keep internal pll frequencies within acceptable ranges. The divided
|
||||
* clock's minimum and maximum frequencies have a ratio of around 128. This
|
||||
* leaves fairly substantial room to work with, especially since the only
|
||||
* affected parameter is r. The VCO's minimum and maximum frequency have a ratio
|
||||
* of 5, which is considerably more restrictive.
|
||||
*
|
||||
* The r and od factors are stored in a table. This is to make it easy to find
|
||||
* the next-largest product. Some products have multiple factorizations, but
|
||||
* only when one factor has at least a 2.5x ratio to the factors of the other
|
||||
* factorization. This is because any smaller ratio would not make a difference
|
||||
* when ensuring the VCO's frequency is within spec.
|
||||
*
|
||||
* Throughout the calculation function, fixed point arithmetic is used. Because
|
||||
* the range of rate and rate_in may be up to 1.75 GHz, or around 2^30, 64-bit
|
||||
* 32.32 fixed-point numbers are used to represent ratios. In general, to
|
||||
* implement division, the numerator is first multiplied by 2^32. This gives a
|
||||
* result where the whole number part is in the upper 32 bits, and the fraction
|
||||
* is in the lower 32 bits.
|
||||
*
|
||||
* In general, rounding is done to the closest integer. This helps find the best
|
||||
* approximation for the ratio. Rounding in one direction (e.g down) could cause
|
||||
* the function to miss a better ratio with one of the parameters increased by
|
||||
* one.
|
||||
*/
|
||||
|
||||
const VCO_MIN: u64 = 340_000_000;
|
||||
const VCO_MAX: u64 = 1_750_000_000;
|
||||
const DIV_MIN: u32 = 13_300_000;
|
||||
const DIV_MAX: u32 = 1_750_000_000;
|
||||
const R_MIN: u32 = 1;
|
||||
const R_MAX: u32 = 16;
|
||||
const F_MIN: u32 = 1;
|
||||
const F_MAX: u32 = 64;
|
||||
const OD_MIN: u32 = 1;
|
||||
const OD_MAX: u32 = 16;
|
||||
const OUT_MIN: u32 = (VCO_MIN as u32) / OD_MAX; /* 21_250_000 */
|
||||
const OUT_MAX: u32 = (VCO_MAX as u32) / OD_MIN; /* 1_750_000_000 */
|
||||
const IN_MIN: u32 = DIV_MIN * R_MIN; /* 13_300_000 */
|
||||
const IN_MAX: u32 = OUT_MAX; /* 1_750_000_000 */
|
||||
|
||||
/*
|
||||
* The factors table was generated with the following python code:
|
||||
*
|
||||
* def p(x, y):
|
||||
* return (1.0*x/y > 2.5) or (1.0*y/x > 2.5)
|
||||
*
|
||||
* factors = {}
|
||||
* for i in range(1, 17):
|
||||
* for j in range(1, 17):
|
||||
* fs = factors.get(i*j) or []
|
||||
* if fs == [] or all([
|
||||
* (p(i, x) and p(i, y)) or (p(j, x) and p(j, y))
|
||||
* for (x, y) in fs]):
|
||||
* fs.append((i, j))
|
||||
* factors[i*j] = fs
|
||||
*
|
||||
* for k, l in sorted(factors.items()):
|
||||
* for v in l:
|
||||
* print("pack(%s, %s)," % v)
|
||||
*/
|
||||
struct Factor {
|
||||
packed: u8,
|
||||
}
|
||||
|
||||
/* Apologies, but there are no native bitfields (yet) afaik */
|
||||
const fn pack(r: u32, od: u32) -> Factor {
|
||||
Factor { packed: (((((r as u8) - 1) & 0xF) << 4) | (((od as u8) - 1) & 0xF)) }
|
||||
}
|
||||
|
||||
const fn unpack_r(factor: &&Factor) -> u32 {
|
||||
(((factor.packed as u32) >> 4) & 0xF) + 1
|
||||
}
|
||||
|
||||
const fn unpack_od(factor: &&Factor) -> u32 {
|
||||
((factor.packed as u32) & 0xF) + 1
|
||||
}
|
||||
|
||||
static FACTORS: &'static [Factor] = &[
|
||||
pack(1, 1),
|
||||
pack(1, 2),
|
||||
pack(1, 3),
|
||||
pack(1, 4),
|
||||
pack(1, 5),
|
||||
pack(1, 6),
|
||||
pack(1, 7),
|
||||
pack(1, 8),
|
||||
pack(1, 9),
|
||||
pack(3, 3),
|
||||
pack(1, 10),
|
||||
pack(1, 11),
|
||||
pack(1, 12),
|
||||
pack(3, 4),
|
||||
pack(1, 13),
|
||||
pack(1, 14),
|
||||
pack(1, 15),
|
||||
pack(3, 5),
|
||||
pack(1, 16),
|
||||
pack(4, 4),
|
||||
pack(2, 9),
|
||||
pack(2, 10),
|
||||
pack(3, 7),
|
||||
pack(2, 11),
|
||||
pack(2, 12),
|
||||
pack(5, 5),
|
||||
pack(2, 13),
|
||||
pack(3, 9),
|
||||
pack(2, 14),
|
||||
pack(2, 15),
|
||||
pack(2, 16),
|
||||
pack(3, 11),
|
||||
pack(5, 7),
|
||||
pack(3, 12),
|
||||
pack(3, 13),
|
||||
pack(4, 10),
|
||||
pack(3, 14),
|
||||
pack(4, 11),
|
||||
pack(3, 15),
|
||||
pack(3, 16),
|
||||
pack(7, 7),
|
||||
pack(5, 10),
|
||||
pack(4, 13),
|
||||
pack(6, 9),
|
||||
pack(5, 11),
|
||||
pack(4, 14),
|
||||
pack(4, 15),
|
||||
pack(7, 9),
|
||||
pack(4, 16),
|
||||
pack(5, 13),
|
||||
pack(6, 11),
|
||||
pack(5, 14),
|
||||
pack(6, 12),
|
||||
pack(5, 15),
|
||||
pack(7, 11),
|
||||
pack(6, 13),
|
||||
pack(5, 16),
|
||||
pack(9, 9),
|
||||
pack(6, 14),
|
||||
pack(8, 11),
|
||||
pack(6, 15),
|
||||
pack(7, 13),
|
||||
pack(6, 16),
|
||||
pack(7, 14),
|
||||
pack(9, 11),
|
||||
pack(10, 10),
|
||||
pack(8, 13),
|
||||
pack(7, 15),
|
||||
pack(9, 12),
|
||||
pack(10, 11),
|
||||
pack(7, 16),
|
||||
pack(9, 13),
|
||||
pack(8, 15),
|
||||
pack(11, 11),
|
||||
pack(9, 14),
|
||||
pack(8, 16),
|
||||
pack(10, 13),
|
||||
pack(11, 12),
|
||||
pack(9, 15),
|
||||
pack(10, 14),
|
||||
pack(11, 13),
|
||||
pack(9, 16),
|
||||
pack(10, 15),
|
||||
pack(11, 14),
|
||||
pack(12, 13),
|
||||
pack(10, 16),
|
||||
pack(11, 15),
|
||||
pack(12, 14),
|
||||
pack(13, 13),
|
||||
pack(11, 16),
|
||||
pack(12, 15),
|
||||
pack(13, 14),
|
||||
pack(12, 16),
|
||||
pack(13, 15),
|
||||
pack(14, 14),
|
||||
pack(13, 16),
|
||||
pack(14, 15),
|
||||
pack(14, 16),
|
||||
pack(15, 15),
|
||||
pack(15, 16),
|
||||
pack(16, 16),
|
||||
];
|
||||
|
||||
/* Divide and round to the closest integer */
|
||||
fn div_round_closest(n: u64, d: u32) -> u64 {
|
||||
let _d: u64 = d as u64;
|
||||
|
||||
(n + (_d / 2)) / _d
|
||||
}
|
||||
|
||||
/* Integer with that bit set */
|
||||
fn bit(bit: u8) -> u64 {
|
||||
1 << bit
|
||||
}
|
||||
|
||||
/* | a - b | */
|
||||
fn abs_diff(a: u32, b: u32) -> u32 {
|
||||
if a > b {
|
||||
a - b
|
||||
} else {
|
||||
b - a
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_params(freq_in: u32, freq_out: u32) -> Option<Params> {
|
||||
let mut best: Option<Params> = None;
|
||||
let mut factors = FACTORS.iter().peekable();
|
||||
let (mut error, mut best_error): (i64, i64);
|
||||
let (ratio, inv_ratio): (u64, u64); /* fixed point 32.32 ratio of the freqs */
|
||||
let max_r: u32;
|
||||
let (mut r, mut f, mut od): (u32, u32, u32);
|
||||
|
||||
/*
|
||||
* Can't go over 1.75 GHz or under 21.25 MHz due to limitations on the
|
||||
* VCO frequency. These are not the same limits as below because od can
|
||||
* reduce the output frequency by 16.
|
||||
*/
|
||||
if freq_out > OUT_MAX || freq_out < OUT_MIN {
|
||||
return None;
|
||||
}
|
||||
|
||||
/* Similar restrictions on the input ratio */
|
||||
if freq_in > IN_MAX || freq_in < IN_MIN {
|
||||
return None;
|
||||
}
|
||||
|
||||
ratio = div_round_closest((freq_out as u64) << 32, freq_in);
|
||||
inv_ratio = div_round_closest((freq_in as u64) << 32, freq_out);
|
||||
/* Can't increase by more than 64 or reduce by more than 256 */
|
||||
if freq_out > freq_in && ratio > (64 << 32) {
|
||||
return None;
|
||||
} else if freq_out <= freq_in && inv_ratio > (256 << 32) {
|
||||
return None;
|
||||
}
|
||||
|
||||
/*
|
||||
* The divided clock (freq_in / r) must stay between 1.75 GHz and 13.3
|
||||
* MHz. There is no minimum, since the only way to get a higher input
|
||||
* clock than 26 MHz is to use a clock generated by a PLL. Because PLLs
|
||||
* cannot output frequencies greater than 1.75 GHz, the minimum would
|
||||
* never be greater than one.
|
||||
*/
|
||||
max_r = freq_in / DIV_MIN;
|
||||
|
||||
/* Variables get immediately incremented, so start at -1th iteration */
|
||||
f = 0;
|
||||
r = 0;
|
||||
od = 0;
|
||||
best_error = i64::max_value();
|
||||
error = best_error;
|
||||
/* Always try at least one ratio */
|
||||
'outer: loop {
|
||||
/*
|
||||
* Whether we swapped r and od while enforcing frequency limits
|
||||
*/
|
||||
let mut swapped: bool = false;
|
||||
let last_od: u32 = od;
|
||||
let last_r: u32 = r;
|
||||
|
||||
/*
|
||||
* Try the next largest value for f (or r and od) and
|
||||
* recalculate the other parameters based on that
|
||||
*/
|
||||
if freq_out > freq_in {
|
||||
/*
|
||||
* Skip factors of the same product if we already tried
|
||||
* out that product
|
||||
*/
|
||||
while r * od == last_r * last_od {
|
||||
match factors.next() {
|
||||
Some(factor) => {
|
||||
r = unpack_r(&factor);
|
||||
od = unpack_od(&factor);
|
||||
},
|
||||
None => break 'outer,
|
||||
}
|
||||
}
|
||||
|
||||
/* Round close */
|
||||
f = ((((r * od) as u64) * ratio + bit(31)) >> 32) as u32;
|
||||
if f > F_MAX {
|
||||
f = F_MAX;
|
||||
}
|
||||
} else {
|
||||
f += 1;
|
||||
let tmp: u64 = (f as u64) * inv_ratio;
|
||||
let round_up: bool = tmp & bit(31) != 0;
|
||||
let goal: u32 = ((tmp >> 32) as u32) + (round_up as u32);
|
||||
let (err, last_err): (u32, u32);
|
||||
|
||||
/*
|
||||
* Get the next r/od pair in factors. If the last_* pair is better,
|
||||
* then we will use it instead, so don't call next until after we're
|
||||
* sure we won't need this pair.
|
||||
*/
|
||||
loop {
|
||||
match factors.peek() {
|
||||
Some(factor) => {
|
||||
r = unpack_r(factor);
|
||||
od = unpack_od(factor)
|
||||
},
|
||||
None => break 'outer,
|
||||
}
|
||||
|
||||
if r * od < goal {
|
||||
factors.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a case of double rounding. If we rounded up
|
||||
* above, we need to round down (in cases of ties) here.
|
||||
* This prevents off-by-one errors resulting from
|
||||
* choosing X+2 over X when X.Y rounds up to X+1 and
|
||||
* there is no r * od = X+1. For the converse, when X.Y
|
||||
* is rounded down to X, we should choose X+1 over X-1.
|
||||
*/
|
||||
err = abs_diff(r * od, goal);
|
||||
last_err = abs_diff(last_r * last_od, goal);
|
||||
if last_err < err || (round_up && last_err == err) {
|
||||
r = last_r;
|
||||
od = last_od;
|
||||
} else {
|
||||
factors.next();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enforce limits on internal clock frequencies. If we
|
||||
* aren't in spec, try swapping r and od. If everything is
|
||||
* in-spec, calculate the relative error.
|
||||
*/
|
||||
loop {
|
||||
/*
|
||||
* Whether the intermediate frequencies are out-of-spec
|
||||
*/
|
||||
let mut out_of_spec: bool = false;
|
||||
|
||||
if r > max_r {
|
||||
out_of_spec = true;
|
||||
} else {
|
||||
/*
|
||||
* There is no way to only divide once; we need
|
||||
* to examine the frequency with and without the
|
||||
* effect of od.
|
||||
*/
|
||||
let vco: u64 = div_round_closest((freq_in as u64) * (f as u64), r);
|
||||
|
||||
if vco > VCO_MAX || vco < VCO_MIN {
|
||||
out_of_spec = true;
|
||||
}
|
||||
}
|
||||
|
||||
if out_of_spec {
|
||||
if !swapped {
|
||||
let tmp: u32 = r;
|
||||
|
||||
r = od;
|
||||
od = tmp;
|
||||
swapped = true;
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* Try looking ahead to see if there are
|
||||
* additional factors for the same
|
||||
* product.
|
||||
*/
|
||||
match factors.peek() {
|
||||
Some(factor) => {
|
||||
let new_r: u32 = unpack_r(factor);
|
||||
let new_od: u32 = unpack_od(factor);
|
||||
|
||||
if r * od == new_r * new_od {
|
||||
factors.next();
|
||||
r = new_r;
|
||||
od = new_od;
|
||||
swapped = false;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
},
|
||||
None => break 'outer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = div_round_closest((f as u64) * inv_ratio, r * od) as i64;
|
||||
/* The lower 16 bits are spurious */
|
||||
error = i64::abs(error - (bit(32) as i64)) >> 16;
|
||||
|
||||
if error < best_error {
|
||||
best_error = error;
|
||||
best = Some(Params {
|
||||
clkr: (r - 1) as u8,
|
||||
clkf: (f - 1) as u8,
|
||||
clkod: (od - 1) as u8,
|
||||
bwadj: (f - 1) as u8,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if error == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn brute_force_params(freq_in: u32, freq_out: u32) -> Option<Params> {
|
||||
let mut best: Option<Params> = None;
|
||||
let (mut error, mut best_error): (i64, i64);
|
||||
let (max_r, inv_ratio): (u32, u64);
|
||||
|
||||
best_error = i64::max_value();
|
||||
max_r = u32::min(R_MAX, (freq_in / DIV_MIN) as u32);
|
||||
inv_ratio = div_round_closest((freq_in as u64) << 32, freq_out);
|
||||
|
||||
/* Brute force it */
|
||||
for r in R_MIN..=max_r {
|
||||
for f in F_MIN..=F_MAX {
|
||||
for od in OD_MIN..=OD_MAX {
|
||||
let vco: u64 = div_round_closest((freq_in as u64) * (f as u64), r);
|
||||
|
||||
if vco > VCO_MAX || vco < VCO_MIN {
|
||||
continue;
|
||||
}
|
||||
|
||||
error = div_round_closest((f as u64) * inv_ratio, r * od) as i64;
|
||||
/* The lower 16 bits are spurious */
|
||||
error = i64::abs(error - (bit(32) as i64)) >> 16;
|
||||
if error < best_error {
|
||||
best_error = error;
|
||||
best = Some(Params {
|
||||
clkr: (r - 1) as u8,
|
||||
clkf: (f - 1) as u8,
|
||||
clkod: (od - 1) as u8,
|
||||
bwadj: (f - 1) as u8,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
fn params_equal(_a: Option<Params>, _b: Option<Params>) -> bool {
|
||||
match (_a, _b) {
|
||||
(None, None) => true,
|
||||
(Some(_), None) => false,
|
||||
(None, Some(_)) => false,
|
||||
(Some(a), Some(b)) => {
|
||||
let ar: u32 = (a.clkr + 1) as u32;
|
||||
let af: u32 = (a.clkf + 1) as u32;
|
||||
let aod: u32 = (a.clkod + 1) as u32;
|
||||
let br: u32 = (b.clkr + 1) as u32;
|
||||
let bf: u32 = (b.clkf + 1) as u32;
|
||||
let bod: u32 = (b.clkod + 1) as u32;
|
||||
|
||||
af * br * bod == bf * ar * aod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_compute_params(freq_in: u32, freq_out: u32) -> bool {
|
||||
params_equal(compute_params(freq_in, freq_out), brute_force_params(freq_in, freq_out))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_params() {
|
||||
assert_eq!(compute_params(26_000_000, 0), None);
|
||||
assert_eq!(compute_params(0, 390_000_000), None);
|
||||
assert_eq!(compute_params(26_000_000, 2_000_000_000), None);
|
||||
assert_eq!(compute_params(2_000_000_000, 390_000_000), None);
|
||||
assert_eq!(compute_params(20_000_000, 1_500_000_000), None);
|
||||
|
||||
assert!(verify_compute_params(26_000_000, 1_500_000_000));
|
||||
assert!(verify_compute_params(26_000_000, 1_000_000_000));
|
||||
assert!(verify_compute_params(26_000_000, 800_000_000));
|
||||
assert!(verify_compute_params(26_000_000, 700_000_000));
|
||||
assert!(verify_compute_params(26_000_000, 300_000_000));
|
||||
assert!(verify_compute_params(26_000_000, 45_158_400));
|
||||
assert!(verify_compute_params(26_000_000, 27_000_000));
|
||||
assert!(verify_compute_params(27_000_000, 26_000_000));
|
||||
assert!(verify_compute_params(390_000_000, 26_000_000));
|
||||
assert!(verify_compute_params(390_000_000, 383_846_400));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
#![allow(unused)]
|
||||
|
||||
//! Miscelleneous utilities for SoC functions (private)
|
||||
|
||||
pub fn set_bit(inval: u32, bit: u8, state: bool) -> u32 {
|
||||
if state {
|
||||
inval | (1 << u32::from(bit))
|
||||
} else {
|
||||
inval & !(1 << u32::from(bit))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_bit(inval: u32, bit: u8) -> bool {
|
||||
(inval & (1 << u32::from(bit))) != 0
|
||||
}
|
@ -0,0 +1 @@
|
||||
* @rust-embedded/riscv
|
@ -0,0 +1,4 @@
|
||||
block_labels = ["needs-decision"]
|
||||
delete_merged_branches = true
|
||||
required_approvals = 1
|
||||
status = ["continuous-integration/travis-ci/push"]
|
@ -0,0 +1,5 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
bin/*.after
|
||||
bin/*.before
|
||||
bin/*.o
|
@ -0,0 +1,51 @@
|
||||
language: rust
|
||||
|
||||
env:
|
||||
- TARGET=x86_64-unknown-linux-gnu
|
||||
- TARGET=riscv32imac-unknown-none-elf
|
||||
- TARGET=riscv64imac-unknown-none-elf
|
||||
- TARGET=riscv64gc-unknown-none-elf
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- stable
|
||||
- 1.42.0 # MSRV
|
||||
|
||||
if: (branch = staging OR branch = trying OR branch = master) OR (type = pull_request AND branch = master)
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
||||
include:
|
||||
- env: CHECK_BLOBS=1
|
||||
rust:
|
||||
language: bash
|
||||
if: (branch = staging OR branch = trying OR branch = master) OR (type = pull_request AND branch = master)
|
||||
|
||||
- env: RUSTFMT=1
|
||||
rust: stable
|
||||
if: (branch = staging OR branch = trying OR branch = master) OR (type = pull_request AND branch = master)
|
||||
|
||||
|
||||
install:
|
||||
- ci/install.sh
|
||||
|
||||
script:
|
||||
- ci/script.sh
|
||||
|
||||
|
||||
cache:
|
||||
cargo: true
|
||||
directories:
|
||||
- gcc
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
@ -0,0 +1,45 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.6.0] - 2020-06-20
|
||||
|
||||
### Changed
|
||||
|
||||
- `Mtvec::trap_mode()`, `Stvec::trap_mode()` and `Utvec::trap_mode()` functions now return `Option<TrapMode>` (breaking change)
|
||||
- Updated Minimum Supported Rust Version to 1.42.0
|
||||
- Use `llvm_asm!` instead of `asm!`
|
||||
|
||||
### Removed
|
||||
|
||||
- vexriscv-specific registers were moved to the `vexriscv` crate
|
||||
|
||||
## [v0.5.6] - 2020-03-14
|
||||
|
||||
### Added
|
||||
|
||||
- Added vexriscv-specific registers
|
||||
|
||||
## [v0.5.5] - 2020-02-28
|
||||
|
||||
### Added
|
||||
|
||||
- Added `riscv32i-unknown-none-elf` target support
|
||||
- Added user trap setup and handling registers
|
||||
- Added write methods for the `mip` and `satp` registers
|
||||
- Added `mideleg` register
|
||||
- Added Changelog
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed MSRV by restricting the upper bound of `bare-metal` version
|
||||
|
||||
[Unreleased]: https://github.com/rust-embedded/riscv/compare/v0.6.0...HEAD
|
||||
[v0.6.0]: https://github.com/rust-embedded/riscv/compare/v0.5.6...v0.6.0
|
||||
[v0.5.6]: https://github.com/rust-embedded/riscv/compare/v0.5.5...v0.5.6
|
||||
[v0.5.5]: https://github.com/rust-embedded/riscv/compare/v0.5.4...v0.5.5
|
@ -0,0 +1,37 @@
|
||||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [RISC-V team](https://github.com/rust-embedded/wg#the-riscv-team)
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
|
||||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
|
||||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
|
||||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [RISC-V team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
|
||||
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
|
||||
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
|
||||
|
||||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||
|
||||
The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org).
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-riscv-team
|
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "riscv"
|
||||
version = "0.6.0"
|
||||
repository = "https://github.com/rust-embedded/riscv"
|
||||
authors = ["The RISC-V Team <risc-v@teams.rust-embedded.org>"]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
description = "Low level access to RISC-V processors"
|
||||
keywords = ["riscv", "register", "peripheral"]
|
||||
license = "ISC"
|
||||
|
||||
[dependencies]
|
||||
bare-metal = "0.2.5"
|
||||
bitflags = "1.0"
|
||||
bit_field = "0.10.0"
|
||||
log = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
riscv-target = "0.1.2"
|
||||
|
||||
[features]
|
||||
inline-asm = []
|
@ -0,0 +1,41 @@
|
||||
[](https://crates.io/crates/riscv)
|
||||
[](https://crates.io/crates/riscv)
|
||||
[](https://travis-ci.org/rust-embedded/riscv)
|
||||
|
||||
# `riscv`
|
||||
|
||||
> Low level access to RISC-V processors
|
||||
|
||||
This project is developed and maintained by the [RISC-V team][team].
|
||||
|
||||
## [Documentation](https://docs.rs/crate/riscv)
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.42.0 and up. It *might*
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2019-2020 [RISC-V team][team]
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [RISC-V team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-riscv-team
|
@ -0,0 +1,454 @@
|
||||
#include "asm.h"
|
||||
|
||||
.section .text.__ebreak
|
||||
.global __ebreak
|
||||
__ebreak:
|
||||
ebreak
|
||||
ret
|
||||
|
||||
.section .text.__wfi
|
||||
.global __wfi
|
||||
__wfi:
|
||||
wfi
|
||||
ret
|
||||
|
||||
.section .text.__sfence_vma_all
|
||||
.global __sfence_vma_all
|
||||
__sfence_vma_all:
|
||||
sfence.vma
|
||||
ret
|
||||
|
||||
.section .text.__sfence_vma
|
||||
.global __sfence_vma
|
||||
__sfence_vma:
|
||||
sfence.vma a0, a1
|
||||
ret
|
||||
|
||||
// RISC-V hypervisor instructions.
|
||||
|
||||
// The switch for enabling LLVM support for asm generation.
|
||||
// #define LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
|
||||
|
||||
.section .text.__hfence_gvma
|
||||
.global __hfence_gvma
|
||||
__hfence_gvma:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hfence.gvma a0, a1
|
||||
#else
|
||||
.word 1656029299
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hfence_vvma
|
||||
.global __hfence_vvma
|
||||
__hfence_vvma:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hfence.vvma a0, a1
|
||||
#else
|
||||
.word 582287475
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_b
|
||||
.global __hlv_b
|
||||
__hlv_b:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.b a0, a0
|
||||
#else
|
||||
.word 1610958195
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_bu
|
||||
.global __hlv_bu
|
||||
__hlv_bu:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.bu a0, a0
|
||||
#else
|
||||
.word 1612006771
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_h
|
||||
.global __hlv_h
|
||||
__hlv_h:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.h a0, a0
|
||||
#else
|
||||
.word 1678067059
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_hu
|
||||
.global __hlv_hu
|
||||
__hlv_hu:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.hu a0, a0
|
||||
#else
|
||||
.word 1679115635
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlvx_hu
|
||||
.global __hlvx_hu
|
||||
__hlvx_hu:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlvx.hu a0, a0
|
||||
#else
|
||||
.word 1681212787
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_w
|
||||
.global __hlv_w
|
||||
__hlv_w:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.w a0, a0
|
||||
#else
|
||||
.word 1745175923
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlvx_wu
|
||||
.global __hlvx_wu
|
||||
__hlvx_wu:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlvx.wu a0, a0
|
||||
#else
|
||||
.word 1748321651
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hsv_b
|
||||
.global __hsv_b
|
||||
__hsv_b:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hsv.b a0, a1
|
||||
#else
|
||||
.word 1656045683
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hsv_h
|
||||
.global __hsv_h
|
||||
__hsv_h:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hsv.h a0, a1
|
||||
#else
|
||||
.word 1723154547
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hsv_w
|
||||
.global __hsv_w
|
||||
__hsv_w:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hsv.w a0, a1
|
||||
#else
|
||||
.word 1790263411
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_wu
|
||||
.global __hlv_wu
|
||||
__hlv_wu:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.wu a0, a0
|
||||
#else
|
||||
.word 1746224499
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hlv_d
|
||||
.global __hlv_d
|
||||
__hlv_d:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hlv.d a0, a0
|
||||
#else
|
||||
.word 1812284787
|
||||
#endif
|
||||
ret
|
||||
.section .text.__hsv_d
|
||||
.global __hsv_d
|
||||
__hsv_d:
|
||||
#ifdef LLVM_RISCV_HYPERVISOR_EXTENSION_SUPPORT
|
||||
hsv.d a0, a1
|
||||
#else
|
||||
.word 1857372275
|
||||
#endif
|
||||
ret
|
||||
|
||||
|
||||
// User Trap Setup
|
||||
RW(0x000, ustatus) // User status register
|
||||
RW(0x004, uie) // User interrupt-enable register
|
||||
RW(0x005, utvec) // User trap handler base address
|
||||
|
||||
// User Trap Handling
|
||||
RW(0x040, uscratch) // Scratch register for user trap handlers
|
||||
RW(0x041, uepc) // User exception program counter
|
||||
RW(0x042, ucause) // User trap cause
|
||||
RW(0x043, utval) // User bad address or instruction
|
||||
RW(0x044, uip) // User interrupt pending
|
||||
|
||||
// User Floating-Point CSRs
|
||||
RW(0x001, fflags) // Floating-Point Accrued Exceptions
|
||||
RW(0x002, frm) // Floating-Point Dynamic Rounding Mode
|
||||
RW(0x003, fcsr) // Floating-Point Control and Status Register (frm + fflags)
|
||||
|
||||
// User Counter/Timers
|
||||
RO( 0xC00, cycle) // Cycle counter for RDCYCLE instruction
|
||||
RO( 0xC01, time) // Timer for RDTIME instruction
|
||||
RO( 0xC02, instret) // Instructions-retired counter for RDINSTRET instruction
|
||||
RO( 0xC03, hpmcounter3) // Performance-monitoring counter
|
||||
RO( 0xC04, hpmcounter4) // Performance-monitoring counter
|
||||
RO( 0xC05, hpmcounter5) // Performance-monitoring counter
|
||||
RO( 0xC06, hpmcounter6) // Performance-monitoring counter
|
||||
RO( 0xC07, hpmcounter7) // Performance-monitoring counter
|
||||
RO( 0xC08, hpmcounter8) // Performance-monitoring counter
|
||||
RO( 0xC09, hpmcounter9) // Performance-monitoring counter
|
||||
RO( 0xC0A, hpmcounter10) // Performance-monitoring counter
|
||||
RO( 0xC0B, hpmcounter11) // Performance-monitoring counter
|
||||
RO( 0xC0C, hpmcounter12) // Performance-monitoring counter
|
||||
RO( 0xC0D, hpmcounter13) // Performance-monitoring counter
|
||||
RO( 0xC0E, hpmcounter14) // Performance-monitoring counter
|
||||
RO( 0xC0F, hpmcounter15) // Performance-monitoring counter
|
||||
RO( 0xC10, hpmcounter16) // Performance-monitoring counter
|
||||
RO( 0xC11, hpmcounter17) // Performance-monitoring counter
|
||||
RO( 0xC12, hpmcounter18) // Performance-monitoring counter
|
||||
RO( 0xC13, hpmcounter19) // Performance-monitoring counter
|
||||
RO( 0xC14, hpmcounter20) // Performance-monitoring counter
|
||||
RO( 0xC15, hpmcounter21) // Performance-monitoring counter
|
||||
RO( 0xC16, hpmcounter22) // Performance-monitoring counter
|
||||
RO( 0xC17, hpmcounter23) // Performance-monitoring counter
|
||||
RO( 0xC18, hpmcounter24) // Performance-monitoring counter
|
||||
RO( 0xC19, hpmcounter25) // Performance-monitoring counter
|
||||
RO( 0xC1A, hpmcounter26) // Performance-monitoring counter
|
||||
RO( 0xC1B, hpmcounter27) // Performance-monitoring counter
|
||||
RO( 0xC1C, hpmcounter28) // Performance-monitoring counter
|
||||
RO( 0xC1D, hpmcounter29) // Performance-monitoring counter
|
||||
RO( 0xC1E, hpmcounter30) // Performance-monitoring counter
|
||||
RO( 0xC1F, hpmcounter31) // Performance-monitoring counter
|
||||
RO32(0xC80, cycleh) // Upper 32 bits of cycle, RV32I only
|
||||
RO32(0xC81, timeh) // Upper 32 bits of time, RV32I only
|
||||
RO32(0xC82, instreth) // Upper 32 bits of instret, RV32I only
|
||||
RO32(0xC83, hpmcounter3h) // Upper 32 bits of hpmcounter3, RV32I only
|
||||
RO32(0xC84, hpmcounter4h)
|
||||
RO32(0xC85, hpmcounter5h)
|
||||
RO32(0xC86, hpmcounter6h)
|
||||
RO32(0xC87, hpmcounter7h)
|
||||
RO32(0xC88, hpmcounter8h)
|
||||
RO32(0xC89, hpmcounter9h)
|
||||
RO32(0xC8A, hpmcounter10h)
|
||||
RO32(0xC8B, hpmcounter11h)
|
||||
RO32(0xC8C, hpmcounter12h)
|
||||
RO32(0xC8D, hpmcounter13h)
|
||||
RO32(0xC8E, hpmcounter14h)
|
||||
RO32(0xC8F, hpmcounter15h)
|
||||
RO32(0xC90, hpmcounter16h)
|
||||
RO32(0xC91, hpmcounter17h)
|
||||
RO32(0xC92, hpmcounter18h)
|
||||
RO32(0xC93, hpmcounter19h)
|
||||
RO32(0xC94, hpmcounter20h)
|
||||
RO32(0xC95, hpmcounter21h)
|
||||
RO32(0xC96, hpmcounter22h)
|
||||
RO32(0xC97, hpmcounter23h)
|
||||
RO32(0xC98, hpmcounter24h)
|
||||
RO32(0xC99, hpmcounter25h)
|
||||
RO32(0xC9A, hpmcounter26h)
|
||||
RO32(0xC9B, hpmcounter27h)
|
||||
RO32(0xC9C, hpmcounter28h)
|
||||
RO32(0xC9D, hpmcounter29h)
|
||||
RO32(0xC9E, hpmcounter30h)
|
||||
RO32(0xC9F, hpmcounter31h)
|
||||
|
||||
// Supervisor Trap Setup
|
||||
RW(0x100, sstatus) // Supervisor status register
|
||||
RW(0x102, sedeleg) // Supervisor exception delegation register
|
||||
RW(0x103, sideleg) // Supervisor interrupt delegation register
|
||||
RW(0x104, sie) // Supervisor interrupt-enable register
|
||||
RW(0x105, stvec) // Supervisor trap handler base address
|
||||
RW(0x106, scounteren) // Supervisor counter enable
|
||||
|
||||
// Supervisor Trap Handling
|
||||
RW(0x140, sscratch) // Scratch register for supervisor trap handlers
|
||||
RW(0x141, sepc) // Supervisor exception program counter
|
||||
RW(0x142, scause) // Supervisor trap cause
|
||||
RW(0x143, stval) // Supervisor bad address or instruction
|
||||
RW(0x144, sip) // Supervisor interrupt pending
|
||||
|
||||
// Supervisor Protection and Translation
|
||||
RW(0x180, satp) // Supervisor address translation and protection
|
||||
|
||||
// Machine Information Registers
|
||||
RO(0xF11, mvendorid) // Vendor ID
|
||||
RO(0xF12, marchid) // Architecture ID
|
||||
RO(0xF13, mimpid) // Implementation ID
|
||||
RO(0xF14, mhartid) // Hardware thread ID
|
||||
|
||||
// Machine Trap Setup
|
||||
RW(0x300, mstatus) // Machine status register
|
||||
RW(0x301, misa) // ISA and extensions
|
||||
RW(0x302, medeleg) // Machine exception delegation register
|
||||
RW(0x303, mideleg) // Machine interrupt delegation register
|
||||
RW(0x304, mie) // Machine interrupt-enable register
|
||||
RW(0x305, mtvec) // Machine trap handler base address
|
||||
RW(0x306, mcounteren) // Machine counter enable
|
||||
|
||||
// Machine Trap Handling
|
||||
RW(0x340, mscratch) // Scratch register for machine trap handlers
|
||||
RW(0x341, mepc) // Machine exception program counter
|
||||
RW(0x342, mcause) // Machine trap cause
|
||||
RW(0x343, mtval) // Machine bad address or instruction
|
||||
RW(0x344, mip) // Machine interrupt pending
|
||||
|
||||
// Machine Protection and Translation
|
||||
RW( 0x3A0, pmpcfg0) // Physical memory protection configuration
|
||||
RW32(0x3A1, pmpcfg1) // Physical memory protection configuration, RV32 only
|
||||
RW( 0x3A2, pmpcfg2) // Physical memory protection configuration
|
||||
RW32(0x3A3, pmpcfg3) // Physical memory protection configuration, RV32 only
|
||||
RW( 0x3B0, pmpaddr0) // Physical memory protection address register
|
||||
RW( 0x3B1, pmpaddr1) // Physical memory protection address register
|
||||
RW( 0x3B2, pmpaddr2) // Physical memory protection address register
|
||||
RW( 0x3B3, pmpaddr3) // Physical memory protection address register
|
||||
RW( 0x3B4, pmpaddr4) // Physical memory protection address register
|
||||
RW( 0x3B5, pmpaddr5) // Physical memory protection address register
|
||||
RW( 0x3B6, pmpaddr6) // Physical memory protection address register
|
||||
RW( 0x3B7, pmpaddr7) // Physical memory protection address register
|
||||
RW( 0x3B8, pmpaddr8) // Physical memory protection address register
|
||||
RW( 0x3B9, pmpaddr9) // Physical memory protection address register
|
||||
RW( 0x3BA, pmpaddr10) // Physical memory protection address register
|
||||
RW( 0x3BB, pmpaddr11) // Physical memory protection address register
|
||||
RW( 0x3BC, pmpaddr12) // Physical memory protection address register
|
||||
RW( 0x3BD, pmpaddr13) // Physical memory protection address register
|
||||
RW( 0x3BE, pmpaddr14) // Physical memory protection address register
|
||||
RW( 0x3BF, pmpaddr15) // Physical memory protection address register
|
||||
|
||||
// Machine Counter/Timers
|
||||
RO( 0xB00, mcycle) // Machine cycle counter
|
||||
RO( 0xB02, minstret) // Machine instructions-retired counter
|
||||
RO( 0xB03, mhpmcounter3) // Machine performance-monitoring counter
|
||||
RO( 0xB04, mhpmcounter4) // Machine performance-monitoring counter
|
||||
RO( 0xB05, mhpmcounter5) // Machine performance-monitoring counter
|
||||
RO( 0xB06, mhpmcounter6) // Machine performance-monitoring counter
|
||||
RO( 0xB07, mhpmcounter7) // Machine performance-monitoring counter
|
||||
RO( 0xB08, mhpmcounter8) // Machine performance-monitoring counter
|
||||
RO( 0xB09, mhpmcounter9) // Machine performance-monitoring counter
|
||||
RO( 0xB0A, mhpmcounter10) // Machine performance-monitoring counter
|
||||
RO( 0xB0B, mhpmcounter11) // Machine performance-monitoring counter
|
||||
RO( 0xB0C, mhpmcounter12) // Machine performance-monitoring counter
|
||||
RO( 0xB0D, mhpmcounter13) // Machine performance-monitoring counter
|
||||
RO( 0xB0E, mhpmcounter14) // Machine performance-monitoring counter
|
||||
RO( 0xB0F, mhpmcounter15) // Machine performance-monitoring counter
|
||||
RO( 0xB10, mhpmcounter16) // Machine performance-monitoring counter
|
||||
RO( 0xB11, mhpmcounter17) // Machine performance-monitoring counter
|
||||
RO( 0xB12, mhpmcounter18) // Machine performance-monitoring counter
|
||||
RO( 0xB13, mhpmcounter19) // Machine performance-monitoring counter
|
||||
RO( 0xB14, mhpmcounter20) // Machine performance-monitoring counter
|
||||
RO( 0xB15, mhpmcounter21) // Machine performance-monitoring counter
|
||||
RO( 0xB16, mhpmcounter22) // Machine performance-monitoring counter
|
||||
RO( 0xB17, mhpmcounter23) // Machine performance-monitoring counter
|
||||
RO( 0xB18, mhpmcounter24) // Machine performance-monitoring counter
|
||||
RO( 0xB19, mhpmcounter25) // Machine performance-monitoring counter
|
||||
RO( 0xB1A, mhpmcounter26) // Machine performance-monitoring counter
|
||||
RO( 0xB1B, mhpmcounter27) // Machine performance-monitoring counter
|
||||
RO( 0xB1C, mhpmcounter28) // Machine performance-monitoring counter
|
||||
RO( 0xB1D, mhpmcounter29) // Machine performance-monitoring counter
|
||||
RO( 0xB1E, mhpmcounter30) // Machine performance-monitoring counter
|
||||
RO( 0xB1F, mhpmcounter31) // Machine performance-monitoring counter
|
||||
RO32(0xB80, mcycleh) // Upper 32 bits of mcycle, RV32I only
|
||||
RO32(0xB82, minstreth) // Upper 32 bits of minstret, RV32I only
|
||||
RO32(0xB83, mhpmcounter3h) // Upper 32 bits of mhpmcounter3, RV32I only
|
||||
RO32(0xB84, mhpmcounter4h)
|
||||
RO32(0xB85, mhpmcounter5h)
|
||||
RO32(0xB86, mhpmcounter6h)
|
||||
RO32(0xB87, mhpmcounter7h)
|
||||
RO32(0xB88, mhpmcounter8h)
|
||||
RO32(0xB89, mhpmcounter9h)
|
||||
RO32(0xB8A, mhpmcounter10h)
|
||||
RO32(0xB8B, mhpmcounter11h)
|
||||
RO32(0xB8C, mhpmcounter12h)
|
||||
RO32(0xB8D, mhpmcounter13h)
|
||||
RO32(0xB8E, mhpmcounter14h)
|
||||
RO32(0xB8F, mhpmcounter15h)
|
||||
RO32(0xB90, mhpmcounter16h)
|
||||
RO32(0xB91, mhpmcounter17h)
|
||||
RO32(0xB92, mhpmcounter18h)
|
||||
RO32(0xB93, mhpmcounter19h)
|
||||
RO32(0xB94, mhpmcounter20h)
|
||||
RO32(0xB95, mhpmcounter21h)
|
||||
RO32(0xB96, mhpmcounter22h)
|
||||
RO32(0xB97, mhpmcounter23h)
|
||||
RO32(0xB98, mhpmcounter24h)
|
||||
RO32(0xB99, mhpmcounter25h)
|
||||
RO32(0xB9A, mhpmcounter26h)
|
||||
RO32(0xB9B, mhpmcounter27h)
|
||||
RO32(0xB9C, mhpmcounter28h)
|
||||
RO32(0xB9D, mhpmcounter29h)
|
||||
RO32(0xB9E, mhpmcounter30h)
|
||||
RO32(0xB9F, mhpmcounter31h)
|
||||
|
||||
RW(0x323, mhpmevent3) // Machine performance-monitoring event selector
|
||||
RW(0x324, mhpmevent4) // Machine performance-monitoring event selector
|
||||
RW(0x325, mhpmevent5) // Machine performance-monitoring event selector
|
||||
RW(0x326, mhpmevent6) // Machine performance-monitoring event selector
|
||||
RW(0x327, mhpmevent7) // Machine performance-monitoring event selector
|
||||
RW(0x328, mhpmevent8) // Machine performance-monitoring event selector
|
||||
RW(0x329, mhpmevent9) // Machine performance-monitoring event selector
|
||||
RW(0x32A, mhpmevent10) // Machine performance-monitoring event selector
|
||||
RW(0x32B, mhpmevent11) // Machine performance-monitoring event selector
|
||||
RW(0x32C, mhpmevent12) // Machine performance-monitoring event selector
|
||||
RW(0x32D, mhpmevent13) // Machine performance-monitoring event selector
|
||||
RW(0x32E, mhpmevent14) // Machine performance-monitoring event selector
|
||||
RW(0x32F, mhpmevent15) // Machine performance-monitoring event selector
|
||||
RW(0x330, mhpmevent16) // Machine performance-monitoring event selector
|
||||
RW(0x331, mhpmevent17) // Machine performance-monitoring event selector
|
||||
RW(0x332, mhpmevent18) // Machine performance-monitoring event selector
|
||||
RW(0x333, mhpmevent19) // Machine performance-monitoring event selector
|
||||
RW(0x334, mhpmevent20) // Machine performance-monitoring event selector
|
||||
RW(0x335, mhpmevent21) // Machine performance-monitoring event selector
|
||||
RW(0x336, mhpmevent22) // Machine performance-monitoring event selector
|
||||
RW(0x337, mhpmevent23) // Machine performance-monitoring event selector
|
||||
RW(0x338, mhpmevent24) // Machine performance-monitoring event selector
|
||||
RW(0x339, mhpmevent25) // Machine performance-monitoring event selector
|
||||
RW(0x33A, mhpmevent26) // Machine performance-monitoring event selector
|
||||
RW(0x33B, mhpmevent27) // Machine performance-monitoring event selector
|
||||
RW(0x33C, mhpmevent28) // Machine performance-monitoring event selector
|
||||
RW(0x33D, mhpmevent29) // Machine performance-monitoring event selector
|
||||
RW(0x33E, mhpmevent30) // Machine performance-monitoring event selector
|
||||
RW(0x33F, mhpmevent31) // Machine performance-monitoring event selector
|
||||
|
||||
// Debug/Trace Registers (shared with Debug Mode)
|
||||
RW(0x7A0, tselect) // Debug/Trace trigger register select
|
||||
RW(0x7A1, tdata1) // First Debug/Trace trigger data register
|
||||
RW(0x7A2, tdata2) // Second Debug/Trace trigger data register
|
||||
RW(0x7A3, tdata3) // Third Debug/Trace trigger data register
|
||||
|
||||
// Debug Mode Registers
|
||||
RW(0x7B0, dcsr) // Debug control and status register
|
||||
RW(0x7B1, dpc) // Debug PC
|
||||
RW(0x7B2, dscratch) // Debug scratch register
|
||||
|
||||
// Hypervisor Trap Setup
|
||||
RW(0x600, hstatus) // Hypervisor status register
|
||||
RW(0x602, hedeleg) // Hypervisor exception delegation register
|
||||
RW(0x603, hideleg) // Hypervisor interrupt delegation register
|
||||
RW(0x604, hie) // Hypervisor interrupt-enable register
|
||||
RW(0x606, hcounteren) // Hypervisor counter enable
|
||||
RW(0x607, hgeie) // Hypervisor guest external interrupt-enable register
|
||||
|
||||
// Hypervisor Trap Handling
|
||||
RW(0x643, htval) // Hypervisor bad guest physical address
|
||||
RW(0x644, hip) // Hypervisor interrupt pending
|
||||
RW(0x645, hvip) // Hypervisor virtual interrupt pending
|
||||
RW(0x64a, htinst) // Hypervisor trap instruction (transformed)
|
||||
RW(0xe12, hgeip) // Hypervisor guest external interrupt pending
|
||||
|
||||
// Hypervisor Protection and Translation
|
||||
RO(0x680, hgatp) // Hypervisor guest address translation and protection
|
||||
|
||||
// Debug/Trace Registers
|
||||
RW(0x6a8, hcontext) // Hypervisor-mode context register
|
||||
|
||||
// Hypervisor Counter/Timer Virtualization Registers
|
||||
RW(0x605, htimedelta) // Delta for VS/VU-mode timer
|
||||
RW32(0x615, htimedeltah) // Upper 32 bits of {\tt htimedelta}, RV32 only
|
||||
|
||||
// Virtual Supervisor Registers
|
||||
RW(0x200, vsstatus) // Virtual supervisor status register
|
||||
RW(0x204, vsie) // Virtual supervisor interrupt-enable register
|
||||
RW(0x205, vstvec) // Virtual supervisor trap handler base address
|
||||
RW(0x240, vsscratch) // Virtual supervisor scratch register
|
||||
RW(0x241, vsepc) // Virtual supervisor exception program counter
|
||||
RW(0x242, vscause) // Virtual supervisor trap cause
|
||||
RW(0x243, vstval) // Virtual supervisor bad address or instruction
|
||||
RW(0x244, vsip) // Virtual supervisor interrupt pending
|
||||
RW(0x280, vsatp) // Virtual supervisor address translation and protection
|
@ -0,0 +1,48 @@
|
||||
#ifndef __ASM_H
|
||||
#define __ASM_H
|
||||
|
||||
#define REG_READ(name, offset) \
|
||||
.section .text.__read_ ## name; \
|
||||
.global __read_ ## name; \
|
||||
__read_ ## name: \
|
||||
csrrs a0, offset, x0; \
|
||||
ret
|
||||
|
||||
#define REG_WRITE(name, offset) \
|
||||
.section .text.__write_ ## name; \
|
||||
.global __write_ ## name; \
|
||||
__write_ ## name: \
|
||||
csrrw x0, offset, a0; \
|
||||
ret
|
||||
|
||||
#define REG_SET(name, offset) \
|
||||
.section .text.__set_ ## name; \
|
||||
.global __set_ ## name; \
|
||||
__set_ ## name: \
|
||||
csrrs x0, offset, a0; \
|
||||
ret
|
||||
|
||||
#define REG_CLEAR(name, offset) \
|
||||
.section .text.__clear_ ## name; \
|
||||
.global __clear_ ## name; \
|
||||
__clear_ ## name: \
|
||||
csrrc x0, offset, a0; \
|
||||
ret
|
||||
|
||||
|
||||
#define REG_READ_WRITE(name, offset) REG_READ(name, offset); REG_WRITE(name, offset)
|
||||
#define REG_SET_CLEAR(name, offset) REG_SET(name, offset); REG_CLEAR(name, offset)
|
||||
|
||||
#define RW(offset, name) REG_READ_WRITE(name, offset); REG_SET_CLEAR(name, offset)
|
||||
#define RO(offset, name) REG_READ(name, offset)
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
#define RW32(offset, name) RW(offset, name)
|
||||
#define RO32(offset, name) RO(offset, name)
|
||||
#else
|
||||
#define RW32(offset, name)
|
||||
#define RO32(offset, name)
|
||||
#endif
|
||||
|
||||
#endif /* __ASM_H */
|
||||
|
@ -0,0 +1,20 @@
|
||||
New-Item -Force -Name bin -Type Directory
|
||||
|
||||
# remove existing blobs because otherwise this will append object files to the old blobs
|
||||
Remove-Item -Force bin/*.a
|
||||
|
||||
$crate = "riscv"
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32i asm.S -o bin/$crate.o
|
||||
riscv64-unknown-elf-ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32ic asm.S -o bin/$crate.o
|
||||
riscv64-unknown-elf-ar crs bin/riscv32ic-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=lp64 -march=rv64i asm.S -o bin/$crate.o
|
||||
riscv64-unknown-elf-ar crs bin/riscv64i-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=lp64 -march=rv64ic asm.S -o bin/$crate.o
|
||||
riscv64-unknown-elf-ar crs bin/riscv64ic-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
Remove-Item bin/$crate.o
|
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
crate=riscv
|
||||
|
||||
# remove existing blobs because otherwise this will append object files to the old blobs
|
||||
rm -f bin/*.a
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32i asm.S -o bin/$crate.o
|
||||
ar crs bin/riscv32i-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=ilp32 -march=rv32ic asm.S -o bin/$crate.o
|
||||
ar crs bin/riscv32ic-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=lp64 -march=rv64i asm.S -o bin/$crate.o
|
||||
ar crs bin/riscv64i-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
riscv64-unknown-elf-gcc -c -mabi=lp64 -march=rv64ic asm.S -o bin/$crate.o
|
||||
ar crs bin/riscv64ic-unknown-none-elf.a bin/$crate.o
|
||||
|
||||
rm bin/$crate.o
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,35 @@
|
||||
extern crate riscv_target;
|
||||
|
||||
use riscv_target::Target;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let name = env::var("CARGO_PKG_NAME").unwrap();
|
||||
|
||||
if target.starts_with("riscv") && env::var_os("CARGO_FEATURE_INLINE_ASM").is_none() {
|
||||
let mut target = Target::from_target_str(&target);
|
||||
target.retain_extensions("ic");
|
||||
|
||||
let target = target.to_string();
|
||||
|
||||
fs::copy(
|
||||
format!("bin/{}.a", target),
|
||||
out_dir.join(format!("lib{}.a", name)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("cargo:rustc-link-lib=static={}", name);
|
||||
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||
}
|
||||
|
||||
if target.contains("riscv32") {
|
||||
println!("cargo:rustc-cfg=riscv");
|
||||
println!("cargo:rustc-cfg=riscv32");
|
||||
} else if target.contains("riscv64") {
|
||||
println!("cargo:rustc-cfg=riscv");
|
||||
println!("cargo:rustc-cfg=riscv64");
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Checks that the blobs are up to date with the committed assembly files
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
for lib in $(ls bin/*.a); do
|
||||
filename=$(basename $lib)
|
||||
riscv64-unknown-elf-objdump -Cd $lib > bin/${filename%.a}.before
|
||||
done
|
||||
|
||||
./assemble.sh
|
||||
|
||||
for lib in $(ls bin/*.a); do
|
||||
filename=$(basename $lib)
|
||||
riscv64-unknown-elf-objdump -Cd $lib > bin/${filename%.a}.after
|
||||
done
|
||||
|
||||
for cksum in $(ls bin/*.after); do
|
||||
diff -u $cksum ${cksum%.after}.before
|
||||
done
|
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
if [ -n "${TARGET:-}" ]; then
|
||||
rustup target add $TARGET
|
||||
fi
|
||||
|
||||
if [ -n "${CHECK_BLOBS:-}" ]; then
|
||||
if [ ! -d gcc/bin ]; then
|
||||
mkdir -p gcc
|
||||
curl -L https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.1.0-2018.12.0-x86_64-linux-ubuntu14.tar.gz | tar --strip-components=1 -C gcc -xz
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${RUSTFMT:-}" ]; then
|
||||
rustup component add rustfmt
|
||||
fi
|
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
export PATH=$PATH:~/.cargo/bin
|
||||
|
||||
if [ -n "${TARGET:-}" ]; then
|
||||
cargo check --target $TARGET
|
||||
|
||||
if [ $TRAVIS_RUST_VERSION = nightly ]; then
|
||||
cargo check --target $TARGET --features inline-asm
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "${CHECK_BLOBS:-}" ]; then
|
||||
PATH="$PATH:$PWD/gcc/bin"
|
||||
./check-blobs.sh
|
||||
fi
|
||||
|
||||
if [ -n "${RUSTFMT:-}" ]; then
|
||||
cargo fmt -- --check
|
||||
fi
|
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
rustc generator.rs
|
||||
rm -f ../src/register/hypervisorx64/mod.rs;
|
||||
for i in *.txt; do
|
||||
./generator <$i > ../src/register/hypervisorx64/`basename -s .txt $i`.rs;
|
||||
echo "pub mod $(basename -s .txt $i);" >> ../src/register/hypervisorx64/mod.rs;
|
||||
done
|
||||
rm -f generator
|
@ -0,0 +1,355 @@
|
||||
use std::fmt::*;
|
||||
macro_rules! CSR_ACCESSOR {
|
||||
() => {
|
||||
r#"
|
||||
pub mod csr {{
|
||||
pub const CSR_ID: usize = {};
|
||||
#[inline]
|
||||
pub unsafe fn csrrw(rs1: usize)->usize{{
|
||||
let mut rd;
|
||||
llvm_asm!("csrrw $0, $2, $1" :"=r"(rd): "r"(rs1), "i"(CSR_ID) :: "volatile");
|
||||
rd
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn csrrw_x0(rs1: usize){{
|
||||
llvm_asm!("csrrw x0, $1, $0" :: "r"(rs1), "i"(CSR_ID) :: "volatile");
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn csrrs(rs1: usize)->usize{{
|
||||
let mut rd;
|
||||
llvm_asm!("csrrs $0, $2, $1" :"=r"(rd): "r"(rs1), "i"(CSR_ID) :: "volatile");
|
||||
rd
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn csrrs_x0()->usize{{
|
||||
let mut rd;
|
||||
llvm_asm!("csrrs $0, $1, x0" :"=r"(rd): "i"(CSR_ID) :: "volatile");
|
||||
rd
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn csrrc(rs1: usize)->usize{{
|
||||
let mut rd;
|
||||
llvm_asm!("csrrc $0, $2, $1" :"=r"(rd): "r"(rs1), "i"(CSR_ID) :: "volatile");
|
||||
rd
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn csrrc_x0()->usize{{
|
||||
let mut rd;
|
||||
llvm_asm!("csrrc $0, $1, x0" :"=r"(rd): "i"(CSR_ID) :: "volatile");
|
||||
rd
|
||||
}}
|
||||
}}
|
||||
"#
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! as_str_polyfill {
|
||||
($x: expr, $r: expr) => {{
|
||||
let mut y = $x.clone();
|
||||
if let Some(x) = y.next() {
|
||||
$r.split_at(x.as_ptr() as usize - $r.as_ptr() as usize).1
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}};
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
struct EnumerationDescriptor<'a> {
|
||||
enumerations: Vec<(&'a str, usize)>,
|
||||
}
|
||||
impl<'a> EnumerationDescriptor<'a> {
|
||||
pub fn parse(enums: &'a str) -> Self {
|
||||
let mut counter = 0;
|
||||
let list = enums.split(";");
|
||||
let mut e = Vec::new();
|
||||
for tup in list {
|
||||
let mut t = tup.split("=");
|
||||
let n = t.next().unwrap();
|
||||
if let Some(new_id) = t.next() {
|
||||
counter = new_id.parse().unwrap();
|
||||
}
|
||||
e.push((n, counter));
|
||||
counter += 1;
|
||||
}
|
||||
EnumerationDescriptor { enumerations: e }
|
||||
}
|
||||
fn generate_enum(&self, name: &str) -> String {
|
||||
let mut ret = String::new();
|
||||
write!(
|
||||
&mut ret,
|
||||
"#[derive(Copy, Clone, Debug)]
|
||||
#[repr(usize)]
|
||||
"
|
||||
)
|
||||
.unwrap();
|
||||
write!(&mut ret, "pub enum {}{{\n", name).unwrap();
|
||||
let mut branches = String::new();
|
||||
for e in self.enumerations.iter() {
|
||||
write!(&mut ret, " {} = {},\n", e.0, e.1).unwrap();
|
||||
write!(&mut branches, " {} => Self::{},\n", e.1, e.0).unwrap();
|
||||
}
|
||||
|
||||
write!(
|
||||
&mut ret,
|
||||
"}}
|
||||
impl {}{{
|
||||
fn from(x: usize)->Self{{
|
||||
match x{{
|
||||
{} _ => unreachable!()
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
",
|
||||
name, branches
|
||||
)
|
||||
.unwrap();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
struct BitFieldDescriptor<'a> {
|
||||
name: &'a str,
|
||||
description: &'a str,
|
||||
lo: usize,
|
||||
hi: usize,
|
||||
ed: Option<(&'a str, EnumerationDescriptor<'a>)>,
|
||||
}
|
||||
|
||||
impl<'a> BitFieldDescriptor<'a> {
|
||||
pub fn parse(desc: &'a str) -> Self {
|
||||
let mut parts = desc.split(",");
|
||||
let name = parts.next().unwrap();
|
||||
let hi = parts.next().unwrap().parse::<usize>().unwrap();
|
||||
let lo = parts.next().unwrap().parse::<usize>().unwrap();
|
||||
let (lo, hi) = if lo < hi { (lo, hi) } else { (hi, lo) };
|
||||
let use_enum = parts.next().unwrap();
|
||||
let ed = if use_enum != "number" {
|
||||
let opts = parts.next().unwrap();
|
||||
Some((use_enum, EnumerationDescriptor::parse(opts)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let description = as_str_polyfill!(parts, desc);
|
||||
BitFieldDescriptor {
|
||||
name,
|
||||
lo,
|
||||
hi,
|
||||
description,
|
||||
ed,
|
||||
}
|
||||
}
|
||||
pub fn generate_enum(&self) -> Option<String> {
|
||||
if let Some((n, e)) = &self.ed {
|
||||
Some(e.generate_enum(n))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn flag_type(&self) -> &str {
|
||||
if let Some((n, _)) = self.ed {
|
||||
n
|
||||
} else {
|
||||
if self.lo == self.hi {
|
||||
"bool"
|
||||
} else {
|
||||
"usize"
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mask(&self) -> String {
|
||||
format!("{}", (1usize << (self.hi - self.lo + 1)) - 1)
|
||||
}
|
||||
fn getter(&self) -> String {
|
||||
if self.lo == self.hi {
|
||||
return format!("self.bits.get_bit({})", self.lo);
|
||||
} else if self.flag_type() != "usize" {
|
||||
return format!(
|
||||
"{}::from(self.bits.get_bits({}..{}))",
|
||||
self.flag_type(),
|
||||
self.lo,
|
||||
self.hi + 1
|
||||
);
|
||||
} else {
|
||||
return format!("self.bits.get_bits({}..{})", self.lo, self.hi + 1);
|
||||
}
|
||||
}
|
||||
fn setter(&self) -> String {
|
||||
if self.lo == self.hi {
|
||||
return format!("self.bits.set_bit({}, val);", self.lo);
|
||||
} else if self.flag_type() != "usize" {
|
||||
return format!(
|
||||
"self.bits.set_bits({}..{}, val as usize);",
|
||||
self.lo,
|
||||
self.hi + 1
|
||||
);
|
||||
} else {
|
||||
return format!("self.bits.set_bits({}..{}, val);", self.lo, self.hi + 1);
|
||||
}
|
||||
}
|
||||
fn generate_read_write(&self) -> String {
|
||||
format!(
|
||||
" /// {}
|
||||
#[inline]
|
||||
pub fn {}(&self)->{}{{
|
||||
{}
|
||||
}}
|
||||
#[inline]
|
||||
pub fn set_{}(&mut self, val: {}){{
|
||||
{}
|
||||
}}\n",
|
||||
self.description,
|
||||
self.name,
|
||||
self.flag_type(),
|
||||
self.getter(),
|
||||
self.name,
|
||||
self.flag_type(),
|
||||
self.setter()
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_bit_set(&self) -> String {
|
||||
format!(
|
||||
" pub fn set_{}()->bool{{
|
||||
unsafe {{csr::csrrc({}) & {} !=0}}
|
||||
}}
|
||||
pub fn clear_{}()->bool{{
|
||||
unsafe {{csr::csrrs({}) & {} !=0 }}
|
||||
}}\n",
|
||||
self.name,
|
||||
1usize << self.lo,
|
||||
1usize << self.lo,
|
||||
self.name,
|
||||
1usize << self.lo,
|
||||
1usize << self.lo
|
||||
)
|
||||
}
|
||||
fn generate_bitops(&self) -> String {
|
||||
format!(
|
||||
" set_clear_csr!(
|
||||
///{}
|
||||
, set_{}, clear_{}, 1 << {});\n",
|
||||
self.description, self.name, self.name, self.lo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CSRDescriptor<'a> {
|
||||
name: &'a str,
|
||||
id: usize,
|
||||
description: &'a str,
|
||||
bfs: Vec<BitFieldDescriptor<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CSRDescriptor<'a> {
|
||||
fn canonical_name(&self) -> String {
|
||||
self.name.to_lowercase()
|
||||
}
|
||||
pub fn parse(d: &'a str) -> Self {
|
||||
let mut parts = d.split("\n");
|
||||
let name = parts.next().unwrap();
|
||||
let id = parts.next().unwrap().parse::<usize>().unwrap();
|
||||
let mut bfs = Vec::new();
|
||||
while let Some(x) = parts.next() {
|
||||
if x == "end" {
|
||||
break;
|
||||
} else {
|
||||
bfs.push(BitFieldDescriptor::parse(x));
|
||||
}
|
||||
}
|
||||
CSRDescriptor {
|
||||
name,
|
||||
id,
|
||||
description: as_str_polyfill!(parts, d),
|
||||
bfs,
|
||||
}
|
||||
}
|
||||
pub fn generate(&self) -> String {
|
||||
let mut trait_impls = String::new();
|
||||
let mut bit_sets = String::new();
|
||||
let mut enums = String::new();
|
||||
for bf in self.bfs.iter() {
|
||||
if bf.lo == bf.hi {
|
||||
write!(&mut bit_sets, "{}", bf.generate_bitops()).unwrap();
|
||||
//write!(&mut trait_impls, "{}",bf.generate_bit_set()).unwrap();
|
||||
}
|
||||
write!(&mut trait_impls, "{}", bf.generate_read_write()).unwrap();
|
||||
if let Some(x) = bf.generate_enum() {
|
||||
write!(&mut enums, "{}", x).unwrap();
|
||||
}
|
||||
}
|
||||
if &trait_impls == "" && &bit_sets == "" {
|
||||
format!(
|
||||
"
|
||||
//! {}
|
||||
read_csr_as_usize!({}, __read_{});
|
||||
write_csr_as_usize!({}, __write_{});
|
||||
",
|
||||
self.description,
|
||||
self.id,
|
||||
self.canonical_name(),
|
||||
self.id,
|
||||
self.canonical_name()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"
|
||||
//! {}
|
||||
|
||||
use bit_field::BitField;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct {}{{\n bits: usize,\n}}
|
||||
impl {}{{
|
||||
#[inline]
|
||||
pub fn bits(&self) -> usize{{
|
||||
return self.bits;
|
||||
}}
|
||||
#[inline]
|
||||
pub fn from_bits(x: usize) -> Self{{
|
||||
return {}{{bits: x}};
|
||||
}}
|
||||
#[inline]
|
||||
pub unsafe fn write(&self){{
|
||||
_write(self.bits);
|
||||
}}
|
||||
{}
|
||||
}}
|
||||
read_csr_as!({}, {}, __read_{});
|
||||
write_csr!({}, __write_{});
|
||||
set!({}, __set_{});
|
||||
clear!({}, __clear_{});
|
||||
// bit ops
|
||||
{}
|
||||
// enums
|
||||
{}
|
||||
|
||||
",
|
||||
self.description,
|
||||
self.name,
|
||||
self.name,
|
||||
self.name,
|
||||
trait_impls,
|
||||
self.name,
|
||||
self.id,
|
||||
self.canonical_name(),
|
||||
self.id,
|
||||
self.canonical_name(),
|
||||
self.id,
|
||||
self.canonical_name(),
|
||||
self.id,
|
||||
self.canonical_name(),
|
||||
bit_sets,
|
||||
enums,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::io::Read;
|
||||
let mut buffer = String::new();
|
||||
std::io::stdin().read_to_string(&mut buffer).unwrap();
|
||||
let csr = CSRDescriptor::parse(&buffer);
|
||||
println!("{}", csr.generate());
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
Hcounteren
|
||||
3602
|
||||
cy,0,0,number,
|
||||
tm,1,1,number,
|
||||
ir,2,2,number,
|
||||
hpm3,3,3,number,
|
||||
hpm4,4,4,number,
|
||||
hpm5,5,5,number,
|
||||
hpm6,6,6,number,
|
||||
hpm7,7,7,number,
|
||||
hpm8,8,8,number,
|
||||
hpm9,9,9,number,
|
||||
hpm10,10,10,number,
|
||||
hpm11,11,11,number,
|
||||
hpm12,12,12,number,
|
||||
hpm13,13,13,number,
|
||||
hpm14,14,14,number,
|
||||
hpm15,15,15,number,
|
||||
hpm16,16,16,number,
|
||||
hpm17,17,17,number,
|
||||
hpm18,18,18,number,
|
||||
hpm19,19,19,number,
|
||||
hpm20,20,20,number,
|
||||
hpm21,21,21,number,
|
||||
hpm22,22,22,number,
|
||||
hpm23,23,23,number,
|
||||
hpm24,24,24,number,
|
||||
hpm25,25,25,number,
|
||||
hpm26,26,26,number,
|
||||
hpm27,27,27,number,
|
||||
hpm28,28,28,number,
|
||||
hpm29,29,29,number,
|
||||
hpm30,30,30,number,
|
||||
hpm31,31,31,number,
|
||||
end
|
||||
Hypervisor Guest External Interrupt Pending Register.
|
@ -0,0 +1,16 @@
|
||||
Hedeleg
|
||||
1538
|
||||
ex0,0,0,number,Instruction address misaligned
|
||||
ex1,1,1,number,Instruction access fault
|
||||
ex2,2,2,number,Illegal instruction
|
||||
ex3,3,3,number,Breakpoint
|
||||
ex4,4,4,number,Load address misaligned
|
||||
ex5,5,5,number,Load access fault
|
||||
ex6,6,6,number,Store/AMO address misaligned
|
||||
ex7,7,7,number,Store/AMO access fault
|
||||
ex8,8,8,number,Environment call from U-mode or VU-mode
|
||||
ex12,12,12,number,Instruction page fault
|
||||
ex13,13,13,number,Load page fault
|
||||
ex15,15,15,number,Store/AMO page fault
|
||||
end
|
||||
Hypervisor Exception Delegation Register.
|
@ -0,0 +1,7 @@
|
||||
Hgatp
|
||||
1664
|
||||
mode,63,60,HgatpValues,Bare=0;Sv39x4=8;Sv48x4=9,Guest address translation mode.
|
||||
vmid,57,44,number,Virtual machine ID.
|
||||
ppn,43,0,number,Physical Page Number for root page table.
|
||||
end
|
||||
Hypervisor Guest Address Translation and Protection Register.
|
@ -0,0 +1,4 @@
|
||||
Hgeie
|
||||
1543
|
||||
end
|
||||
Hypervisor Guest External Interrupt Enable Register.
|
@ -0,0 +1,4 @@
|
||||
Hgeip
|
||||
3602
|
||||
end
|
||||
Hypervisor Guest External Interrupt Pending Register.
|
@ -0,0 +1,7 @@
|
||||
Hideleg
|
||||
1539
|
||||
sip,2,2,number,Software Interrupt
|
||||
tip,6,6,number,Timer Interrupt
|
||||
eip,10,10,number,External Interrupt
|
||||
end
|
||||
Hypervisor Interrupt Delegation Register.
|
@ -0,0 +1,8 @@
|
||||
Hie
|
||||
1540
|
||||
vssie,2,2,number,Software Interrupt
|
||||
vstie,6,6,number,Timer Interrupt
|
||||
vseie,10,10,number,External Interrupt
|
||||
sgeie,12,12,number,Guest External Interrupt
|
||||
end
|
||||
Hypervisor Interrupt Enable Register.
|
@ -0,0 +1,8 @@
|
||||
Hip
|
||||
1604
|
||||
vssip,2,2,number,Software Interrupt
|
||||
vstip,6,6,number,Timer Interrupt
|
||||
vseip,10,10,number,External Interrupt
|
||||
sgeip,12,12,number,Guest External Interrupt
|
||||
end
|
||||
Hypervisor Interrupt Pending Register.
|
@ -0,0 +1,14 @@
|
||||
Hstatus
|
||||
1536
|
||||
vsxl,33,32,VsxlValues,Vsxl32=1;Vsxl64;Vsxl128,Effective XLEN for VM.
|
||||
vtsr,22,22,number,TSR for VM.
|
||||
vtw,21,21,number,TW for VM.
|
||||
vtvm,20,20,number,TVM for VM.
|
||||
vgein,17,12,number,Virtual Guest External Interrupt Number.
|
||||
hu,9,9,number,Hypervisor User mode.
|
||||
spvp,8,8,number,Supervisor Previous Virtual Privilege.
|
||||
spv,7,7,number,Supervisor Previous Virtualization mode.
|
||||
gva,6,6,number,Guest Virtual Address.
|
||||
vsbe,5,5,number,VS access endianness.
|
||||
end
|
||||
HStatus Register.
|
@ -0,0 +1,5 @@
|
||||
Htimedelta
|
||||
1541
|
||||
end
|
||||
Hypervisor Time Delta Register.
|
||||
read_composite_csr!(super::htimedeltah::read(), read());
|
@ -0,0 +1,4 @@
|
||||
Htimedeltah
|
||||
1557
|
||||
end
|
||||
Hypervisor Time Delta Register.
|
@ -0,0 +1,4 @@
|
||||
Htinst
|
||||
1610
|
||||
end
|
||||
Hypervisor Trap Instruction Register.
|
@ -0,0 +1,4 @@
|
||||
Htval
|
||||
1603
|
||||
end
|
||||
Hypervisor Trap Value Register.
|
@ -0,0 +1,7 @@
|
||||
Hvip
|
||||
1605
|
||||
vssip,2,2,number,Software Interrupt
|
||||
vstip,6,6,number,Timer Interrupt
|
||||
vseip,10,10,number,External Interrupt
|
||||
end
|
||||
Hypervisor Virtual Interrupt Pending Register.
|
@ -0,0 +1,7 @@
|
||||
Vsatp
|
||||
640
|
||||
mode,63,60,HgatpValues,Bare=0;Sv39x4=8;Sv48x4=9,Guest address translation mode.
|
||||
asid,59,44,number,ASID.
|
||||
ppn,43,0,number,Physical Page Number for root page table.
|
||||
end
|
||||
Virtual Supervisor Guest Address Translation and Protection Register.
|
@ -0,0 +1,6 @@
|
||||
Vscause
|
||||
578
|
||||
interrupt,63,63,number,Is cause interrupt.
|
||||
code,62,0,number,Exception code
|
||||
end
|
||||
Virtual Supervisor Cause Register.
|
@ -0,0 +1,4 @@
|
||||
Vsepc
|
||||
577
|
||||
end
|
||||
Virtual Supervisor Exception Program Counter.
|
@ -0,0 +1,7 @@
|
||||
Vsie
|
||||
516
|
||||
ssie,1,1,number,Software Interrupt
|
||||
stie,5,5,number,Timer Interrupt
|
||||
seie,9,9,number,External Interrupt
|
||||
end
|
||||
Virtual Supevisor Interrupt Enable Register.
|
@ -0,0 +1,7 @@
|
||||
Vsip
|
||||
580
|
||||
ssip,1,1,number,Software Interrupt
|
||||
stip,5,5,number,Timer Interrupt
|
||||
seip,9,9,number,External Interrupt
|
||||
end
|
||||
Virtual Supevisor Interrupt Pending Register.
|
@ -0,0 +1,4 @@
|
||||
Vsscratch
|
||||
576
|
||||
end
|
||||
Virtual Supervisor Scratch Register.
|
@ -0,0 +1,14 @@
|
||||
Vsstatus
|
||||
512
|
||||
sd,63,60,number,
|
||||
uxl,33,32,UxlValues,Uxl32=1;Uxl64;Uxl128,Effective User XLEN.
|
||||
mxr,19,19,number,
|
||||
sum,18,18,number,
|
||||
xs,16,15,number,
|
||||
fs,14,13,number,
|
||||
spp,8,8,number,
|
||||
ube,6,6,number,
|
||||
spie,5,5,number,
|
||||
sie,1,1,number,
|
||||
end
|
||||
Hypervisor Guest External Interrupt Pending Register.
|
@ -0,0 +1,4 @@
|
||||
Vstval
|
||||
579
|
||||
end
|
||||
Virtual Supervisor Trap Value Register.
|
@ -0,0 +1,6 @@
|
||||
Vstvec
|
||||
517
|
||||
base,63,2,number,
|
||||
mode,1,0,number,
|
||||
end
|
||||
Virtual Supervisor Trap Vector Base Address Register.
|
@ -0,0 +1,211 @@
|
||||
use super::*;
|
||||
use bit_field::BitField;
|
||||
use core::convert::TryInto;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct GPAddrSv32X4(u64);
|
||||
|
||||
impl Address for GPAddrSv32X4 {
|
||||
fn new(addr: usize) -> Self {
|
||||
Self::new_u64(addr as u64)
|
||||
}
|
||||
fn as_usize(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
fn page_number(&self) -> usize {
|
||||
self.0.get_bits(12..34) as usize
|
||||
}
|
||||
fn page_offset(&self) -> usize {
|
||||
self.0.get_bits(0..12) as usize
|
||||
}
|
||||
fn to_4k_aligned(&self) -> Self {
|
||||
GPAddrSv32X4((self.0 >> 12) << 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualAddress for GPAddrSv32X4 {
|
||||
unsafe fn as_mut<'a, 'b, T>(&'a self) -> &'b mut T {
|
||||
&mut *(self.0 as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressL2 for GPAddrSv32X4 {
|
||||
fn p2_index(&self) -> usize {
|
||||
self.0.get_bits(22..34) as usize
|
||||
}
|
||||
fn p1_index(&self) -> usize {
|
||||
self.0.get_bits(12..22) as usize
|
||||
}
|
||||
fn from_page_table_indices(p2_index: usize, p1_index: usize, offset: usize) -> Self {
|
||||
let p2_index = p2_index as u64;
|
||||
let p1_index = p1_index as u64;
|
||||
let offset = offset as u64;
|
||||
assert!(p2_index.get_bits(12..) == 0, "p2_index exceeding 12 bits");
|
||||
assert!(p1_index.get_bits(10..) == 0, "p1_index exceeding 10 bits");
|
||||
assert!(offset.get_bits(12..) == 0, "offset exceeding 12 bits");
|
||||
GPAddrSv32X4::new_u64((p2_index << 22) | (p1_index << 12) | offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressX64 for GPAddrSv32X4 {
|
||||
fn new_u64(addr: u64) -> Self {
|
||||
assert!(
|
||||
addr.get_bits(34..64) == 0,
|
||||
"Sv32x4 does not allow pa 34..64!=0"
|
||||
);
|
||||
GPAddrSv32X4(addr)
|
||||
}
|
||||
fn as_u64(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct GPAddrSv39X4(u64);
|
||||
|
||||
impl Address for GPAddrSv39X4 {
|
||||
fn new(addr: usize) -> Self {
|
||||
GPAddrSv39X4(addr.try_into().unwrap())
|
||||
}
|
||||
fn as_usize(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
fn page_number(&self) -> usize {
|
||||
self.0.get_bits(12..41) as usize
|
||||
}
|
||||
fn page_offset(&self) -> usize {
|
||||
self.0.get_bits(0..12) as usize
|
||||
}
|
||||
fn to_4k_aligned(&self) -> Self {
|
||||
GPAddrSv39X4((self.0 >> 12) << 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualAddress for GPAddrSv39X4 {
|
||||
unsafe fn as_mut<'a, 'b, T>(&'a self) -> &'b mut T {
|
||||
&mut *(self.0 as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressL3 for GPAddrSv39X4 {
|
||||
fn p3_index(&self) -> usize {
|
||||
self.0.get_bits(30..41) as usize
|
||||
}
|
||||
fn p2_index(&self) -> usize {
|
||||
self.0.get_bits(21..30) as usize
|
||||
}
|
||||
fn p1_index(&self) -> usize {
|
||||
self.0.get_bits(12..21) as usize
|
||||
}
|
||||
fn from_page_table_indices(
|
||||
p3_index: usize,
|
||||
p2_index: usize,
|
||||
p1_index: usize,
|
||||
offset: usize,
|
||||
) -> Self {
|
||||
let p3_index = p3_index as u64;
|
||||
let p2_index = p2_index as u64;
|
||||
let p1_index = p1_index as u64;
|
||||
let offset = offset as u64;
|
||||
assert!(p3_index.get_bits(11..) == 0, "p3_index exceeding 11 bits");
|
||||
assert!(p2_index.get_bits(9..) == 0, "p2_index exceeding 9 bits");
|
||||
assert!(p1_index.get_bits(9..) == 0, "p1_index exceeding 9 bits");
|
||||
assert!(offset.get_bits(12..) == 0, "offset exceeding 12 bits");
|
||||
GPAddrSv39X4::new_u64(
|
||||
(p3_index << 12 << 9 << 9) | (p2_index << 12 << 9) | (p1_index << 12) | offset,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressX64 for GPAddrSv39X4 {
|
||||
fn new_u64(addr: u64) -> Self {
|
||||
assert!(
|
||||
addr.get_bits(41..64) == 0,
|
||||
"Sv39x4 does not allow pa 41..64!=0"
|
||||
);
|
||||
GPAddrSv39X4(addr)
|
||||
}
|
||||
fn as_u64(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct GPAddrSv48X4(u64);
|
||||
|
||||
impl Address for GPAddrSv48X4 {
|
||||
fn new(addr: usize) -> Self {
|
||||
GPAddrSv48X4(addr.try_into().unwrap())
|
||||
}
|
||||
fn as_usize(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
fn page_number(&self) -> usize {
|
||||
self.0.get_bits(12..50) as usize
|
||||
}
|
||||
fn page_offset(&self) -> usize {
|
||||
self.0.get_bits(0..12) as usize
|
||||
}
|
||||
fn to_4k_aligned(&self) -> Self {
|
||||
GPAddrSv48X4((self.0 >> 12) << 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualAddress for GPAddrSv48X4 {
|
||||
unsafe fn as_mut<'a, 'b, T>(&'a self) -> &'b mut T {
|
||||
&mut *(self.0 as *mut T)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressL4 for GPAddrSv48X4 {
|
||||
fn p4_index(&self) -> usize {
|
||||
self.0.get_bits(39..50) as usize
|
||||
}
|
||||
fn p3_index(&self) -> usize {
|
||||
self.0.get_bits(30..39) as usize
|
||||
}
|
||||
fn p2_index(&self) -> usize {
|
||||
self.0.get_bits(21..30) as usize
|
||||
}
|
||||
fn p1_index(&self) -> usize {
|
||||
self.0.get_bits(12..21) as usize
|
||||
}
|
||||
fn from_page_table_indices(
|
||||
p4_index: usize,
|
||||
p3_index: usize,
|
||||
p2_index: usize,
|
||||
p1_index: usize,
|
||||
offset: usize,
|
||||
) -> Self {
|
||||
let p4_index = p4_index as u64;
|
||||
let p3_index = p3_index as u64;
|
||||
let p2_index = p2_index as u64;
|
||||
let p1_index = p1_index as u64;
|
||||
let offset = offset as u64;
|
||||
assert!(p4_index.get_bits(11..) == 0, "p4_index exceeding 11 bits");
|
||||
assert!(p3_index.get_bits(9..) == 0, "p3_index exceeding 9 bits");
|
||||
assert!(p2_index.get_bits(9..) == 0, "p2_index exceeding 9 bits");
|
||||
assert!(p1_index.get_bits(9..) == 0, "p1_index exceeding 9 bits");
|
||||
assert!(offset.get_bits(12..) == 0, "offset exceeding 12 bits");
|
||||
GPAddrSv48X4::new_u64(
|
||||
(p4_index << 12 << 9 << 9 << 9)
|
||||
| (p3_index << 12 << 9 << 9)
|
||||
| (p2_index << 12 << 9)
|
||||
| (p1_index << 12)
|
||||
| offset,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressX64 for GPAddrSv48X4 {
|
||||
fn new_u64(addr: u64) -> Self {
|
||||
assert!(
|
||||
addr.get_bits(50..64) == 0,
|
||||
"Sv48x4 does not allow pa 50..64!=0"
|
||||
);
|
||||
GPAddrSv48X4(addr)
|
||||
}
|
||||
fn as_u64(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
pub trait Address: core::fmt::Debug + Copy + Clone + PartialEq + Eq + PartialOrd + Ord {
|
||||
fn new(addr: usize) -> Self;
|
||||
fn page_number(&self) -> usize;
|
||||
fn page_offset(&self) -> usize;
|
||||
fn to_4k_aligned(&self) -> Self;
|
||||
fn as_usize(&self) -> usize;
|
||||
}
|
||||
|
||||
pub trait VirtualAddress: Address {
|
||||
unsafe fn as_mut<'a, 'b, T>(&'a self) -> &'b mut T;
|
||||
}
|
||||
|
||||
pub trait AddressX32: Address {
|
||||
fn new_u32(addr: u32) -> Self;
|
||||
fn as_u32(&self) -> u32;
|
||||
}
|
||||
pub trait AddressX64: Address {
|
||||
fn new_u64(addr: u64) -> Self;
|
||||
fn as_u64(&self) -> u64;
|
||||
}
|
||||
|
||||
pub trait PhysicalAddress: AddressX64 {}
|
||||
|
||||
pub trait AddressL3: Address {
|
||||
fn p3_index(&self) -> usize;
|
||||
fn p2_index(&self) -> usize;
|
||||
fn p1_index(&self) -> usize;
|
||||
fn from_page_table_indices(
|
||||
p3_index: usize,
|
||||
p2_index: usize,
|
||||
p1_index: usize,
|
||||
offset: usize,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
pub trait AddressL4: Address {
|
||||
fn p4_index(&self) -> usize;
|
||||
fn p3_index(&self) -> usize;
|
||||
fn p2_index(&self) -> usize;
|
||||
fn p1_index(&self) -> usize;
|
||||
fn from_page_table_indices(
|
||||
p4_index: usize,
|
||||
p3_index: usize,
|
||||
p2_index: usize,
|
||||
p1_index: usize,
|
||||
offset: usize,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
pub trait AddressL2: Address {
|
||||
fn p2_index(&self) -> usize;
|
||||
fn p1_index(&self) -> usize;
|
||||
fn from_page_table_indices(p2_index: usize, p1_index: usize, offset: usize) -> Self;
|
||||
}
|
||||
pub mod gpax4;
|
||||
pub mod page;
|
||||
pub mod sv32;
|
||||
pub mod sv39;
|
||||
pub mod sv48;
|
||||
|
||||
pub use self::gpax4::*;
|
||||
pub use self::page::*;
|
||||
pub use self::sv32::*;
|
||||
pub use self::sv39::*;
|
||||
pub use self::sv48::*;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! use_sv32 {
|
||||
() => {
|
||||
pub type VirtAddr = VirtAddrSv32;
|
||||
pub type PhysAddr = PhysAddrSv32;
|
||||
pub type Page = PageWith<VirtAddr>;
|
||||
pub type Frame = FrameWith<PhysAddr>;
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! use_sv39 {
|
||||
() => {
|
||||
pub type VirtAddr = VirtAddrSv39;
|
||||
pub type PhysAddr = PhysAddrSv39;
|
||||
pub type Page = PageWith<VirtAddr>;
|
||||
pub type Frame = FrameWith<PhysAddr>;
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! use_sv48 {
|
||||
() => {
|
||||
pub type VirtAddr = VirtAddrSv48;
|
||||
pub type PhysAddr = PhysAddrSv48;
|
||||
pub type Page = PageWith<VirtAddr>;
|
||||
pub type Frame = FrameWith<PhysAddr>;
|
||||
};
|
||||
}
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
use_sv48!();
|
||||
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
use_sv32!();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue