diff --git a/Cargo.lock b/Cargo.lock index 206d1cfc..04a2ccd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,6 +668,7 @@ dependencies = [ "atsamv71q21b", "cortex-m", "embedded-hal", + "fugit", "nb 0.1.3", ] @@ -728,6 +729,24 @@ dependencies = [ "void", ] +[[package]] +name = "fugit" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d8595783d5ca52f0e9830036b3d24f359fae0fcc6bb5fde41f2dd82997cb58" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a" +dependencies = [ + "paste", +] + [[package]] name = "nb" version = "0.1.3" @@ -743,6 +762,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" +[[package]] +name = "paste" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" + [[package]] name = "proc-macro2" version = "1.0.18" diff --git a/Cargo.toml b/Cargo.toml index f6f47a8b..7fe8abf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ -[workspace] -members = [ - "hal", - "pac/*", -] \ No newline at end of file +[workspace] +members = [ + "hal", + "pac/*", +] +exclude = [ + "boards/atsame70_xpro" +] diff --git a/boards/.gitignore b/boards/.gitignore new file mode 100644 index 00000000..64857759 --- /dev/null +++ b/boards/.gitignore @@ -0,0 +1 @@ +**/Cargo.lock diff --git a/boards/atsame70_xpro/.cargo/config b/boards/atsame70_xpro/.cargo/config new file mode 100644 index 00000000..099801d8 --- /dev/null +++ b/boards/atsame70_xpro/.cargo/config @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = "arm-none-eabi-gdb -q -x openocd.gdb" + +[build] +target = "thumbv7em-none-eabihf" +rustflags = [ + "-C", "link-arg=-Tlink.x", +] diff --git a/boards/atsame70_xpro/Cargo.toml b/boards/atsame70_xpro/Cargo.toml new file mode 100644 index 00000000..1b657598 --- /dev/null +++ b/boards/atsame70_xpro/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "atsame70_xpro" +version = "0.1.0" +edition = "2021" +authors = [ + "Viktor Sonesten " +] +license = "0BSD" + +[dependencies] +cortex-m-rtic = "1.0" +cortex-m = "0.7" +panic-halt = "0.2" + +[dependencies.atsamx7x-hal] +version = "0.1.0" +path = "../../hal" +features = ["same70q21b-rt"] + +[profile.dev] +debug = true +lto = true \ No newline at end of file diff --git a/boards/atsame70_xpro/README.md b/boards/atsame70_xpro/README.md new file mode 100644 index 00000000..26db2bee --- /dev/null +++ b/boards/atsame70_xpro/README.md @@ -0,0 +1,31 @@ +# SAM E70 Xplained Pro Evaluation Kit examples + +This crate provides HAL usage examples for working with the [SAM E70 Xplained Pro Evaulation Kit](https://www.microchip.com/en-us/development-tool/DM320113). +The examples are written in [RTIC](https://rtic.rs). + +## Prerequisites +* Install the cross-compilation toolchain: `rustup target add thumbv7em-none-eabihf`. +* Install [openocd `v0.11.0`](https://openocd.org/openocd-0-11-0-released.html) (or above). + +## Flashing an example +First, the General-Purpose Non-Volatile-Memory (GPNVM) boot bit must be set in order to map the flashed firmware to address `0x0`; +required for the interrupts and software resets to work as expected: +1. Connect the board via the "Debug USB" port and run + ```shell + $ openocd -f openocd.cfg -c "atsamv gpnvm set 1" -c exit + ``` +2. Power-cycle the board, and the verify with + ```shell + $ openocd -f openocd.cfg -c "atsamv gpnvm show 1" -c exit + [...] + samv-gpnvm1: 1 + ``` + +The example can now be flashed after first, in a seperate shell, `openocd -f openocd.cfg` +```shell +$ cargo run +``` + +## Erasing bad firmware +In case the board has been flashed with software that sets it in a bad state before the debugger can attach, bridge the "ERASE" header (J400; north of the MCU) between power cycles, and remove the bridge. +The flash area has now been zeroed. diff --git a/boards/atsame70_xpro/memory.x b/boards/atsame70_xpro/memory.x new file mode 100644 index 00000000..57560e6c --- /dev/null +++ b/boards/atsame70_xpro/memory.x @@ -0,0 +1,31 @@ +MEMORY +{ + FLASH : ORIGIN = 0x400000, LENGTH = 2M + RAM : ORIGIN = 0x20400000, LENGTH = 256K /* 256K or 384K; p. 54 */ +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ diff --git a/boards/atsame70_xpro/openocd.cfg b/boards/atsame70_xpro/openocd.cfg new file mode 100644 index 00000000..f4ef1c76 --- /dev/null +++ b/boards/atsame70_xpro/openocd.cfg @@ -0,0 +1,6 @@ +source [find interface/cmsis-dap.cfg] +set CHIPNAME atsame70n21 +source [find target/atsamv.cfg] + +init +reset halt diff --git a/boards/atsame70_xpro/openocd.gdb b/boards/atsame70_xpro/openocd.gdb new file mode 100644 index 00000000..7aa22fd7 --- /dev/null +++ b/boards/atsame70_xpro/openocd.gdb @@ -0,0 +1,35 @@ +target extended-remote :3333 + +set print asm-demangle on + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind + +# *try* to stop at the user entry point (it might be gone due to inlining) +# break main + +monitor arm semihosting enable +monitor halt + +load + +# start the process but immediately halt the processor +stepi + +# Helpers +define reload + monitor reset halt + continue +end + +define reflash + !cargo build + monitor reset halt + load + continue +end + +alias rl = reload +alias rf = reflash \ No newline at end of file diff --git a/boards/atsame70_xpro/src/main.rs b/boards/atsame70_xpro/src/main.rs new file mode 100644 index 00000000..3bbfd1e9 --- /dev/null +++ b/boards/atsame70_xpro/src/main.rs @@ -0,0 +1,94 @@ +#![no_std] +#![no_main] + +use panic_halt as _; + +#[rtic::app(device = atsamx7x_hal::target_device, peripherals = true, dispatchers = [IXC])] +mod app { + use atsamx7x_hal as hal; + use hal::ehal::watchdog::WatchdogDisable; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { + cortex_m::asm::bkpt(); + + // Disable the watchdog. + hal::watchdog::Watchdog::new(ctx.device.WDT).disable(); + + let mut efc = { + use hal::efc::{Efc, VddioLevel}; + Efc::new(ctx.device.EFC, VddioLevel::V3) + }; + + // Configure the clock hierarchy + { + use hal::pmc::{ + HostClockConfig, MainCkSource, MckDivider, MckPrescaler, Megahertz, PckId, + PllaConfig, Pmc, UpllDivider, + }; + + let mut pmc = Pmc::new(ctx.device.PMC); + let mainck = pmc + .get_mainck(MainCkSource::ExternalBypass(Megahertz::from_raw(12))) + .unwrap(); + let _plla = pmc + .get_pllack(PllaConfig { div: 1, mult: 8 }, &mainck) + .unwrap(); + let _hclk = pmc + .get_hclk( + HostClockConfig { + pres: MckPrescaler::CLK_1, + div: MckDivider::EQ_PCK, + }, + &mainck, + &mut efc, + ) + .unwrap(); + + let upllck = pmc.get_upllck(&mainck, &mut ctx.device.UTMI).unwrap(); + let upllckdiv = pmc.get_upllckdiv(&upllck, UpllDivider::Div2); + let _pck2 = pmc.get_pck(&upllckdiv, 100 - 1, PckId::Pck2); // @ 2.4MHz + } + + // Configure PA03 as PCK2 output + { + let pioa = ctx.device.PIOA; + + // Configure pins for function C: UART4 (0b10) + pioa.pio_abcdsr[1].modify(|_, w| w.p3().set_bit()); + pioa.pio_abcdsr[0].modify(|_, w| w.p3().clear_bit()); + + // Give pins to the peripheral. + pioa.pio_pdr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_psr.read().p3().bit_is_clear()); + + // disable multidrive + pioa.pio_mddr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_mdsr.read().p3().bit_is_clear()); + + // ensure we dont pull the pin up/down + pioa.pio_pudr.write(|w| w.p3().set_bit()); + pioa.pio_ppddr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_pusr.read().p3().bit_is_set()); + assert!(pioa.pio_ppdsr.read().p3().bit_is_set()); + } + + (Shared {}, Local {}, init::Monotonics()) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } +} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 01d4b063..d6e45bf4 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -24,6 +24,7 @@ edition = "2021" cortex-m = "0.7" embedded-hal = { version = "0.2.7", features = ["unproven"] } nb = "0.1.2" +fugit = "0.3.5" atsame70j19 = { version = "0.21.0", path = "../pac/atsame70j19", optional = true } atsame70j19b = { version = "0.21.0", path = "../pac/atsame70j19b", optional = true } @@ -93,136 +94,156 @@ atsamv71q21 = { version = "0.21.0", path = "../pac/atsamv71q21", optional = tru atsamv71q21b = { version = "0.21.0", path = "../pac/atsamv71q21b", optional = true } [features] +# Internal-only feature flags; do not set directly. +# Refer to §2 in the data sheet. +## Refer to §2. +v71 = [] +v70 = [] +e70 = [] +s70 = [] +pins-64 = [] # J variants +pins-100 = [] # N variants +pins-144 = [] # Q variants +flash-2M = [] # "21"-suffix +flash-1M = [] # "20"-suffix +flash-512K = [] # "19"-suffix +rev-a = [] # No suffix after flash memory density number +rev-b = ["device-selected"] # "b"-suffix + device-selected = [] -same70j19 = ["atsame70j19", "device-selected"] + +same70j19 = ["atsame70j19", "e70", "pins-64", "flash-512K", "rev-a"] same70j19-rt = ["same70j19", "atsame70j19/rt"] -same70j19b = ["atsame70j19b", "device-selected"] +same70j19b = ["atsame70j19b", "e70", "pins-64", "flash-512K", "rev-b"] same70j19b-rt = ["same70j19b", "atsame70j19b/rt"] -same70j20 = ["atsame70j20", "device-selected"] +same70j20 = ["atsame70j20", "e70", "pins-64", "flash-1M", "rev-a"] same70j20-rt = ["same70j20", "atsame70j20/rt"] -same70j20b = ["atsame70j20b", "device-selected"] +same70j20b = ["atsame70j20b", "e70", "pins-64", "flash-1M", "rev-b"] same70j20b-rt = ["same70j20b", "atsame70j20b/rt"] -same70j21 = ["atsame70j21", "device-selected"] +same70j21 = ["atsame70j21", "e70", "pins-64", "flash-2M", "rev-a"] same70j21-rt = ["same70j21", "atsame70j21/rt"] -same70j21b = ["atsame70j21b", "device-selected"] +same70j21b = ["atsame70j21b", "e70", "pins-64", "flash-2M", "rev-b"] same70j21b-rt = ["same70j21b", "atsame70j21b/rt"] -same70n19 = ["atsame70n19", "device-selected"] +same70n19 = ["atsame70n19", "e70", "pins-100", "flash-512K", "rev-a"] same70n19-rt = ["same70n19", "atsame70n19/rt"] -same70n19b = ["atsame70n19b", "device-selected"] +same70n19b = ["atsame70n19b", "e70", "pins-100", "flash-512K", "rev-b"] same70n19b-rt = ["same70n19b", "atsame70n19b/rt"] -same70n20 = ["atsame70n20", "device-selected"] +same70n20 = ["atsame70n20", "e70", "pins-100", "flash-1M", "rev-a"] same70n20-rt = ["same70n20", "atsame70n20/rt"] -same70n20b = ["atsame70n20b", "device-selected"] +same70n20b = ["atsame70n20b", "e70", "pins-100", "flash-1M", "rev-b"] same70n20b-rt = ["same70n20b", "atsame70n20b/rt"] -same70n21 = ["atsame70n21", "device-selected"] +same70n21 = ["atsame70n21", "e70", "pins-100", "flash-2M", "rev-a"] same70n21-rt = ["same70n21", "atsame70n21/rt"] -same70n21b = ["atsame70n21b", "device-selected"] +same70n21b = ["atsame70n21b", "e70", "pins-100", "flash-2M", "rev-b"] same70n21b-rt = ["same70n21b", "atsame70n21b/rt"] -same70q19 = ["atsame70q19", "device-selected"] +same70q19 = ["atsame70q19", "e70", "pins-144", "flash-512K", "rev-a"] same70q19-rt = ["same70q19", "atsame70q19/rt"] -same70q19b = ["atsame70q19b", "device-selected"] +same70q19b = ["atsame70q19b", "e70", "pins-144", "flash-512K", "rev-b"] same70q19b-rt = ["same70q19b", "atsame70q19b/rt"] -same70q20 = ["atsame70q20", "device-selected"] +same70q20 = ["atsame70q20", "e70", "pins-144", "flash-1M", "rev-a"] same70q20-rt = ["same70q20", "atsame70q20/rt"] -same70q20b = ["atsame70q20b", "device-selected"] +same70q20b = ["atsame70q20b", "e70", "pins-144", "flash-1M", "rev-b"] same70q20b-rt = ["same70q20b", "atsame70q20b/rt"] -same70q21 = ["atsame70q21", "device-selected"] +same70q21 = ["atsame70q21", "e70", "pins-144", "flash-2M", "rev-a"] same70q21-rt = ["same70q21", "atsame70q21/rt"] -same70q21b = ["atsame70q21b", "device-selected"] +same70q21b = ["atsame70q21b", "e70", "pins-144", "flash-2M", "rev-b"] same70q21b-rt = ["same70q21b", "atsame70q21b/rt"] -sams70j19 = ["atsams70j19", "device-selected"] + +sams70j19 = ["atsams70j19", "s70", "pins-64", "flash-512K", "rev-a"] sams70j19-rt = ["sams70j19", "atsams70j19/rt"] -sams70j19b = ["atsams70j19b", "device-selected"] +sams70j19b = ["atsams70j19b", "s70", "pins-64", "flash-512K", "rev-b"] sams70j19b-rt = ["sams70j19b", "atsams70j19b/rt"] -sams70j20 = ["atsams70j20", "device-selected"] +sams70j20 = ["atsams70j20", "s70", "pins-64", "flash-1M", "rev-a"] sams70j20-rt = ["sams70j20", "atsams70j20/rt"] -sams70j20b = ["atsams70j20b", "device-selected"] +sams70j20b = ["atsams70j20b", "s70", "pins-64", "flash-1M", "rev-b"] sams70j20b-rt = ["sams70j20b", "atsams70j20b/rt"] -sams70j21 = ["atsams70j21", "device-selected"] +sams70j21 = ["atsams70j21", "s70", "pins-64", "flash-2M", "rev-a"] sams70j21-rt = ["sams70j21", "atsams70j21/rt"] -sams70j21b = ["atsams70j21b", "device-selected"] +sams70j21b = ["atsams70j21b", "s70", "pins-64", "flash-2M", "rev-b"] sams70j21b-rt = ["sams70j21b", "atsams70j21b/rt"] -sams70n19 = ["atsams70n19", "device-selected"] +sams70n19 = ["atsams70n19", "s70", "pins-100", "flash-512K", "rev-a"] sams70n19-rt = ["sams70n19", "atsams70n19/rt"] -sams70n19b = ["atsams70n19b", "device-selected"] +sams70n19b = ["atsams70n19b", "s70", "pins-100", "flash-512K", "rev-a"] sams70n19b-rt = ["sams70n19b", "atsams70n19b/rt"] -sams70n20 = ["atsams70n20", "device-selected"] +sams70n20 = ["atsams70n20", "s70", "pins-100", "flash-1M", "rev-a"] sams70n20-rt = ["sams70n20", "atsams70n20/rt"] -sams70n20b = ["atsams70n20b", "device-selected"] +sams70n20b = ["atsams70n20b", "s70", "pins-100", "flash-1M", "rev-b"] sams70n20b-rt = ["sams70n20b", "atsams70n20b/rt"] -sams70n21 = ["atsams70n21", "device-selected"] +sams70n21 = ["atsams70n21", "s70", "pins-100", "flash-2M", "rev-a"] sams70n21-rt = ["sams70n21", "atsams70n21/rt"] -sams70n21b = ["atsams70n21b", "device-selected"] +sams70n21b = ["atsams70n21b", "s70", "pins-100", "flash-2M", "rev-b"] sams70n21b-rt = ["sams70n21b", "atsams70n21b/rt"] -sams70q19 = ["atsams70q19", "device-selected"] +sams70q19 = ["atsams70q19", "s70", "pins-144", "flash-512K", "rev-a"] sams70q19-rt = ["sams70q19", "atsams70q19/rt"] -sams70q19b = ["atsams70q19b", "device-selected"] +sams70q19b = ["atsams70q19b", "s70", "pins-144", "flash-512K", "rev-b"] sams70q19b-rt = ["sams70q19b", "atsams70q19b/rt"] -sams70q20 = ["atsams70q20", "device-selected"] +sams70q20 = ["atsams70q20", "s70", "pins-144", "flash-1M", "rev-a"] sams70q20-rt = ["sams70q20", "atsams70q20/rt"] -sams70q20b = ["atsams70q20b", "device-selected"] +sams70q20b = ["atsams70q20b", "s70", "pins-144", "flash-1M", "rev-b"] sams70q20b-rt = ["sams70q20b", "atsams70q20b/rt"] -sams70q21 = ["atsams70q21", "device-selected"] +sams70q21 = ["atsams70q21", "s70", "pins-144", "flash-2M", "rev-a"] sams70q21-rt = ["sams70q21", "atsams70q21/rt"] -sams70q21b = ["atsams70q21b", "device-selected"] +sams70q21b = ["atsams70q21b", "s70", "pins-144", "flash-2M", "rev-b"] sams70q21b-rt = ["sams70q21b", "atsams70q21b/rt"] -samv70j19 = ["atsamv70j19", "device-selected"] + +samv70j19 = ["atsamv70j19", "v70", "pins-64", "flash-512K", "rev-a"] samv70j19-rt = ["samv70j19", "atsamv70j19/rt"] -samv70j19b = ["atsamv70j19b", "device-selected"] +samv70j19b = ["atsamv70j19b", "v70", "pins-64", "flash-512K", "rev-b"] samv70j19b-rt = ["samv70j19b", "atsamv70j19b/rt"] -samv70j20 = ["atsamv70j20", "device-selected"] +samv70j20 = ["atsamv70j20", "v70", "pins-64", "flash-1M", "rev-a"] samv70j20-rt = ["samv70j20", "atsamv70j20/rt"] -samv70j20b = ["atsamv70j20b", "device-selected"] +samv70j20b = ["atsamv70j20b", "v70", "pins-64", "flash-1M", "rev-b"] samv70j20b-rt = ["samv70j20b", "atsamv70j20b/rt"] -samv70n19 = ["atsamv70n19", "device-selected"] +samv70n19 = ["atsamv70n19", "v70", "pins-100", "flash-512K", "rev-a"] samv70n19-rt = ["samv70n19", "atsamv70n19/rt"] -samv70n19b = ["atsamv70n19b", "device-selected"] +samv70n19b = ["atsamv70n19b", "v70", "pins-100", "flash-512K", "rev-b"] samv70n19b-rt = ["samv70n19b", "atsamv70n19b/rt"] -samv70n20 = ["atsamv70n20", "device-selected"] +samv70n20 = ["atsamv70n20", "v70", "pins-100", "flash-1M", "rev-a"] samv70n20-rt = ["samv70n20", "atsamv70n20/rt"] -samv70n20b = ["atsamv70n20b", "device-selected"] +samv70n20b = ["atsamv70n20b", "v70", "pins-100", "flash-1M", "rev-b"] samv70n20b-rt = ["samv70n20b", "atsamv70n20b/rt"] -samv70q19 = ["atsamv70q19", "device-selected"] +samv70q19 = ["atsamv70q19", "v70", "pins-144", "flash-512K", "rev-a"] samv70q19-rt = ["samv70q19", "atsamv70q19/rt"] -samv70q19b = ["atsamv70q19b", "device-selected"] +samv70q19b = ["atsamv70q19b", "v70", "pins-144", "flash-512K", "rev-b"] samv70q19b-rt = ["samv70q19b", "atsamv70q19b/rt"] -samv70q20 = ["atsamv70q20", "device-selected"] +samv70q20 = ["atsamv70q20", "v70", "pins-144", "flash-1M", "rev-a"] samv70q20-rt = ["samv70q20", "atsamv70q20/rt"] -samv70q20b = ["atsamv70q20b", "device-selected"] +samv70q20b = ["atsamv70q20b", "v70", "pins-144", "flash-1M", "rev-b"] samv70q20b-rt = ["samv70q20b", "atsamv70q20b/rt"] -samv71j19 = ["atsamv71j19", "device-selected"] + +samv71j19 = ["atsamv71j19", "v71", "pins-64", "flash-512K", "rev-a"] samv71j19-rt = ["samv71j19", "atsamv71j19/rt"] -samv71j19b = ["atsamv71j19b", "device-selected"] +samv71j19b = ["atsamv71j19b", "v71", "pins-64", "flash-512K", "rev-b"] samv71j19b-rt = ["samv71j19b", "atsamv71j19b/rt"] -samv71j20 = ["atsamv71j20", "device-selected"] +samv71j20 = ["atsamv71j20", "v71", "pins-64", "flash-1M", "rev-a"] samv71j20-rt = ["samv71j20", "atsamv71j20/rt"] -samv71j20b = ["atsamv71j20b", "device-selected"] +samv71j20b = ["atsamv71j20b", "v71", "pins-64", "flash-1M", "rev-b"] samv71j20b-rt = ["samv71j20b", "atsamv71j20b/rt"] -samv71j21 = ["atsamv71j21", "device-selected"] +samv71j21 = ["atsamv71j21", "v71", "pins-64", "flash-2M", "rev-a"] samv71j21-rt = ["samv71j21", "atsamv71j21/rt"] -samv71j21b = ["atsamv71j21b", "device-selected"] +samv71j21b = ["atsamv71j21b", "v71", "pins-64", "flash-2M", "rev-b"] samv71j21b-rt = ["samv71j21b", "atsamv71j21b/rt"] -samv71n19 = ["atsamv71n19", "device-selected"] +samv71n19 = ["atsamv71n19", "v71", "pins-100", "flash-512K", "rev-a"] samv71n19-rt = ["samv71n19", "atsamv71n19/rt"] -samv71n19b = ["atsamv71n19b", "device-selected"] +samv71n19b = ["atsamv71n19b", "v71", "pins-100", "flash-512K", "rev-b"] samv71n19b-rt = ["samv71n19b", "atsamv71n19b/rt"] -samv71n20 = ["atsamv71n20", "device-selected"] +samv71n20 = ["atsamv71n20", "v71", "pins-100", "flash-1M", "rev-a"] samv71n20-rt = ["samv71n20", "atsamv71n20/rt"] -samv71n20b = ["atsamv71n20b", "device-selected"] +samv71n20b = ["atsamv71n20b", "v71", "pins-100", "flash-1M", "rev-b"] samv71n20b-rt = ["samv71n20b", "atsamv71n20b/rt"] -samv71n21 = ["atsamv71n21", "device-selected"] +samv71n21 = ["atsamv71n21", "v71", "pins-100", "flash-2M", "rev-a"] samv71n21-rt = ["samv71n21", "atsamv71n21/rt"] -samv71n21b = ["atsamv71n21b", "device-selected"] +samv71n21b = ["atsamv71n21b", "v71", "pins-100", "flash-2M", "rev-b"] samv71n21b-rt = ["samv71n21b", "atsamv71n21b/rt"] -samv71q19 = ["atsamv71q19", "device-selected"] +samv71q19 = ["atsamv71q19", "v71", "pins-144", "flash-512K", "rev-a"] samv71q19-rt = ["samv71q19", "atsamv71q19/rt"] -samv71q19b = ["atsamv71q19b", "device-selected"] +samv71q19b = ["atsamv71q19b", "v71", "pins-144", "flash-512K", "rev-b"] samv71q19b-rt = ["samv71q19b", "atsamv71q19b/rt"] -samv71q20 = ["atsamv71q20", "device-selected"] +samv71q20 = ["atsamv71q20", "v71", "pins-144", "flash-1M", "rev-a"] samv71q20-rt = ["samv71q20", "atsamv71q20/rt"] -samv71q20b = ["atsamv71q20b", "device-selected"] +samv71q20b = ["atsamv71q20b", "v71", "pins-144", "flash-1M", "rev-b"] samv71q20b-rt = ["samv71q20b", "atsamv71q20b/rt"] -samv71q21 = ["atsamv71q21", "device-selected"] +samv71q21 = ["atsamv71q21", "v71", "pins-144", "flash-2M", "rev-a"] samv71q21-rt = ["samv71q21", "atsamv71q21/rt"] -samv71q21b = ["atsamv71q21b", "device-selected"] +samv71q21b = ["atsamv71q21b", "v71", "pins-144", "flash-2M", "rev-b"] samv71q21b-rt = ["samv71q21b", "atsamv71q21b/rt"] diff --git a/hal/build.rs b/hal/build.rs new file mode 100644 index 00000000..f8f900c8 --- /dev/null +++ b/hal/build.rs @@ -0,0 +1,21 @@ +use std::env; + +fn main() -> Result<(), &'static str> { + // Refer to + // + fn feat(f: &str) -> bool { + env::var(format!( + "CARGO_FEATURE_{}", + f.to_ascii_uppercase().replace("-", "_") + )) + .is_ok() + } + + if !feat("device-selected") || ["e70", "s70", "v70", "v71"].iter().all(|&f| !feat(f)) { + return Err( + "The HAL is built for a specific target device selected using a feature, but no such a feature was selected." + ); + } + + Ok(()) +} diff --git a/hal/src/efc.rs b/hal/src/efc.rs new file mode 100644 index 00000000..be75ef14 --- /dev/null +++ b/hal/src/efc.rs @@ -0,0 +1,108 @@ +//! Flash controller configuration + +use crate::pmc::{Megahertz, PmcError}; +use crate::target_device::EFC; + +/// The voltage which drives the MCU. +/// +/// Refer to §58 and §59. +#[derive(PartialEq)] +pub enum VddioLevel { + /// VDDIO = 3.3V, typical + V3, + /// VDDIO = 1.7V, minimal + V1, +} + +pub struct Efc { + pub(crate) periph: EFC, + vddio: VddioLevel, +} + +impl Efc { + pub fn new(periph: EFC, vddio: VddioLevel) -> Self { + periph.eefc_wpmr.modify(|_r, w| { + w.wpkey().passwd(); + w.wpen().clear_bit(); + w + }); + + Self { periph, vddio } + } + + /// Calculates and sets the lowest possible number of flash wait + /// states from a given master clock frequency in MHz. + /// + /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, + /// which may go up to 300MHz. + pub fn set_wait_states(&mut self, freq: Megahertz) -> Result<(), PmcError> { + let fws = FlashWaitStates::calculate(freq, &self.vddio)?; + + self.periph + .eefc_fmr + .modify(|_r, w| unsafe { w.fws().bits(fws as u8) }); + + Ok(()) + } +} + +/// The number of flash wait states for a read operation. +/// +/// Note: The number of cycles a read takes is 1 + FWS. +#[derive(Debug, PartialEq, Copy, Clone)] +#[repr(u8)] +enum FlashWaitStates { + Zero, + One, + Two, + Three, + Four, + Five, + Six, +} + +impl FlashWaitStates { + pub fn calculate(freq: Megahertz, vddio: &VddioLevel) -> Result { + #[cfg(any(feature = "v70", feature = "v71"))] + if vddio == &VddioLevel::V1 { + // V70/V71 must be driven with VDDIO = 3.3V, typical + return Err(PmcError::InvalidConfiguration); + } + + Self::fws_from_freq(freq, vddio) + } + + fn fws_from_freq(freq: Megahertz, vddio: &VddioLevel) -> Result { + match vddio { + VddioLevel::V1 => { + // References: + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) + Ok(match freq.to_MHz() { + 0..=21 => Self::Zero, + 22..=42 => Self::One, + 43..=63 => Self::Two, + 64..=84 => Self::Three, + 85..=106 => Self::Four, + 107..=125 => Self::Five, + 126..=137 => Self::Six, + _ => return Err(PmcError::InvalidConfiguration), + }) + } + VddioLevel::V3 => { + // References: + // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) + Ok(match freq.to_MHz() { + 0..=23 => Self::Zero, + 24..=46 => Self::One, + 47..=69 => Self::Two, + 70..=92 => Self::Three, + 93..=115 => Self::Four, + 116..=138 => Self::Five, + 139..=150 => Self::Six, + _ => return Err(PmcError::InvalidConfiguration), + }) + } + } + } +} diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 1b8888d5..8798a1e1 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -3,11 +3,6 @@ pub use embedded_hal as ehal; pub use nb; -#[cfg(not(feature = "device-selected"))] -compile_error!( - "The HAL is built for a specific target device selected using a feature, but no such a feature was selected." -); - #[cfg(feature = "same70j19")] pub use atsame70j19 as target_device; #[cfg(feature = "same70j19b")] @@ -141,7 +136,11 @@ pub use atsamv71q21 as target_device; #[cfg(feature = "samv71q21b")] pub use atsamv71q21b as target_device; -#[cfg(feature = "device-selected")] +#[cfg(feature = "rev-b")] +pub mod efc; +#[cfg(feature = "rev-b")] +pub mod pmc; +#[cfg(feature = "rev-b")] pub mod serial; -#[cfg(feature = "device-selected")] +#[cfg(feature = "rev-b")] pub mod watchdog; diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs new file mode 100644 index 00000000..6783aab7 --- /dev/null +++ b/hal/src/pmc.rs @@ -0,0 +1,808 @@ +//! Clock hierarchy configuration +//! +//! This module handles the clock hierarchy configuration. It is based +//! upon trait tokens to statically limit which clocks can be +//! connected where, following what hardware supports (c.f. §31.3). +//! +//! Below is an example configuration that drives the host clock via +//! MAINCK, and PCK2 with PLLA, clocked at 96 MHz: +//! +//! ```ignore +//! let mut efc = { +//! use hal::efc::{Efc, VddioLevel}; +//! Efc::new(ctx.device.EFC, VddioLevel::V3) +//! }; +//! +//! // Configure the clock hierarchy +//! { +//! use hal::pmc::{ +//! HostClockConfig, MainCkSource, MckDivider, MckPrescaler, Megahertz, PckId, +//! PllaConfig, Pmc, +//! }; +//! +//! let mut pmc = Pmc::new(ctx.device.PMC); +//! let mainck = pmc +//! .get_mainck(MainCkSource::ExternalBypass(Megahertz::from_raw(12))) +//! .unwrap(); +//! let plla = pmc +//! .get_pllack(PllaConfig { div: 1, mult: 8 }, &mainck) +//! .unwrap(); +//! let _hclk = pmc +//! .get_hclk( +//! HostClockConfig { +//! pres: MckPrescaler::CLK_1, +//! div: MckDivider::EQ_PCK, +//! }, +//! &mainck, +//! &mut efc, +//! ) +//! .unwrap(); +//! +//! let _pck2 = pmc.get_pck(&plla, 0, PckId::Pck2); +//! } +//! ``` + +use crate::efc::Efc; +use crate::target_device::PMC; +use crate::target_device::SUPC; + +pub use fugit::{HertzU32 as Hertz, MegahertzU32 as Megahertz}; + +pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; +pub use crate::target_device::pmc::pmc_mckr::CSS_A as HCC_CSS; +pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; +pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; +pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; + +/// Output divider for UPLLCK. +#[derive(Debug, PartialEq, Clone)] +pub enum UpllDivider { + /// UPLLCK is divided by 1: input and output frequencies are + /// equal. + Div1, + /// UPLLCK is diveded by 2: output frequency is half of input + /// frequency. + Div2, +} + +/// Power Management Controller +pub struct Pmc { + pmc: PMC, +} + +/// Possible errors that can occur on PMC configuration. +#[derive(Debug, PartialEq, Clone)] +pub enum PmcError { + ClockingError(PeripheralIdentifier), + InvalidConfiguration, + UnimplementedError, + InternalError, +} + +/// The source of the Main Clock (MAINCK) +/// +/// Refer to §60.2.1. +#[derive(Debug, PartialEq, Clone)] +pub enum MainCkSource { + /// Internal "Main RC" oscillator. + InternalRC(MainRcFreq), + /// External crystal powered by the MCU and connected to XIN and + /// XOUT. + ExternalNormal(Megahertz), + /// External clock signal connected to XIN, XOUT potentially + /// unconnected. Bypasses the oscillator otherwise used when using + /// [MainCkSource::ExternalNormal]. + ExternalBypass(Megahertz), +} + +/// MAINCK token +pub struct MainClock { + freq: Megahertz, +} + +/// The source of the Slow Clock (SLCK) +/// +/// Refer to §23.4.2 and §60.2.1. +pub enum SlowCkSource { + /// Internal "Slow RC" oscillator. + InternalRC, + /// External crystal powered by the MCU and connected to XIN32 and + /// XOUT32. + ExternalNormal, + /// External clock signal connected to XIN32, XOUT32 potentially + /// unconnected. Bypasses the oscillator otherwise used when using + /// [SlowCkSource::ExternalNormal]. + ExternalBypass, +} + +/// SCLK token +pub struct SlowClock { + freq: Hertz, +} + +/// PLLA configuration +pub struct PllaConfig { + pub div: u8, + pub mult: u8, +} + +/// PLLA token +pub struct PllaClock { + freq: Hertz, +} + +/// UPLLCK token +pub struct UpllClock { + freq: Megahertz, +} + +/// UPLLCKDIV token +pub struct UpllDivClock { + freq: Megahertz, +} + +/// HCLK/MCK configuration +pub struct HostClockConfig { + /// General prescaler that affects HCLK, SysTick, FCLK, MCK and + /// peripheral clocks. + pub pres: MckPrescaler, + /// Divider that only affects MCK and peripheral clocks. + pub div: MckDivider, +} + +/// MCK token +pub struct HostClock {} + +/// HCLK token +pub struct ProcessorClock {} + +/// PCK token +pub struct Pck { + #[allow(dead_code)] + id: PckId, +} + +/// PCK to configure +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum PckId { + Pck0, + Pck1, + Pck2, + Pck3, + Pck4, + Pck5, + Pck6, + Pck7, +} + +#[doc(hidden)] +pub trait UpllDivSource { + fn freq(&self) -> Megahertz; +} + +impl UpllDivSource for UpllClock { + fn freq(&self) -> Megahertz { + self.freq + } +} + +#[doc(hidden)] +pub trait UpllSource { + fn freq(&self) -> Megahertz; +} + +impl UpllSource for MainClock { + fn freq(&self) -> Megahertz { + self.freq + } +} + +#[doc(hidden)] +pub trait PllaSource { + fn freq(&self) -> Megahertz; +} + +impl PllaSource for MainClock { + fn freq(&self) -> Megahertz { + self.freq + } +} + +#[doc(hidden)] +pub trait HostClockSource { + const HCC_CSS: HCC_CSS; + + fn freq(&self) -> Hertz; +} + +impl HostClockSource for SlowClock { + const HCC_CSS: HCC_CSS = HCC_CSS::SLOW_CLK; + + fn freq(&self) -> Hertz { + self.freq + } +} +impl HostClockSource for MainClock { + const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; + + fn freq(&self) -> Hertz { + self.freq.convert() + } +} +impl HostClockSource for PllaClock { + const HCC_CSS: HCC_CSS = HCC_CSS::PLLA_CLK; + + fn freq(&self) -> Hertz { + self.freq.convert() + } +} +impl HostClockSource for UpllDivClock { + const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; + + fn freq(&self) -> Hertz { + self.freq.convert() + } +} + +#[doc(hidden)] +pub trait PckSource { + const PCK_CSS: PCK_CSS; +} + +impl PckSource for SlowClock { + const PCK_CSS: PCK_CSS = PCK_CSS::SLOW_CLK; +} +impl PckSource for MainClock { + const PCK_CSS: PCK_CSS = PCK_CSS::MAIN_CLK; +} +impl PckSource for UpllDivClock { + const PCK_CSS: PCK_CSS = PCK_CSS::UPLL_CLK; +} +impl PckSource for PllaClock { + const PCK_CSS: PCK_CSS = PCK_CSS::PLLA_CLK; +} +impl PckSource for HostClock { + const PCK_CSS: PCK_CSS = PCK_CSS::MCK; +} + +impl Pmc { + /// Time to wait until an observed clock is stable, from the + /// perpective of SLCK (@ 32.768KHz). + /// + /// Refer to §31.20.8, for example. + const WAIT_UNTIL_STABLE_62_MILLISECS: u8 = u8::MAX; + + const SLCK_FREQ: Hertz = Hertz::from_raw(32_768); + const UPLL_FREQ: Megahertz = Megahertz::from_raw(480); + + pub fn new(pmc: PMC) -> Self { + pmc.pmc_wpmr.modify(|_r, w| { + w.wpkey().passwd(); + w.wpen().clear_bit(); + w + }); + + Self { pmc } + } + + /// Configures SLCK and returns a corresponding Clock Token. + /// + /// Note: Changes to The SLCK source cannot be unmade in software. + pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowCkSource) -> SlowClock { + match source { + SlowCkSource::InternalRC => (), + SlowCkSource::ExternalNormal => { + supc.supc_cr.write(|w| { + w.xtalsel().set_bit(); + w.key().passwd() + }); + } + SlowCkSource::ExternalBypass => { + supc.supc_mr.modify(|_, w| { + w.oscbypass().set_bit(); + w.key().passwd() + }); + supc.supc_cr.write(|w| { + w.xtalsel().set_bit(); + w.key().passwd() + }); + } + } + + SlowClock { + freq: Self::SLCK_FREQ, + } + } + + /// Configures MAINCK and returns a corresponding Clock Token. + /// This Method corresponds to Steps 2-4 in 31.17 Recommended Programming Sequence. + pub fn get_mainck(&mut self, source: MainCkSource) -> Result { + // Refer to §31.20.8, §31.20.16 + + let freq = match source { + MainCkSource::InternalRC(freq) => { + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscsel().clear_bit(); + w.moscrcen().set_bit(); + w.moscrcf().variant(freq); + w + }); + + // TODO hande note for MOSCRCF unhandled (p. 276; + // first table, second row) + + // Wait until clock is stable. + while self.pmc.pmc_sr.read().moscrcs().bit_is_clear() {} + + Megahertz::from_raw(match freq { + MainRcFreq::_4_MHZ => 4, + MainRcFreq::_8_MHZ => 8, + MainRcFreq::_12_MHZ => 12, + }) + } + MainCkSource::ExternalNormal(freq) => { + // Clock signal frequency needs to be between 3 and + // 20MHz (§30.2). + if freq.to_MHz() < 3 || freq.to_MHz() > 20 { + return Err(PmcError::InvalidConfiguration); + } + + // Enable the external oscillator and wait for it to + // stabilize. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscxten().set_bit(); + unsafe { + w.moscxtst().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); + } + w + }); + while self.pmc.pmc_sr.read().moscxts().bit_is_clear() {} + + // Switch over to the main oscillator. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscsel().set_bit(); + w + }); + while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} + + // TODO check MAINCK frequency (§31.17; step 5). + + freq + } + MainCkSource::ExternalBypass(freq) => { + // Crystal frequency needs to be between 3 and 20MHz + // (§30.2). + if freq.to_MHz() < 3 || freq.to_MHz() > 20 { + return Err(PmcError::InvalidConfiguration); + } + + // Bypass the main crystal oscillator and disable it. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscxtby().set_bit(); + w.moscxten().clear_bit(); + unsafe { + w.moscxtst().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); + } + w + }); + + // Wait until oscillator is stable. + while self.pmc.pmc_sr.read().moscxts().bit_is_clear() {} + + // Switch over to the external clock. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscsel().set_bit(); + w + }); + while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} + + freq + } + }; + + Ok(MainClock { freq }) + } + + /// Configures PLLACK and returns a corresponding clock token. + /// This method corresponds to Step 6 of 31.17 Recommended Programming Sequence. + pub fn get_pllack( + &mut self, + PllaConfig { div, mult }: PllaConfig, + source: &SRC, + ) -> Result { + if mult > 63 || mult < 2 { + return Err(PmcError::InvalidConfiguration); + } + if div == 0 || div > 127 { + return Err(PmcError::InvalidConfiguration); + } + // TODO: Ensure valid requested output frequency. + + // Configure PLLA and wait for lock. + self.pmc.ckgr_pllar.modify(|_, w| { + w.one().set_bit(); + unsafe { + w.mula().bits(mult as u16 - 1); // HW adds 1 + w.diva().bits(div); + } + w + }); + while self.pmc.pmc_sr.read().locka().bit_is_clear() {} + + Ok(PllaClock { + freq: (source.freq().convert() / div as u32) * mult as u32, + }) + } + + /// Configures UPLLCK + pub fn get_upllck( + &mut self, + source: &SRC, + utmi: &mut crate::target_device::UTMI, + ) -> Result { + use crate::target_device::utmi::utmi_cktrim::FREQ_A as FREQ; + + let freq = match source.freq().to_MHz() { + 12 => FREQ::XTAL12, + 16 => FREQ::XTAL16, + _ => return Err(PmcError::InvalidConfiguration), + }; + + // Configure the UTMI PLL clock and wait for lock. + utmi.utmi_cktrim.modify(|_, w| w.freq().variant(freq)); + self.pmc.ckgr_uckr.modify(|_, w| { + w.upllen().set_bit(); + unsafe { + w.upllcount().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); + } + w + }); + while self.pmc.pmc_sr.read().locku().bit_is_clear() {} + + Ok(UpllClock { + freq: Self::UPLL_FREQ, + }) + } + + /// Configures UPLLCKDIV + pub fn get_upllckdiv( + &mut self, + source: &SRC, + div: UpllDivider, + ) -> UpllDivClock { + self.pmc + .pmc_mckr + .modify(|_, w| w.uplldiv2().bit(div == UpllDivider::Div2)); + + UpllDivClock { + freq: source.freq() + / match div { + UpllDivider::Div1 => 1, + UpllDivider::Div2 => 2, + }, + } + } + + /// Configures HCLK and MCK and returns corresponding Clock Tokens. + /// This method corresponds to Step 7 in 31.17. + pub fn get_hclk( + &mut self, + HostClockConfig { pres, div }: HostClockConfig, + source: &SRC, + efc: &mut Efc, + ) -> Result<(ProcessorClock, HostClock), PmcError> { + // Ensure we use the correct amount of wait states for flash + // access for the new HCLK frequency. + efc.set_wait_states( + source.freq().convert() + / match pres { + MckPrescaler::CLK_1 => 1, + MckPrescaler::CLK_2 => 2, + MckPrescaler::CLK_3 => 3, + MckPrescaler::CLK_4 => 4, + MckPrescaler::CLK_8 => 8, + MckPrescaler::CLK_16 => 16, + MckPrescaler::CLK_32 => 32, + MckPrescaler::CLK_64 => 64, + }, + )?; + + let source = SRC::HCC_CSS; + + match source { + HCC_CSS::PLLA_CLK | HCC_CSS::UPLL_CLK => { + self.pmc.pmc_mckr.modify(|_, w| w.pres().variant(pres)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.mdiv().variant(div)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + } + HCC_CSS::MAIN_CLK | HCC_CSS::SLOW_CLK => { + self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.pres().variant(pres)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + // XXX: Not a part of the RPS: is it a noop? Or is + // MDIV static? + self.pmc.pmc_mckr.modify(|_, w| w.mdiv().variant(div)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + } + } + + Ok((ProcessorClock {}, HostClock {})) + } + + /// Configures PCKx and returns a token. + /// Corresponds to Step 8 in 31.17 + pub fn get_pck(&mut self, _source: &SRC, pres: u8, id: PckId) -> Pck { + self.pmc.pmc_pck[id as usize].write(|w| unsafe { + w.pres().bits(pres); + w.css().bits(SRC::PCK_CSS as u8) + }); + self.pmc + .pmc_scer + .write(|w| unsafe { w.bits(1 << (id as u8 + 8)) }); + while (self.pmc.pmc_scsr.read().bits() & (1 << (id as u8 + 8))) == 0 {} + Pck { id } + } + + pub fn enable_peripherals(&mut self, pids: &[PeripheralIdentifier]) -> Result<(), PmcError> { + if pids.is_empty() { + return Ok(()); + } + + let pcsr0 = self.pmc.pmc_pcsr0.read().bits(); + let pcsr1 = self.pmc.pmc_pcsr1.read().bits(); + + let mut pcr0 = 0; + let mut pcr1 = 0; + + for pid in pids { + // Check if this supports PMC clocking + pid.supports_pmc_clocking() + .map_err(|_| PmcError::ClockingError(*pid))?; + + let pid_val: u32 = (*pid) as u32; + + match pid_val { + 7..=31 => { + let mask = 1 << pid_val; + let pre_set = (pcsr0 & mask) != 0; + let dup_set = (pcr0 & mask) != 0; + + if pre_set || dup_set { + // defmt::warn!("[PMC] Duplicate Clock Enable: {}", pid); + } + + pcr0 |= mask; + } + 32..=63 => { + let mask = 1 << (pid_val - 32); + + let pre_set = (pcsr1 & mask) != 0; + let dup_set = (pcr1 & mask) != 0; + + if pre_set || dup_set { + // defmt::warn!("[PMC] Duplicate Clock Enable: {}", pid); + } + + pcr1 |= mask; + } + 69 | 70 => { + // I don't know how to enable these peripherals yet + return Err(PmcError::UnimplementedError); + } + _ => { + // This should be impossible, and probably means there is an + // error in the `supports_pmc_clocking()` function + return Err(PmcError::InternalError); + } + } + } + + // Enable the newly set peripherals + self.pmc.pmc_pcer0.write(|w| unsafe { w.bits(pcr0) }); + self.pmc.pmc_pcer1.write(|w| unsafe { w.bits(pcr1) }); + + Ok(()) + } +} + +/// Identifiers for peripherals +#[allow(non_camel_case_types)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[repr(u32)] +pub enum PeripheralIdentifier { + /// 0 (NVIC + !PMC CC) Supply Controller + SUPC = 0, + /// 1 (NVIC + !PMC CC) Reset Controller + RSTC = 1, + /// 2 (NVIC + !PMC CC) Real Time Clock + RTC = 2, + /// 3 (NVIC + !PMC CC) Real Time Timer + RTT = 3, + /// 4 (NVIC + !PMC CC) Watchdog Timer + WDT = 4, + /// 5 (NVIC + !PMC CC) Power Management Controller + PMC = 5, + /// 6 (NVIC + !PMC CC) Enhanced Embedded Flash Controller + EFC = 6, + /// 7 (NVIC + PMC CC) Universal Asynchronous Receiver/Transmitter + UART0 = 7, + /// 8 (NVIC + PMC CC) Universal Asynchronous Receiver/Transmitter + UART1 = 8, + /// 9 (!NVIC + PMC CC) Static Memory Controller + SMC = 9, + /// 10 (NVIC + PMC CC) Parallel I/O Controller A + PIOA = 10, + /// 11 (NVIC + PMC CC) Parallel I/O Controller B + PIOB = 11, + /// 12 (NVIC + PMC CC) Parallel I/O Controller C + PIOC = 12, + /// 13 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART0 = 13, + /// 14 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART1 = 14, + /// 15 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART2 = 15, + /// 16 (NVIC + PMC CC) Parallel I/O Controller D + PIOD = 16, + /// 17 (NVIC + PMC CC) Parallel I/O Controller E + PIOE = 17, + /// 18 (NVIC + PMC CC) Multimedia Card Interface + HSMCI = 18, + /// 19 (NVIC + PMC CC) Two-wire Interface (I2C-compatible) + TWIHS0 = 19, + /// 20 (NVIC + PMC CC) Two-wire Interface (I2C-compatible) + TWIHS1 = 20, + /// 21 (NVIC + PMC CC) Serial Peripheral Interface + SPI0 = 21, + /// 22 (NVIC + PMC CC) Synchronous Serial Controller + SSC = 22, + /// 23 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 0 + TC0_CHANNEL0 = 23, + /// 24 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 1 + TC0_CHANNEL1 = 24, + /// 25 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 2 + TC0_CHANNEL2 = 25, + /// 26 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 0 + TC1_CHANNEL0 = 26, + /// 27 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 1 + TC1_CHANNEL1 = 27, + /// 28 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 2 + TC1_CHANNEL2 = 28, + /// 29 (NVIC + PMC CC) Analog Front-End Controller + AFEC0 = 29, + /// 30 (NVIC + PMC CC) Digital-to-Analog Converter + DACC = 30, + /// 31 (NVIC + PMC CC) Pulse Width Modulation Controller + PWM0 = 31, + /// 32 (NVIC + PMC CC) Integrity Check Monitor + ICM = 32, + /// 33 (NVIC + PMC CC) Analog Comparator Controller + ACC = 33, + /// 34 (NVIC + PMC CC) USB Host / Device Controller + USBHS = 34, + /// 35 (NVIC + PMC CC) CAN IRQ Line 0 + MCAN0 = 35, + /// 36 (NVIC + !PMC CC) CAN IRQ Line 1 + MCAN0INT1 = 36, + /// 37 (NVIC + PMC CC) CAN IRQ Line 0 + MCAN1 = 37, + /// 38 (NVIC + !PMC CC) CAN IRQ Line 1 + MCAN1INT1 = 38, + /// 39 (NVIC + PMC CC) Ethernet MAC + GMAC = 39, + /// 40 (NVIC + PMC CC) Analog Front End Controller + AFEC1 = 40, + /// 41 (NVIC + PMC CC) Two-wire Interface + TWIHS2 = 41, + /// 42 (NVIC + PMC CC) Serial Peripheral Interface + SPI1 = 42, + /// 43 (NVIC + PMC CC) Quad I/O Serial Peripheral Interface + QSPI = 43, + /// 44 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART2 = 44, + /// 45 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART3 = 45, + /// 46 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART4 = 46, + /// 47 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 0 + TC2_CHANNEL0 = 47, + /// 48 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 1 + TC2_CHANNEL1 = 48, + /// 49 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 2 + TC2_CHANNEL2 = 49, + /// 50 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 0 + TC3_CHANNEL0 = 50, + /// 51 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 1 + TC3_CHANNEL1 = 51, + /// 52 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 2 + TC3_CHANNEL2 = 52, + /// 53 (NVIC + PMC CC) MediaLB IRQ 0 + MLB_IRQ0 = 53, + /// 54 (NVIC + !PMC CC) MediaLB IRQ 1 + MLB_IRQ1 = 54, + /// 55 (NVIC + !PMC CC) Reserved + _RESERVED = 55, + /// 56 (NVIC + PMC CC) Advanced Encryption Standard + AES = 56, + /// 57 (NVIC + PMC CC) True Random Number Generator + TRNG = 57, + /// 58 (NVIC + PMC CC) DMA Controller + XDMAC = 58, + /// 59 (NVIC + PMC CC) Image Sensor Interface + ISI = 59, + /// 60 (NVIC + PMC CC) Pulse Width Modulation Controller + PWM1 = 60, + /// 61 (NVIC:FPU + !PMC CC) ARM Floating Point Unit interrupt associated with OFC, UFC, IOC, DZC and IDC bits + ARM = 61, + /// 62 (NVIC + !PMC CC) SDRAM Controller + SDRAMC = 62, + /// 63 (NVIC + !PMC CC) Reinforced Safety Watchdog Timer + RSWDT = 63, + /// 64 (NVIC:CCW + !PMC CC) ARM Cache ECC Warning + ARM_CACHE_ECC_WARNING = 64, + /// 65 (NVIC:CCF + !PMC CC) ARM Cache ECC Fault + ARM_CACHE_ECC_FAULT = 65, + /// 66 (NVIC:Q1 + !PMC CC) GMAC Queue 1 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 1 + GMAC_Q1 = 66, + /// 67 (NVIC:Q2 + !PMC CC) GMAC Queue 2 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 2 + GMAC_Q2 = 67, + /// 68 (NVIC:IX + !PMC CC) –Floating Point Unit Interrupt IXC associated with FPU cumulative exception bit + ARM_FPU_IXC_FPU = 68, + /// 69 (NVIC + PMC CC) Inter-IC Sound Controller + // TODO: How does this get enabled with PMC? The PMC bits only go up to 63... + I2SC0 = 69, + /// 70 (NVIC + PMC CC) Inter-IC Sound Controller + // TODO: How does this get enabled with PMC? The PMC bits only go up to 63... + I2SC1 = 70, + /// 71 (NVIC:Q3 + !PMC CC) GMAC Queue 3 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 3 + GMAC_Q3 = 71, + /// 72 (NVIC:Q4 + !PMC CC) GMAC Queue 4 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 4 + GMAC_Q4 = 72, + /// 73 (NVIC:Q5 + !PMC CC) GMAC Queue 5 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 5 + GMAC_Q5 = 73, +} + +impl PeripheralIdentifier { + pub fn supports_pmc_clocking(&self) -> Result<(), ()> { + use PeripheralIdentifier::*; + // These pids don't support PMC clocking + match self { + SUPC => Err(()), + RSTC => Err(()), + RTC => Err(()), + RTT => Err(()), + WDT => Err(()), + PMC => Err(()), + EFC => Err(()), + MCAN0INT1 => Err(()), + MCAN1INT1 => Err(()), + MLB_IRQ1 => Err(()), + _RESERVED => Err(()), + ARM => Err(()), + SDRAMC => Err(()), + RSWDT => Err(()), + ARM_CACHE_ECC_WARNING => Err(()), + ARM_CACHE_ECC_FAULT => Err(()), + GMAC_Q1 => Err(()), + GMAC_Q2 => Err(()), + ARM_FPU_IXC_FPU => Err(()), + GMAC_Q3 => Err(()), + GMAC_Q4 => Err(()), + GMAC_Q5 => Err(()), + _ => Ok(()), + } + } +} diff --git a/hal/src/serial.rs b/hal/src/serial.rs index 2eccf881..a678ab11 100644 --- a/hal/src/serial.rs +++ b/hal/src/serial.rs @@ -2,35 +2,15 @@ use crate::{ehal, nb}; // Smaller part have 3x UART & 2x USART use crate::target_device::{UART0, UART1, UART2, USART0, USART1}; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] use crate::target_device::{UART3, USART2}; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] use crate::target_device::UART4; @@ -46,54 +26,24 @@ pub type Serial0 = Serial; pub type Serial1 = Serial; pub type Serial2 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial3 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial4 = Serial; pub type Serial5 = Serial; pub type Serial6 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial7 = Serial; @@ -132,12 +82,7 @@ impl ehal::serial::Write for Serial { } } -#[cfg(any( - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70q20b", - feature = "same70q21b", -))] +#[cfg(all(any(feature = "e70", feature = "s70"), feature = "pins-100"))] impl ehal::serial::Write for Serial { type Error = Error;