1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Hardware abstract layer (HAL) for the GD32VF103 microcontroller chip.
//!
//! This is an implementation of `embedded-hal` traits for the GD32VF103, MCU with
//! one RISC-V's RV32IMAC core as well as up to 128 KiB of Flash and 32 KiB of SRAM,
//! produced by GigaDevice Semiconductor Inc.
//!
//! # Usage
//! Add this crate to your dependencies:
//! ```
//! [dependencies]
//! gd32vf103-hal = "0.0"
//! ```
//!
//! # Example
//! ```
//! #![no_std]
//! #![no_main]
//! // choose a panic handler crate
//! extern crate panic_halt;
//! // include this library
//! use gd32vf103_hal::{pac, prelude::*};
//! // use the `riscv_rt` runtime to define entry
//! #[riscv_rt::entry]
//! fn main() -> ! {
//!     // Get ownership of device peripherals
//!     let dp = pac::Peripherals::take().unwrap();
//!     // Constrain RCU register for further use
//!     let mut rcu = dp.RCU.constrain();
//!     // Split GPIOA into separate pins.
//!     // You need a mutable reference of APB2 struct to initialize GPIOA,
//!     // so we offer `&mut rcu.apb2` as a parameter here.
//!     let mut gpioa = dp.GPIOA.split(&mut rcu.apb2);
//!     // Change the state of `pa1` into push-pull output with default speed.
//!     let mut pa1 = gpioa.pa1.into_push_pull_output(&mut gpioa.ctl0);
//!     // Use API offered by `embedded-hal` to set `pa1` low,
//!     // An LED light with cathode connected to PA1 should be lit now.
//!     pa1.set_low().unwrap();
//!     // We just end this program with infinite loop.
//!     // A `wfi` instruction should be also acceptable here.
//!     loop {}
//! }
//! ```

#![no_std]
// #![deny(missing_docs)]

pub use gd32vf103_pac as pac;

pub mod adc;
pub mod afio;
pub mod backup;
pub mod crc;
pub mod ctimer;
pub mod debug;
pub mod delay;
pub mod esig;
pub mod fmc;
pub mod gpio;
pub mod rcu;
pub mod serial;
pub mod spi;
pub mod timer;
pub mod unit;
pub mod wdog;

/// Prelude
pub mod prelude {
    pub use embedded_hal::prelude::*;
    pub use crate::afio::AfioExt as _gd32vf103_hal_afio_AfioExt;
    pub use crate::gpio::GpioExt as _gd32vf103_hal_gpio_GpioExt;
    pub use crate::gpio::{Unlock as _gd32vf103_hal_gpio_Unlock, UpTo10MHz, UpTo2MHz, UpTo50MHz};
    pub use crate::rcu::RcuExt as _gd32vf103_hal_rcu_RcuExt;
    pub use crate::unit::U32Ext as _gd32vf103_hal_unit_U32Ext;
}

mod atomic {
    use core::sync::atomic::{AtomicU32, Ordering};
    // This function uses AtomicU32, compiles into atomic instructions to prevent data race
    // and optimize for speed.
    //
    // If we don't do like this, we would need to go into critical section, where additional
    // interrupt disabling and enabling operations are required, which needs lots of CSR
    // read/write instructions and costs lot of time.
    //
    // For all `is_one: true` params, the core feature of this function compiles into
    // only one atomic instruction `amoor.w` to set the target register.
    // (For `is_one: false` params, it compiles into ont `amoand.w`).
    // Additional instructions to set the mask may differ between actual applications,
    // this part may cost additional one to two instructions (mainly `lui` and `addi`).
    //
    // Note: we uses `fetch_and(!mask, ...)` instead of `fetch_nand(mask, ...)`; that's
    // because RISC-V's RV32A does not provide an atomic nand instruction, thus `rustc`
    // may compile code into very long binary output.
    #[inline(always)]
    pub(crate) fn atomic_set_bit(r: &AtomicU32, is_one: bool, index: usize) {
        let mask = 1 << index;
        if is_one {
            r.fetch_or(mask, Ordering::Relaxed);
        } else {
            r.fetch_and(!mask, Ordering::Relaxed);
        }
    }

    // This function compiles into RV32A's `amoxor.w` instruction to prevent data
    // race as well as optimize for speed.
    #[inline(always)]
    pub(crate) fn atomic_toggle_bit(r: &AtomicU32, index: usize) {
        let mask = 1 << index;
        r.fetch_xor(mask, Ordering::Relaxed);
    }
}

// == Notes on prelude trait function naming:
//
// If we wrap some register modules into one Rust `mod`, we infer that
// all the modules share common switches, clocks or unlock process.
//
// To take apart whole module into functional module register groups,
// we use traits with one function. Function name can be arbitrary in
// theory but we prefer following frequent function names:
// - split
// - constrain
// - configure
//
// The function name should depends on how the modules logically effect
// each other:
//
// If logical state of module registers do not depend on each other,
// the trait function name could be `split`.
//
// If logical states of module registers is under inherit or hierarchy
// relation thus may depend on each other, name could be `constain`
// or `configure`. If all combination of register states are valid,
// use `configure`; otherwise if some combinations are invalid, use
// `constrain`.