From f72ca39eaaffa4fd19569571356d6bf849be14c1 Mon Sep 17 00:00:00 2001 From: Felix Lelchuk Date: Wed, 26 Apr 2023 23:21:22 +0200 Subject: [PATCH] Add `feather_m4_can` board, add `mcan` example - the new `feather_m4_can` board is added as a clone of `feather_m4` with just the chip changed ATSAM(D->E)51J - adapt pinmux: alternate function pins for CAN1 connecting to TCAN1051 transceiver - adapt `mcan` example from `xplain` board replacing button triggering by a periodic task --- boards/feather_m4_can/.cargo/config | 15 + boards/feather_m4_can/CHANGELOG.md | 9 + boards/feather_m4_can/Cargo.toml | 101 ++ boards/feather_m4_can/README.md | 26 + boards/feather_m4_can/build.rs | 16 + .../feather_m4_can/examples/blinky_basic.rs | 37 + boards/feather_m4_can/examples/clocking_v2.rs | 171 +++ boards/feather_m4_can/examples/dmac.rs | 103 ++ boards/feather_m4_can/examples/i2c.rs | 83 ++ boards/feather_m4_can/examples/mcan.rs | 310 ++++++ .../examples/neopixel_rainbow.rs | 67 ++ boards/feather_m4_can/examples/nvm_dsu.rs | 245 +++++ boards/feather_m4_can/examples/pukcc_test.rs | 998 ++++++++++++++++++ boards/feather_m4_can/examples/pwm.rs | 58 + boards/feather_m4_can/examples/serial.rs | 55 + .../examples/sleeping_timer_rtc.rs | 91 ++ .../feather_m4_can/examples/smart_eeprom.rs | 225 ++++ boards/feather_m4_can/examples/timers.rs | 51 + boards/feather_m4_can/examples/trng.rs | 49 + boards/feather_m4_can/examples/uart.rs | 104 ++ .../feather_m4_can/examples/uart_poll_echo.rs | 77 ++ boards/feather_m4_can/examples/usb_echo.rs | 114 ++ boards/feather_m4_can/memory.x | 8 + boards/feather_m4_can/src/lib.rs | 295 ++++++ crates.json | 5 + 25 files changed, 3313 insertions(+) create mode 100644 boards/feather_m4_can/.cargo/config create mode 100644 boards/feather_m4_can/CHANGELOG.md create mode 100644 boards/feather_m4_can/Cargo.toml create mode 100644 boards/feather_m4_can/README.md create mode 100644 boards/feather_m4_can/build.rs create mode 100644 boards/feather_m4_can/examples/blinky_basic.rs create mode 100644 boards/feather_m4_can/examples/clocking_v2.rs create mode 100644 boards/feather_m4_can/examples/dmac.rs create mode 100644 boards/feather_m4_can/examples/i2c.rs create mode 100644 boards/feather_m4_can/examples/mcan.rs create mode 100644 boards/feather_m4_can/examples/neopixel_rainbow.rs create mode 100644 boards/feather_m4_can/examples/nvm_dsu.rs create mode 100644 boards/feather_m4_can/examples/pukcc_test.rs create mode 100644 boards/feather_m4_can/examples/pwm.rs create mode 100644 boards/feather_m4_can/examples/serial.rs create mode 100644 boards/feather_m4_can/examples/sleeping_timer_rtc.rs create mode 100644 boards/feather_m4_can/examples/smart_eeprom.rs create mode 100644 boards/feather_m4_can/examples/timers.rs create mode 100644 boards/feather_m4_can/examples/trng.rs create mode 100644 boards/feather_m4_can/examples/uart.rs create mode 100644 boards/feather_m4_can/examples/uart_poll_echo.rs create mode 100644 boards/feather_m4_can/examples/usb_echo.rs create mode 100644 boards/feather_m4_can/memory.x create mode 100644 boards/feather_m4_can/src/lib.rs diff --git a/boards/feather_m4_can/.cargo/config b/boards/feather_m4_can/.cargo/config new file mode 100644 index 000000000000..9525ef126c86 --- /dev/null +++ b/boards/feather_m4_can/.cargo/config @@ -0,0 +1,15 @@ +# vim:ft=toml: +[target.thumbv7em-none-eabihf] +runner = 'arm-none-eabi-gdb' +#runner = 'probe-run --chip ATSAME51J19A' + +[build] +target = "thumbv7em-none-eabihf" +rustflags = [ + + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", + + "-C", "link-arg=-Tlink.x", +] diff --git a/boards/feather_m4_can/CHANGELOG.md b/boards/feather_m4_can/CHANGELOG.md new file mode 100644 index 000000000000..2d6849b663b5 --- /dev/null +++ b/boards/feather_m4_can/CHANGELOG.md @@ -0,0 +1,9 @@ +# v0.1.0 + +- The board is added as a clone of `feather_m4` with chip changed ATSAM(D->E)51J +- adapt pinmux: alternate function pins for CAN1 connecting to TCAN1051 transceiver +- clone `mcan` example from `atsame54_xpro` board (using periodic task instead of button) + +--- + +Changelog tracking started at v0.1.0 diff --git a/boards/feather_m4_can/Cargo.toml b/boards/feather_m4_can/Cargo.toml new file mode 100644 index 000000000000..94ffe5d61568 --- /dev/null +++ b/boards/feather_m4_can/Cargo.toml @@ -0,0 +1,101 @@ +[package] +name = "feather_m4_can" +version = "0.1.0" +edition = "2021" +authors = ["Theodore DeRego "] +description = "Board Support crate for the Adafruit Feather M4 CAN" +keywords = ["no-std", "arm", "cortex-m", "embedded-hal", "can"] +categories = ["embedded", "hardware-support", "no-std"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/atsamd-rs/atsamd" +readme = "README.md" +documentation = "https://atsamd-rs.github.io/atsamd/atsamd51j/feather_m4_can/" + +# for cargo flash +[package.metadata] +chip = "ATSAME51J19A" + +[dependencies.cortex-m-rt] +version = "0.7" +optional = true + +[dependencies.atsamd-hal] +path = "../../hal" +version = "0.15.1" +default-features = false + +[dependencies.usb-device] +version = "0.2" +optional = true + +[dev-dependencies] +mcan = "0.2" +dwt-systick-monotonic = "1.1" +panic-rtt-target = { version = "0.1", features = ["cortex-m"] } +rtt-target = { version = "0.3", features = ["cortex-m"] } +cortex-m = "0.7" +usbd-serial = "0.1" +cortex-m-rtic = "1.1" +panic-halt = "0.2" +panic-semihosting = "0.5" +smart-leds = "0.3" +ws2812-timer-delay = "0.3" +heapless = "0.7" + +[features] +# ask the HAL to enable atsame51j support +default = ["rt", "atsamd-hal/same51j", "atsamd-hal/same51"] +rt = ["cortex-m-rt", "atsamd-hal/same51j-rt"] +unproven = ["atsamd-hal/unproven"] +usb = ["atsamd-hal/usb", "usb-device"] +can = ["atsamd-hal/can"] +dma = ["atsamd-hal/dma", "unproven"] +max-channels = ["dma", "atsamd-hal/dma"] + + +[profile.dev] +incremental = false +codegen-units = 1 +debug = true +lto = true + +[profile.release] +debug = true +lto = true +opt-level = "s" + +[[example]] +name = "pwm" +required-features = ["unproven"] + +[[example]] +name = "usb_echo" +required-features = ["usb"] + +[[example]] +name = "dmac" +required-features = ["dma"] + +[[example]] +name = "uart" +required-features = ["dma"] + +[[example]] +name = "pukcc_test" +required-features = ["unproven", "usb"] + +[[example]] +name = "nvm_dsu" +required-features = ["unproven", "usb"] + +[[example]] +name = "smart_eeprom" +required-features = ["unproven", "usb"] + +[[example]] +name = "i2c" +required-features = ["atsamd-hal/dma"] + +[[example]] +name = "mcan" +required-features = ["can"] diff --git a/boards/feather_m4_can/README.md b/boards/feather_m4_can/README.md new file mode 100644 index 000000000000..32c47c6d8ef4 --- /dev/null +++ b/boards/feather_m4_can/README.md @@ -0,0 +1,26 @@ +# Adafruit Feather M4 Board Support Crate + +This crate provides a type-safe API for working with the [Adafruit Feather M4 +board](https://www.adafruit.com/product/3857). + +## Prerequisites +* Install the cross compile toolchain `rustup target add thumbv7em-none-eabihf` +* Install [cargo-hf2 the hf2 bootloader flasher tool](https://crates.io/crates/cargo-hf2) however your platform requires + +## Uploading an example +Check out the repository for examples: + +https://github.com/atsamd-rs/atsamd/tree/master/boards/feather_m4_can/examples + +* Be in this directory `cd boards/feather_m4_can` +* Put your device in bootloader mode usually by hitting the reset button twice. +* Build and upload in one step +``` +$ cargo hf2 --release --example blinky_basic + Finished release [optimized + debuginfo] target(s) in 0.19s + Searching for a connected device with known vid/pid pair. + Trying Ok(Some("Adafruit Industries")) Ok(Some("PyBadge")) + Flashing "/Users/User/atsamd/boards/feather_m4_can/target/thumbv7em-none-eabihf/release/examples/blinky_basic" + Finished in 0.079s +$ +``` diff --git a/boards/feather_m4_can/build.rs b/boards/feather_m4_can/build.rs new file mode 100644 index 000000000000..4bed4688f2c0 --- /dev/null +++ b/boards/feather_m4_can/build.rs @@ -0,0 +1,16 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +fn main() { + if env::var_os("CARGO_FEATURE_RT").is_some() { + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=memory.x"); + } + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/boards/feather_m4_can/examples/blinky_basic.rs b/boards/feather_m4_can/examples/blinky_basic.rs new file mode 100644 index 000000000000..26d182ed1306 --- /dev/null +++ b/boards/feather_m4_can/examples/blinky_basic.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] + +use feather_m4_can as bsp; +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::hal; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + let mut delay = Delay::new(core.SYST, &mut clocks); + loop { + delay.delay_ms(2000u16); + red_led.set_high().unwrap(); + delay.delay_ms(2000u16); + red_led.set_low().unwrap(); + } +} diff --git a/boards/feather_m4_can/examples/clocking_v2.rs b/boards/feather_m4_can/examples/clocking_v2.rs new file mode 100644 index 000000000000..821e471b3a06 --- /dev/null +++ b/boards/feather_m4_can/examples/clocking_v2.rs @@ -0,0 +1,171 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +use core::fmt::Write as _; + +use atsamd_hal::{ + clock::v2::{ + self as clock, + dpll::Dpll, + gclk::{Gclk, GclkDiv16, GclkDiv8}, + osculp32k::OscUlp32k, + pclk::Pclk, + rtcosc::RtcOsc, + xosc32k::{ControlGainMode, Xosc1k, Xosc32k, Xosc32kBase}, + }, + ehal::serial::Read as _, + ehal::serial::Write, + gpio::{Pins, PA04, PA05}, + rtc::{ClockMode, Rtc}, + sercom::{ + uart::{self, BaudMode, Flags, Oversampling}, + IoSet3, Sercom0, + }, + time::U32Ext, +}; + +use rtic::app; + +type Pads = uart::PadsFromIds; +type Uart = uart::Uart, uart::Duplex>; + +#[app(device = atsamd_hal::pac, peripherals = true)] +mod app { + use super::*; + + #[shared] + struct SharedResources { + uart: Uart, + rtc: Rtc, + } + + #[local] + struct LocalResources {} + + #[init] + fn init(cx: init::Context) -> (SharedResources, LocalResources, init::Monotonics()) { + let mut device = cx.device; + + // Get the clocks & tokens + let (_buses, clocks, tokens) = clock::clock_system_at_reset( + device.OSCCTRL, + device.OSC32KCTRL, + device.GCLK, + device.MCLK, + &mut device.NVMCTRL, + ); + + // This is required because the `sercom` and `rtc` modules have not yet + // been update to use `clock::v2` + let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() }; + + // Get the pins + let pins = Pins::new(device.PORT); + + // Take `Dfll` 48 MHz, divide down to `2 MHz` through `Gclk1` + let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); + let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); + + // Output `Gclk1` on PB15 pin + let (gclk1, _gclk1_out) = gclk1.enable_gclk_out(pins.pb15); + + // Setup a peripheral channel to power up `Dpll0` from `Gclk1` + let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); + + // Configure `Dpll0` with `2 * 60 + 0/32 = 120 MHz` frequency + let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0) + .loop_div(60, 0) + .enable(); + + // Swap source of `Gclk0` from Dfll to Dpll0, `48 Mhz -> 120 MHz` + let (gclk0, _dfll, _dpll0) = clocks.gclk0.swap_sources(dfll, dpll0); + + // Output `Gclk0` on pin PB14 + let (gclk0, _gclk0_out) = gclk0.enable_gclk_out(pins.pb14); + + // Setup a peripheral channel to power up `Dpll1` from `Gclk1` + let (pclk_dpll1, _gclk1) = Pclk::enable(tokens.pclks.dpll1, gclk1); + + // Configure `Dpll1` with `2 * 50 + 0/32 = 100 MHz` frequency + let dpll1 = Dpll::from_pclk(tokens.dpll1, pclk_dpll1) + .loop_div(50, 0) + .enable(); + + // Output `Dpll1` on PB20 pin via `Gclk6`, divided by 200 resulting in 0.5 MHz + // output frequency + let (gclk6, _dpll1) = Gclk::from_source(tokens.gclks.gclk6, dpll1); + let gclk6 = gclk6.div(GclkDiv8::Div(200)).enable(); + let (_gclk6, _gclk6_out) = gclk6.enable_gclk_out(pins.pb12); + + // Configure `Xosc32k` with both outputs (1kHz, 32kHz) activated + let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) + .control_gain_mode(ControlGainMode::HighSpeed) + .on_demand(false) + .run_standby(true) + .enable(); + let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); + let (xosc32k, _xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); + + // Output `Xosc32k` on PB16 pin via `Gclk2`, divided by 2 resulting in 16 kHz + // output frequency + let (gclk2, _xosc32k) = Gclk::from_source(tokens.gclks.gclk2, xosc32k); + let gclk2 = gclk2.div(GclkDiv8::Div(2)).enable(); + let (_gclk2, _gclk2_out) = gclk2.enable_gclk_out(pins.pb16); + + // Output `OscUlp32k` on PB11 pin via `Gclk5`, without any division resulting in + // 32 kHz output frequency + let (osculp32k, _osculp_base) = + OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); + let (gclk5, _osculp32k) = Gclk::from_source(tokens.gclks.gclk5, osculp32k); + let gclk5 = gclk5.enable(); + let (_gclk5, _gclk5_out) = gclk5.enable_gclk_out(pins.pb11); + + // Setup a peripheral channel to power up `Uart` from `Gclk0` + let (pclk_sercom0, _gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0); + + use atsamd_hal::sercom::uart; + + let pads = uart::Pads::default().rx(pins.pa05).tx(pins.pa04); + // In the future, the `Uart` will take ownership of the `Pclk` and will + // take an `ApbClk` instead of `&MCLK` + let mut uart = uart::Config::new(&mclk, device.SERCOM0, pads, pclk_sercom0.freq()) + .baud(115_200.hz(), BaudMode::Arithmetic(Oversampling::Bits16)) + .enable(); + uart.enable_interrupts(Flags::RXC); + + // Initialize the RTC oscillator from the 1 kHz output of XOSC32K + let (rtc_osc, _xosc1k) = RtcOsc::enable(tokens.rtcosc, xosc1k); + + // Setup an `Rtc` in `ClockMode` + // In the future, the `Rtc` will take ownership of the `RtcOsc` + let rtc = Rtc::clock_mode(device.RTC, rtc_osc.freq(), &mut mclk); + + writeln!(&mut uart as &mut dyn Write<_, Error = _>, "RTIC booted!").unwrap(); + + ( + SharedResources { uart, rtc }, + LocalResources {}, + init::Monotonics(), + ) + } + + #[task(binds = SERCOM0_2, shared = [uart, rtc])] + fn uart(cx: uart::Context) { + let mut uart = cx.shared.uart; + let mut rtc = cx.shared.rtc; + // Read from `Uart` to clean interrupt flag + let _ = uart.lock(|u| u.read().unwrap()); + + // Print out `DateTime` coming from `Rtc` + uart.lock(|u| { + writeln!( + u as &mut dyn Write<_, Error = _>, + "{:#?}", + rtc.lock(|r| r.current_time()) + ) + .unwrap() + }); + } +} diff --git a/boards/feather_m4_can/examples/dmac.rs b/boards/feather_m4_can/examples/dmac.rs new file mode 100644 index 000000000000..c2840f265f77 --- /dev/null +++ b/boards/feather_m4_can/examples/dmac.rs @@ -0,0 +1,103 @@ +//! This example shows a safe API to +//! execute a memory-to-memory DMA transfer + +#![no_std] +#![no_main] + +use bsp::hal; +use cortex_m::asm; +use feather_m4_can as bsp; +use panic_halt as _; + +use hal::{ + clock::GenericClockController, + pac::{CorePeripherals, Peripherals}, +}; + +use hal::dmac::{DmaController, PriorityLevel, Transfer, TriggerAction, TriggerSource}; + +#[bsp::entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let _clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut pm = peripherals.PM; + let dmac = peripherals.DMAC; + let _nvic = core.NVIC; + + // Initialize buffers + const LENGTH: usize = 50; + let buf_src: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0xff; LENGTH]).unwrap(); + let buf_dest: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap(); + + // Initialize DMA Controller + let mut dmac = DmaController::init(dmac, &mut pm); + // Get individual handles to DMA channels + let mut channels = dmac.split(); + + // Initialize DMA Channel 0 + let chan0 = channels.0.init(PriorityLevel::LVL0); + + // Setup a DMA transfer (memory-to-memory -> incrementing source, incrementing + // destination) with a 8-bit beat size + let xfer = Transfer::new_from_arrays(chan0, buf_src, buf_dest, false) + .begin(TriggerSource::DISABLE, TriggerAction::BLOCK); + + // Wait for transfer to complete and grab resulting buffers + let (chan0, buf_src, buf_dest) = xfer.wait(); + + // Read the returned buffers + let _a = buf_src[LENGTH - 1]; + let _b = buf_dest[LENGTH - 1]; + + let const_16: &'static mut u16 = cortex_m::singleton!(: u16 = 0xADDE).unwrap(); + let buf_16: &'static mut [u16; LENGTH] = + cortex_m::singleton!(:[u16; LENGTH] = [0x0000; LENGTH]).unwrap(); + + // Setup a DMA transfer (memory-to-memory -> fixed source, incrementing + // destination) with a 16-bit beat size. + let xfer = Transfer::new(chan0, const_16, buf_16, false) + .unwrap() + .begin(TriggerSource::DISABLE, TriggerAction::BLOCK); + + let (chan0, const_16, buf_16) = xfer.wait(); + + // Read the returned buffers + let _a = *const_16; + let _b = buf_16[LENGTH - 1]; + + // Manipulate the returned buffer for fun + for (i, item) in buf_16.iter_mut().enumerate() { + *item = i as u16; + } + + // Setup a DMA transfer (memory-to-memory -> incrementing source, fixed + // destination) with a 16-bit beat size + let xfer = Transfer::new(chan0, buf_16, const_16, false) + .unwrap() + .begin(TriggerSource::DISABLE, TriggerAction::BLOCK); + + let (chan0, buf_16, const_16) = xfer.wait(); + + // Read the returned buffers + let _a = *const_16; // We expect the value "LENGTH - 1" to end up here + let _b = buf_16[LENGTH - 1]; + + // Move split channels back into the Channels struct + channels.0 = chan0.into(); + // Free the DmaController and return the PAC DMAC struct + let _dmac = dmac.free(channels, &mut pm); + + loop { + asm::nop(); + } +} diff --git a/boards/feather_m4_can/examples/i2c.rs b/boards/feather_m4_can/examples/i2c.rs new file mode 100644 index 000000000000..d4bcb344ca18 --- /dev/null +++ b/boards/feather_m4_can/examples/i2c.rs @@ -0,0 +1,83 @@ +//! This example showcases the i2c module. + +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use feather_m4_can as bsp; + +use bsp::hal; +use bsp::pac; +use bsp::{entry, periph_alias}; + +use cortex_m::asm; +use pac::Peripherals; + +use hal::clock::GenericClockController; +use hal::dmac::{DmaController, PriorityLevel}; +use hal::ehal::blocking::i2c::WriteRead; +use hal::prelude::*; +use hal::sercom::i2c; + +const LENGTH: usize = 1; +const ADDRESS: u8 = 0x77; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mclk = peripherals.MCLK; + let dmac = peripherals.DMAC; + let pins = bsp::Pins::new(peripherals.PORT); + + // Take SDA and SCL + let (sda, scl) = (pins.sda, pins.scl); + + // Setup DMA channels for later use + let mut dmac = DmaController::init(dmac, &mut peripherals.PM); + let channels = dmac.split(); + let chan0 = channels.0.init(PriorityLevel::LVL0); + + let buf_src: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap(); + let buf_dest: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap(); + + let gclk0 = clocks.gclk0(); + let sercom2_clock = &clocks.sercom2_core(&gclk0).unwrap(); + let pads = i2c::Pads::new(sda, scl); + let i2c_sercom = periph_alias!(peripherals.i2c_sercom); + let mut i2c = i2c::Config::new(&mclk, i2c_sercom, pads, sercom2_clock.freq()) + .baud(100.khz()) + .enable(); + + let mut buffer = [0x00; 1]; + + // Test writing then reading from an I2C chip + i2c.write_read(ADDRESS, &[0x00], &mut buffer).unwrap(); + + // Test writing then reading using DMA + let init_token = i2c.init_dma_transfer().unwrap(); + let xfer = i2c.send_with_dma(ADDRESS, init_token, buf_src, chan0, |_| {}); + let (chan0, _buf_src, mut i2c) = xfer.wait(); + + let init_token = i2c.init_dma_transfer().unwrap(); + let xfer = i2c.receive_with_dma(ADDRESS, init_token, buf_dest, chan0, |_| {}); + let (_chan0, _i2c, _buf_dest) = xfer.wait(); + + loop { + // Go to sleep + asm::wfi(); + } +} diff --git a/boards/feather_m4_can/examples/mcan.rs b/boards/feather_m4_can/examples/mcan.rs new file mode 100644 index 000000000000..4f2a1adbc9d5 --- /dev/null +++ b/boards/feather_m4_can/examples/mcan.rs @@ -0,0 +1,310 @@ +//! MCAN example +//! Assumed bus: +//! - 375 kb/s nominal bitrate +//! - 750 kb/s data bitrate if sending/receiving CAN FD frames with bitrate +//! switching +//! +//! 1. Sends a message over CAN on periodically and prints out transmit +//! event queue content, protocol status register and error counter register in +//! RTT terminal. +//! +//! 2. Sets up an interrupt line and message filters +//! - messages with standard IDs will end up in RxFifo0 +//! - messages with extended IDs will end up in RxFifo1 +//! - messages content will be printed out in RTT terminal upon arrival +//! +//! 3. LED0 will blink to indicate activity (sending & receiving) + +#![no_std] +#![no_main] + +use bsp::hal; +use feather_m4_can as bsp; +use hal::clock::v2 as clock; +use hal::prelude::*; + +use dwt_systick_monotonic::fugit::RateExtU32 as _; +use dwt_systick_monotonic::{DwtSystick, ExtU32}; + +use mcan::embedded_can as ecan; +use mcan::generic_array::typenum::consts::*; +use mcan::interrupt::{Interrupt, InterruptLine, OwnedInterruptSet}; +use mcan::message::rx; +use mcan::message::tx; +use mcan::messageram::SharedMemory; +use mcan::prelude::*; +use mcan::rx_fifo::Fifo0; +use mcan::rx_fifo::Fifo1; +use mcan::rx_fifo::RxFifo; +use mcan::{ + config::{BitTiming, Mode}, + filter::{Action, ExtFilter, Filter}, +}; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; + +pub struct Capacities; + +impl mcan::messageram::Capacities for Capacities { + type StandardFilters = U1; + type ExtendedFilters = U1; + type RxBufferMessage = rx::Message<64>; + type DedicatedRxBuffers = U0; + type RxFifo0Message = rx::Message<64>; + type RxFifo0 = U64; + type RxFifo1Message = rx::Message<64>; + type RxFifo1 = U64; + type TxMessage = tx::Message<64>; + type TxBuffers = U32; + type DedicatedTxBuffers = U0; + type TxEventFifo = U32; +} + +type RxFifo0 = RxFifo< + 'static, + Fifo0, + clock::types::Can1, + ::RxFifo0Message, +>; +type RxFifo1 = RxFifo< + 'static, + Fifo1, + clock::types::Can1, + ::RxFifo1Message, +>; +type Tx = mcan::tx_buffers::Tx<'static, clock::types::Can1, Capacities>; +type TxEventFifo = mcan::tx_event_fifo::TxEventFifo<'static, clock::types::Can1>; +type Aux = mcan::bus::Aux< + 'static, + clock::types::Can1, + hal::can::Dependencies< + clock::types::Can1, + clock::gclk::Gclk0Id, + bsp::Tcan1051Rx, + bsp::Tcan1051Tx, + bsp::pac::CAN1, + >, +>; + +#[rtic::app(device = hal::pac, peripherals = true, dispatchers = [FREQM])] +mod app { + use super::*; + + #[monotonic(binds = SysTick, default = true)] + type Mono = DwtSystick<48_000_000>; + + #[shared] + struct Shared {} + + #[local] + struct Local { + led: bsp::RedLed, + line_interrupts: OwnedInterruptSet, + rx_fifo_0: RxFifo0, + rx_fifo_1: RxFifo1, + tx: Tx, + tx_event_fifo: TxEventFifo, + aux: Aux, + } + + #[init(local = [ + #[link_section = ".can"] + can_memory: SharedMemory = SharedMemory::new() + ])] + fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { + rtt_init_print!(); + rprintln!("Application up!"); + + let (_buses, clocks, tokens) = clock::clock_system_at_reset( + ctx.device.OSCCTRL, + ctx.device.OSC32KCTRL, + ctx.device.GCLK, + ctx.device.MCLK, + &mut ctx.device.NVMCTRL, + ); + + let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() }; + + let mono = DwtSystick::new( + &mut ctx.core.DCB, + ctx.core.DWT, + ctx.core.SYST, + clocks.gclk0.freq().0, + ); + + let pins = bsp::Pins::new(ctx.device.PORT); + + let (pclk_eic, gclk0) = clock::pclk::Pclk::enable(tokens.pclks.eic, clocks.gclk0); + + let eic = hal::eic::init_with_ulp32k(&mut mclk, pclk_eic.into(), ctx.device.EIC); + eic.finalize(); + + let can1_rx = bsp::pin_alias!(pins.tcan1051_rx).into_mode(); + let can1_tx = bsp::pin_alias!(pins.tcan1051_tx).into_mode(); + let mut can1_standby = bsp::pin_alias!(pins.tcan1051_s).into_push_pull_output(); + + let _ = can1_standby.set_low(); + + let (pclk_can1, gclk0) = clock::pclk::Pclk::enable(tokens.pclks.can1, gclk0); + + let (dependencies, _gclk0) = hal::can::Dependencies::new( + gclk0, + pclk_can1, + clocks.ahbs.can1, + can1_rx, + can1_tx, + ctx.device.CAN1, + ); + + let mut can = + mcan::bus::CanConfigurable::new(375.kHz(), dependencies, ctx.local.can_memory).unwrap(); + + can.config().loopback = true; + can.config().mode = Mode::Fd { + allow_bit_rate_switching: true, + data_phase_timing: BitTiming::new(750.kHz()), + }; + + let line_interrupts = can + .interrupts() + .enable( + [ + Interrupt::RxFifo0NewMessage, + Interrupt::RxFifo0Full, + Interrupt::RxFifo0MessageLost, + Interrupt::RxFifo1NewMessage, + Interrupt::RxFifo1Full, + Interrupt::RxFifo1MessageLost, + ] + .into_iter() + .collect(), + InterruptLine::Line0, + // ATSAMD chips do not expose separate NVIC lines to MCAN + // InterruptLine::Line0 and InterruptLine::Line1 are wired + // together in the hardware. + ) + .unwrap(); + + can.filters_standard() + .push(Filter::Classic { + action: Action::StoreFifo0, + filter: ecan::StandardId::MAX, + mask: ecan::StandardId::ZERO, + }) + .unwrap_or_else(|_| panic!("Standard filter application failed")); + + can.filters_extended() + .push(ExtFilter::Classic { + action: Action::StoreFifo1, + filter: ecan::ExtendedId::MAX, + mask: ecan::ExtendedId::ZERO, + }) + .unwrap_or_else(|_| panic!("Extended filter application failed")); + + let can = can.finalize().unwrap(); + + let rx_fifo_0 = can.rx_fifo_0; + let rx_fifo_1 = can.rx_fifo_1; + let tx = can.tx; + let tx_event_fifo = can.tx_event_fifo; + let aux = can.aux; + + periodic_1hz::spawn_after(1.secs()).unwrap(); + + let led = bsp::pin_alias!(pins.red_led).into(); + bump_activity_led(); + + ( + Shared {}, + Local { + led, + line_interrupts, + rx_fifo_0, + rx_fifo_1, + tx, + tx_event_fifo, + aux, + }, + init::Monotonics(mono), + ) + } + + #[task(local = [counter: u16 = 0, tx_event_fifo, aux, tx])] + fn periodic_1hz(ctx: periodic_1hz::Context) { + bump_activity_led(); + rprintln!("Status:"); + while let Some(e) = ctx.local.tx_event_fifo.pop() { + rprintln!("TxEvent: {:0X?}", e); + } + rprintln!("{:?}", ctx.local.aux.protocol_status()); + rprintln!("{:?}", ctx.local.aux.error_counters()); + + let counter = *ctx.local.counter; + let wrapped_counter = (counter % u8::MAX as u16) as u8; + let mut payload = [0_u8; 64]; + payload.fill(wrapped_counter); + + ctx.local + .tx + .transmit_queued( + tx::MessageBuilder { + id: ecan::Id::Extended(ecan::ExtendedId::new(counter as _).unwrap()), + frame_type: tx::FrameType::FlexibleDatarate { + payload: &payload, + bit_rate_switching: true, + force_error_state_indicator: false, + }, + store_tx_event: Some(wrapped_counter), + } + .build() + .unwrap(), + ) + .unwrap(); + rprintln!("Message {:0X} sent!", counter); + *ctx.local.counter += 1; + periodic_1hz::spawn_after(1.secs()).unwrap(); + } + + #[task(priority = 2, binds = CAN1, local = [line_interrupts, rx_fifo_0, rx_fifo_1])] + fn can1(mut ctx: can1::Context) { + bump_activity_led(); + let line_interrupts = ctx.local.line_interrupts; + for interrupt in line_interrupts.iter_flagged() { + match interrupt { + Interrupt::RxFifo0NewMessage => { + for message in &mut ctx.local.rx_fifo_0 { + log("RxFifo0", &message); + } + } + Interrupt::RxFifo1NewMessage => { + for message in &mut ctx.local.rx_fifo_1 { + log("RxFifo1", &message); + } + } + i => rprintln!("{:?} interrupt triggered", i), + } + } + } + + #[task(local = [led])] + fn activity_led(ctx: activity_led::Context, led_on: bool) { + let _ = ctx.local.led.set_state((!led_on).into()); + if led_on { + let _ = activity_led::spawn_after(100.millis(), false); + } + } + + fn bump_activity_led() { + let _ = activity_led::spawn(true); + } + + fn log(fifo: &str, message: &impl mcan::message::Raw) { + rprintln!("New message received ({})", fifo); + rprintln!("id: {:0X?}", message.id()); + rprintln!("decoded_dlc: {:?}", message.decoded_dlc()); + rprintln!("fd_format: {:?}", message.fd_format()); + rprintln!("bit_rate_switching: {:?}", message.bit_rate_switching()); + rprintln!("is_remote_frame: {:?}", message.is_remote_frame()); + rprintln!("data: {:0X?}", message.data()); + } +} diff --git a/boards/feather_m4_can/examples/neopixel_rainbow.rs b/boards/feather_m4_can/examples/neopixel_rainbow.rs new file mode 100644 index 000000000000..e43a02856e1b --- /dev/null +++ b/boards/feather_m4_can/examples/neopixel_rainbow.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +// Neopixel Rainbow +// This only functions when the --release version is compiled. Using the debug +// version leads to slow pulse durations which results in a straight white LED +// output. +// +// // Needs to be compiled with --release for the timing to be correct + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::timer::*; + +use smart_leds::{ + hsv::{hsv2rgb, Hsv}, + SmartLedsWrite, +}; +use ws2812_timer_delay::Ws2812; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut delay = Delay::new(core.SYST, &mut clocks); + + let gclk0 = clocks.gclk0(); + let timer_clock = clocks.tc2_tc3(&gclk0).unwrap(); + let mut timer = TimerCounter::tc3_(&timer_clock, peripherals.TC3, &mut peripherals.MCLK); + timer.start(3.mhz()); + + let neopixel_pin = pins.neopixel.into_push_pull_output(); + let mut neopixel = Ws2812::new(timer, neopixel_pin); + + // Loop through all of the available hue values (colors) to make a + // rainbow effect from the onboard neopixel + loop { + for j in 0..255u8 { + let colors = [hsv2rgb(Hsv { + hue: j, + sat: 255, + val: 2, + })]; + neopixel.write(colors.iter().cloned()).unwrap(); + delay.delay_ms(5u8); + } + } +} diff --git a/boards/feather_m4_can/examples/nvm_dsu.rs b/boards/feather_m4_can/examples/nvm_dsu.rs new file mode 100644 index 000000000000..b6eb2e3dba34 --- /dev/null +++ b/boards/feather_m4_can/examples/nvm_dsu.rs @@ -0,0 +1,245 @@ +#![no_std] +#![no_main] +#![allow(clippy::bool_comparison)] + +use bsp::ehal; +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use ehal::digital::v2::ToggleableOutputPin; +use hal::clock::GenericClockController; +use hal::dsu::Dsu; +use hal::nvm::{retrieve_bank_size, Bank, EraseGranularity, Nvm, BLOCKSIZE}; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::usb::UsbBus; + +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; + +use core::sync::atomic; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + let mut nvm = Nvm::new(peripherals.NVMCTRL); + let mut dsu = Dsu::new(peripherals.DSU, &peripherals.PAC).unwrap(); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + pins.usb_dm, + pins.usb_dp, + peripherals.USB, + &mut clocks, + &mut peripherals.MCLK, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB_OTHER, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT0, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT1, 1); + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + } + + while USER_PRESENT.load(atomic::Ordering::Acquire) == false { + cycle_delay(25 * 1024 * 1024); + red_led.toggle().ok(); + } + + serial_writeln!("Booted - Active bank: {:?}!\r\n", nvm.first_bank()); + + unsafe { + let active_bank_address = Bank::Active.address(); + let inactive_bank_address = Bank::Inactive.address(); + let bank_size_in_bytes = retrieve_bank_size(); + let bank_size_in_words = retrieve_bank_size() / 4; + let bank_size_in_blocks = retrieve_bank_size() / BLOCKSIZE; + let crc32_checksum_active_bank = + dsu.crc32(active_bank_address, bank_size_in_bytes).unwrap(); + let crc32_checksum_inactive_bank = dsu + .crc32(inactive_bank_address, bank_size_in_bytes) + .unwrap(); + serial_writeln!( + "Active bank CRC32 checksum: {:0X}", + crc32_checksum_active_bank + ); + serial_writeln!( + "Inactive bank CRC32 checksum: {:0X}", + crc32_checksum_inactive_bank + ); + if crc32_checksum_active_bank != crc32_checksum_inactive_bank { + serial_writeln!("Checksums differ: overwrite inactive bank with active one"); + serial_writeln!("Erase inactive bank"); + nvm.erase( + inactive_bank_address, + bank_size_in_blocks, + EraseGranularity::Block, + ) + .unwrap(); + serial_writeln!("Overwrite inactive bank with active bank"); + nvm.write( + inactive_bank_address, + active_bank_address, + bank_size_in_words, + ) + .unwrap(); + serial_writeln!("Swapping banks & reset!"); + nvm.bank_swap(); + } + } + + serial_writeln!("Checksums are the same: endless loop"); + + loop { + cycle_delay(5 * 1024 * 1024); + red_led.toggle().ok(); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +static USER_PRESENT: atomic::AtomicBool = atomic::AtomicBool::new(false); + +/// Borrows the global singleton `UsbSerial` for a brief period with interrupts +/// disabled +/// +/// # Arguments +/// `borrower`: The closure that gets run borrowing the global `UsbSerial` +/// +/// # Safety +/// the global singleton `UsbSerial` can be safely borrowed because we disable +/// interrupts while it is being borrowed, guaranteeing that interrupt handlers +/// like `USB` cannot mutate `UsbSerial` while we are as well. +/// +/// # Panic +/// If `init` has not been called and we haven't initialized our global +/// singleton `UsbSerial`, we will panic. +fn usbserial_get(borrower: T) -> R +where + T: Fn(&mut SerialPort) -> R, +{ + usb_free(|_| unsafe { + let usb_serial = USB_SERIAL.as_mut().expect("UsbSerial not initialized"); + borrower(usb_serial) + }) +} + +/// Execute closure `f` in an interrupt-free context. +/// +/// This as also known as a "critical section". +#[inline] +fn usb_free(f: F) -> R +where + F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R, +{ + NVIC::mask(interrupt::USB_OTHER); + NVIC::mask(interrupt::USB_TRCPT0); + NVIC::mask(interrupt::USB_TRCPT1); + + let r = f(unsafe { &cortex_m::interrupt::CriticalSection::new() }); + + unsafe { + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + }; + + r +} + +/// Writes the given message out over USB serial. +/// +/// # Arguments +/// * println args: variable arguments passed along to `core::write!` +/// +/// # Warning +/// as this function deals with a static mut, and it is also accessed in the +/// USB interrupt handler, we both have unsafe code for unwrapping a static mut +/// as well as disabling of interrupts while we do so. +/// +/// # Safety +/// the only time the static mut is used, we have interrupts disabled so we know +/// we have sole access +#[macro_export] +macro_rules! serial_writeln { + ($($tt:tt)+) => {{ + use core::fmt::Write; + + let mut s: heapless::String<256> = heapless::String::new(); + core::write!(&mut s, $($tt)*).unwrap(); + usbserial_get(|usbserial| { + usbserial.write(s.as_bytes()).ok(); + usbserial.write("\r\n".as_bytes()).ok(); + }); + }}; +} + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + if count > 0 { + USER_PRESENT.store(true, atomic::Ordering::Release); + } + serial.write(&buf[..count]).unwrap(); + }; + }; + }; + }; +} + +#[interrupt] +fn USB_OTHER() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT0() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT1() { + poll_usb(); +} diff --git a/boards/feather_m4_can/examples/pukcc_test.rs b/boards/feather_m4_can/examples/pukcc_test.rs new file mode 100644 index 000000000000..c2c6867da15d --- /dev/null +++ b/boards/feather_m4_can/examples/pukcc_test.rs @@ -0,0 +1,998 @@ +#![no_std] +#![no_main] +#![allow(clippy::bool_comparison)] + +use bsp::ehal; +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use ehal::digital::v2::ToggleableOutputPin; +use hal::clock::GenericClockController; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::{pukcc::*, usb::UsbBus}; + +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + pins.usb_dm, + pins.usb_dp, + peripherals.USB, + &mut clocks, + &mut peripherals.MCLK, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB_OTHER, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT0, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT1, 1); + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + } + + let pukcc = Pukcc::enable(&mut peripherals.MCLK).unwrap(); + + loop { + serial_writeln!("ECDSA Test"); + serial_writeln!("Column 1: Is generated signature identical to a reference signature?"); + serial_writeln!("Column 2: Is a signature valid according to PUKCC"); + serial_writeln!("Column 3: Is a broken signature invalid according to PUKCC"); + serial_writeln!("Test vector: {} samples", ecdsa::K_SIGNATURE_PAIRS.len()); + for (i, (k, reference_signature)) in ecdsa::K_SIGNATURE_PAIRS.iter().enumerate() { + let i = i + 1; + let mut generated_signature = [0_u8; 64]; + let are_signatures_same = match unsafe { + pukcc.zp_ecdsa_sign_with_raw_k::( + &mut generated_signature, + &HASH, + &ecdsa::PRIVATE_KEY, + k, + ) + } { + Ok(_) => generated_signature + .iter() + .zip(reference_signature.iter()) + .map(|(&left, &right)| left == right) + .all(|r| r == true), + Err(e) => { + serial_writeln!("Error during signature generation: {:?}", e); + false + } + }; + let is_signature_valid = pukcc + .zp_ecdsa_verify_signature::( + &generated_signature, + &HASH, + &ecdsa::PUBLIC_KEY, + ) + .is_ok(); + + // Break signature + generated_signature[14] = generated_signature[14].wrapping_sub(1); + + let is_broken_signature_invalid = pukcc + .zp_ecdsa_verify_signature::( + &generated_signature, + &HASH, + &ecdsa::PUBLIC_KEY, + ) + .is_err(); + + serial_writeln!( + "{:>2}: {:<5} | {:<5} | {:<5}", + i, + are_signatures_same, + is_signature_valid, + is_broken_signature_invalid, + ); + } + serial_writeln!("ExpMod Test (RSA)"); + serial_writeln!("Column 1: Is generated signature identical to a reference signature?"); + serial_writeln!("Column 2: Is a signature valid"); + serial_writeln!("Column 3: Is a broken signature invalid"); + serial_writeln!("Test vector: {} samples", exp_mod::RSA_VECTOR.len()); + for (i, (modulus, private_exponent, reference_signature)) in + exp_mod::RSA_VECTOR.iter().enumerate() + { + let mut buffer = [0_u8; 512 + 5]; + let are_signatures_same = match pukcc.modular_exponentiation( + &HASH, + private_exponent, + modulus, + ExpModMode::Regular, + ExpModWindowSize::One, + &mut buffer, + ) { + Ok(generated_signature) => generated_signature + .iter() + .cloned() + .rev() + .zip(reference_signature.iter().cloned().rev()) + .map(|(left, right)| left == right) + .all(|r| r == true), + Err(e) => { + serial_writeln!("Error during signture generation: {:?}", e); + false + } + }; + let is_signature_valid = match pukcc.modular_exponentiation( + reference_signature, + exp_mod::PUBLIC_EXPONENT, + modulus, + ExpModMode::Regular, + ExpModWindowSize::One, + &mut buffer, + ) { + Ok(retrieved_hash) => retrieved_hash + .iter() + .cloned() + .rev() + .zip(HASH.iter().cloned().rev()) + .map(|(left, right)| left == right) + .all(|r| r == true), + Err(e) => { + serial_writeln!("Error during signture generation: {:?}", e); + false + } + }; + let mut broken_reference_signature = [0_u8; 512]; + broken_reference_signature + .iter_mut() + .rev() + .zip(reference_signature.iter().rev()) + .for_each(|(target_iter, source_iter)| *target_iter = *source_iter); + broken_reference_signature[broken_reference_signature.len() - 1] = + broken_reference_signature[broken_reference_signature.len() - 1].wrapping_sub(1); + let is_broken_signature_invalid = match pukcc.modular_exponentiation( + &broken_reference_signature[broken_reference_signature.len() - modulus.len()..], + exp_mod::PUBLIC_EXPONENT, + modulus, + ExpModMode::Regular, + ExpModWindowSize::One, + &mut buffer, + ) { + Ok(retrieved_hash) => !retrieved_hash + .iter() + .cloned() + .rev() + .zip(HASH.iter().cloned().rev()) + .map(|(left, right)| left == right) + .all(|r| r == true), + Err(e) => { + serial_writeln!("Error during signture generation: {:?}", e); + false + } + }; + serial_writeln!( + "{:>2}: {:<5} | {:<5} | {:<5} (RSA{})", + i, + are_signatures_same, + is_signature_valid, + is_broken_signature_invalid, + modulus.len() * 8, + ); + } + + cycle_delay(5 * 1024 * 1024); + red_led.toggle().ok(); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +/// Borrows the global singleton `UsbSerial` for a brief period with interrupts +/// disabled +/// +/// # Arguments +/// `borrower`: The closure that gets run borrowing the global `UsbSerial` +/// +/// # Safety +/// the global singleton `UsbSerial` can be safely borrowed because we disable +/// interrupts while it is being borrowed, guaranteeing that interrupt handlers +/// like `USB` cannot mutate `UsbSerial` while we are as well. +/// +/// # Panic +/// If `init` has not been called and we haven't initialized our global +/// singleton `UsbSerial`, we will panic. +fn usbserial_get(borrower: T) -> R +where + T: Fn(&mut SerialPort) -> R, +{ + usb_free(|_| unsafe { + let usb_serial = USB_SERIAL.as_mut().expect("UsbSerial not initialized"); + borrower(usb_serial) + }) +} + +/// Execute closure `f` in an interrupt-free context. +/// +/// This as also known as a "critical section". +#[inline] +fn usb_free(f: F) -> R +where + F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R, +{ + NVIC::mask(interrupt::USB_OTHER); + NVIC::mask(interrupt::USB_TRCPT0); + NVIC::mask(interrupt::USB_TRCPT1); + + let r = f(unsafe { &cortex_m::interrupt::CriticalSection::new() }); + + unsafe { + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + }; + + r +} + +/// Writes the given message out over USB serial. +/// +/// # Arguments +/// * println args: variable arguments passed along to `core::write!` +/// +/// # Warning +/// as this function deals with a static mut, and it is also accessed in the +/// USB interrupt handler, we both have unsafe code for unwrapping a static mut +/// as well as disabling of interrupts while we do so. +/// +/// # Safety +/// the only time the static mut is used, we have interrupts disabled so we know +/// we have sole access +#[macro_export] +macro_rules! serial_writeln { + ($($tt:tt)+) => {{ + use core::fmt::Write; + + let mut s: heapless::String<256> = heapless::String::new(); + core::write!(&mut s, $($tt)*).unwrap(); + usbserial_get(|usbserial| { + usbserial.write(s.as_bytes()).ok(); + usbserial.write("\r\n".as_bytes()).ok(); + }); + }}; +} + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write(&[*c]).unwrap(); + } + }; + }; + }; + }; +} + +#[interrupt] +fn USB_OTHER() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT0() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT1() { + poll_usb(); +} + +pub const HASH: [u8; 32] = [ + 0xc7, 0x9a, 0x27, 0x4d, 0x91, 0xbb, 0x92, 0x9f, 0x29, 0x16, 0xf8, 0x9c, 0xb2, 0xa6, 0xec, 0x66, + 0xa0, 0xcd, 0xb4, 0x4a, 0x14, 0x97, 0x63, 0x65, 0x3f, 0x28, 0x08, 0x52, 0xbb, 0xa5, 0x3b, 0x0e, +]; + +mod exp_mod { + pub const PUBLIC_EXPONENT: &[u8] = &[0x01, 0x00, 0x01]; + + /// Vector of tuples of (modulus, private_exponent, expected_signature) + pub const RSA_VECTOR: [(&[u8], &[u8], &[u8]); 8] = [ + // RSA512 + ( + &[ + 0xb4, 0x91, 0x91, 0x01, 0x0e, 0x56, 0x37, 0x0b, 0x63, 0x74, 0xd2, 0xf6, 0x5f, 0xee, + 0x2b, 0x06, 0x29, 0x4d, 0x34, 0x05, 0x97, 0xc0, 0xdd, 0xc5, 0xd4, 0xba, 0x16, 0xbe, + 0x74, 0x7c, 0xf2, 0x5f, 0xbd, 0xdb, 0xbb, 0x64, 0x29, 0x33, 0x32, 0x09, 0x18, 0x45, + 0xde, 0xd6, 0x1c, 0x5b, 0xc3, 0xbc, 0x5c, 0x32, 0xd7, 0xf7, 0xd9, 0xd2, 0xa5, 0x7e, + 0xf3, 0x4e, 0x1b, 0x7b, 0xd6, 0x87, 0x7d, 0xf9, + ], + &[ + 0xb1, 0x3f, 0xa3, 0x23, 0x72, 0x3b, 0x57, 0x55, 0x2a, 0x8f, 0xe6, 0xf6, 0x4a, 0x3c, + 0xb4, 0xa7, 0x1b, 0xab, 0xd9, 0x41, 0x14, 0x32, 0x12, 0x80, 0xbb, 0xcc, 0xdf, 0xbe, + 0x9d, 0x02, 0x79, 0xb7, 0xb3, 0xbb, 0xbd, 0x39, 0xa1, 0x53, 0x4c, 0x9a, 0xdd, 0xfb, + 0x4b, 0x4b, 0x47, 0x78, 0xfe, 0x76, 0x90, 0xa7, 0x29, 0xdf, 0x25, 0x67, 0x4d, 0x2f, + 0x82, 0x92, 0x8a, 0x45, 0xb6, 0xec, 0x74, 0xb1, + ], + &[ + 0x09, 0x8d, 0x27, 0x93, 0xd9, 0xcd, 0x67, 0xcb, 0xb4, 0x85, 0xbd, 0x43, 0xec, 0x43, + 0x12, 0x89, 0xf6, 0x3a, 0x40, 0xd8, 0x5c, 0x07, 0xa9, 0x48, 0x0d, 0x5a, 0xa9, 0x7f, + 0xf6, 0x12, 0x7b, 0x54, 0xe2, 0xad, 0xc6, 0x6d, 0x8d, 0x0a, 0x9f, 0x4b, 0x3f, 0x1c, + 0xf3, 0x00, 0x44, 0xa1, 0xa9, 0x78, 0x9d, 0x9b, 0xa7, 0xf4, 0x17, 0x4f, 0xbd, 0xa5, + 0xa3, 0xe3, 0xa4, 0x5d, 0x6e, 0xf4, 0x6a, 0x4e, + ], + ), + ( + &[ + 0xc5, 0x98, 0xfd, 0x58, 0x86, 0x43, 0x71, 0x16, 0x62, 0xa1, 0xe1, 0x05, 0xf8, 0xe1, + 0x33, 0x7d, 0x88, 0xe3, 0xca, 0xd0, 0x4b, 0x8e, 0xc7, 0x24, 0x49, 0x41, 0x36, 0xeb, + 0x2d, 0x87, 0xab, 0xe9, 0x90, 0x9e, 0x6c, 0x3a, 0x5b, 0xda, 0x27, 0x4c, 0x6c, 0xb2, + 0x58, 0xc2, 0x93, 0x82, 0xe9, 0xc4, 0x04, 0x45, 0x6a, 0x45, 0x26, 0x33, 0x52, 0xc0, + 0xd5, 0x54, 0xfa, 0x76, 0x10, 0x23, 0x59, 0x39, + ], + &[ + 0xb6, 0x23, 0xb1, 0xd3, 0xf9, 0xdf, 0x0c, 0xcc, 0xef, 0x99, 0xfc, 0x0c, 0x3f, 0x3f, + 0x86, 0xf0, 0xfe, 0x4f, 0xcb, 0x51, 0x79, 0x74, 0x60, 0xc8, 0x67, 0xe5, 0xff, 0x33, + 0x85, 0x42, 0x7c, 0x09, 0x2f, 0x2b, 0x1c, 0xf9, 0xd7, 0xec, 0x0b, 0x6a, 0x38, 0x20, + 0xb1, 0xa5, 0x96, 0x09, 0x9b, 0xd8, 0xd0, 0xcb, 0x8b, 0xc8, 0xfb, 0xa4, 0xb0, 0xf8, + 0x6b, 0xdc, 0xf7, 0xa0, 0xf2, 0x94, 0x85, 0x49, + ], + &[ + 0x35, 0xae, 0x2b, 0x10, 0x49, 0x1d, 0xcf, 0x53, 0x51, 0xf2, 0xf4, 0xab, 0xa8, 0x4c, + 0x6c, 0x38, 0xe8, 0xd8, 0xa5, 0x40, 0x65, 0x16, 0x62, 0x2a, 0xe2, 0xdd, 0x93, 0x71, + 0x1e, 0xd0, 0xa7, 0x2d, 0xcd, 0xd7, 0x61, 0x22, 0x83, 0x85, 0x76, 0x1f, 0xb2, 0x96, + 0xca, 0xea, 0x30, 0x7d, 0x05, 0xc8, 0x9f, 0x9c, 0x71, 0xfd, 0x51, 0x0e, 0xe5, 0x2a, + 0xf7, 0xb1, 0xd5, 0x26, 0x9e, 0x98, 0x27, 0x90, + ], + ), + // RSA1024 + ( + &[ + 0xbd, 0x36, 0x49, 0x74, 0xde, 0x21, 0x36, 0x0e, 0x34, 0xaf, 0xc8, 0x97, 0x49, 0xb0, + 0xe6, 0x3d, 0x67, 0x1c, 0x83, 0x0c, 0x43, 0x17, 0x39, 0x53, 0x54, 0xe8, 0xcd, 0x52, + 0xc2, 0xc1, 0x78, 0x03, 0x20, 0xb5, 0xbd, 0x93, 0xc2, 0x52, 0xf6, 0xb1, 0xb9, 0xab, + 0x9c, 0xcb, 0xe0, 0x2f, 0x26, 0xf1, 0x02, 0x7e, 0xe6, 0xfb, 0xc8, 0x61, 0xe3, 0x9f, + 0xd3, 0xa7, 0x43, 0x7e, 0x6e, 0x4f, 0xfa, 0x35, 0x3f, 0x64, 0xe6, 0x59, 0x11, 0xc6, + 0xe4, 0x18, 0xed, 0xf1, 0x0a, 0x78, 0x9b, 0x86, 0xa2, 0x09, 0x46, 0x7f, 0x65, 0x91, + 0x03, 0xf4, 0xc9, 0x31, 0xaa, 0x42, 0x50, 0xe9, 0xdf, 0x26, 0x1e, 0x10, 0x95, 0x62, + 0x40, 0x0e, 0xa5, 0x02, 0x13, 0xe3, 0xcc, 0xc9, 0x94, 0x2f, 0xf8, 0x8a, 0xf2, 0x03, + 0x02, 0x70, 0xb5, 0x62, 0xc4, 0x9c, 0xc9, 0xcf, 0x85, 0x56, 0xf6, 0x3f, 0x64, 0xbe, + 0xd0, 0x77, + ], + &[ + 0x1f, 0xd4, 0x8f, 0x1c, 0xf2, 0xf5, 0x61, 0x53, 0x98, 0x77, 0x6d, 0xe6, 0x4d, 0x86, + 0x4d, 0xe5, 0x4c, 0x80, 0x56, 0x67, 0x6c, 0xad, 0xee, 0x7d, 0xdf, 0x4d, 0xde, 0xa4, + 0xaa, 0x90, 0xc3, 0x70, 0xbb, 0x42, 0xf7, 0xa6, 0x70, 0xcd, 0x66, 0x24, 0xd5, 0xd8, + 0x51, 0xe3, 0x56, 0x4e, 0x78, 0x5d, 0x99, 0x0e, 0xe2, 0x2a, 0xbf, 0x36, 0x00, 0x85, + 0xf5, 0xa4, 0x30, 0xcd, 0x87, 0x1f, 0x3b, 0x37, 0x09, 0xe4, 0x37, 0xf6, 0x2a, 0xa6, + 0x2d, 0x73, 0x82, 0x0c, 0x72, 0xac, 0xc4, 0xe5, 0x2d, 0x37, 0x0e, 0xe0, 0x57, 0x31, + 0xe0, 0x13, 0xdd, 0x35, 0x26, 0x9f, 0x8f, 0x4a, 0x53, 0x61, 0x2b, 0x08, 0xd0, 0xfe, + 0xe8, 0x87, 0xc9, 0xa3, 0x12, 0xad, 0xe7, 0x08, 0x3f, 0xc9, 0xd7, 0xc1, 0xb7, 0x98, + 0x0d, 0xae, 0x32, 0xc2, 0xc7, 0x0b, 0x14, 0x2c, 0xc5, 0xdb, 0xed, 0x0d, 0x22, 0x4a, + 0xc6, 0xf9, + ], + &[ + 0x25, 0x3f, 0x02, 0x7c, 0xa2, 0x6f, 0x25, 0x18, 0x51, 0x2b, 0xb6, 0x47, 0x57, 0xf1, + 0x47, 0xbe, 0xb6, 0xa5, 0x8c, 0xa7, 0x3b, 0x12, 0x3b, 0x16, 0x52, 0xea, 0xe2, 0x73, + 0xe1, 0xda, 0x93, 0x65, 0xd2, 0x1b, 0xe8, 0x18, 0xaa, 0xbb, 0x35, 0x67, 0xbc, 0xa8, + 0xe5, 0x0d, 0xd6, 0x10, 0x50, 0xf8, 0x92, 0x8e, 0xa0, 0x62, 0x39, 0x8c, 0xe1, 0xb7, + 0x00, 0xf9, 0x47, 0x34, 0xa5, 0x6b, 0x37, 0x1e, 0xd3, 0xf5, 0xaa, 0x65, 0x9a, 0x10, + 0x10, 0xfa, 0x26, 0xe1, 0x36, 0x17, 0xe1, 0x87, 0xab, 0xc4, 0xa4, 0x68, 0x69, 0xc1, + 0xe0, 0x32, 0x2c, 0x0b, 0xba, 0x6a, 0x9a, 0x17, 0x09, 0xa8, 0x50, 0xee, 0x5f, 0x13, + 0x30, 0x02, 0x3c, 0xf5, 0x49, 0xd1, 0x58, 0xc7, 0xb6, 0x27, 0x3f, 0x74, 0x77, 0xcd, + 0xb4, 0x08, 0x2f, 0xe5, 0x02, 0x14, 0x1c, 0xd2, 0xdc, 0x85, 0x09, 0x32, 0x40, 0xfb, + 0xd9, 0x7e, + ], + ), + ( + &[ + 0xb5, 0xb7, 0x34, 0x9b, 0x99, 0xc5, 0x39, 0xfc, 0xdd, 0x02, 0x6f, 0x7e, 0x6e, 0xb4, + 0xa6, 0x35, 0xfe, 0xeb, 0x29, 0xe2, 0xba, 0x2f, 0x4d, 0xb2, 0x94, 0x49, 0xe7, 0x57, + 0xb2, 0xcc, 0xce, 0xb0, 0xb7, 0x30, 0xb0, 0x2f, 0x64, 0xe0, 0xf0, 0xd9, 0x68, 0x75, + 0xd0, 0x62, 0x6f, 0x6c, 0x9b, 0xfc, 0x89, 0x42, 0xf3, 0xad, 0x2e, 0x67, 0xef, 0xeb, + 0x77, 0xcc, 0x61, 0xeb, 0xd6, 0x09, 0x32, 0xcf, 0xfc, 0xaa, 0xd0, 0x3c, 0x56, 0xb4, + 0x3d, 0xbe, 0x84, 0x33, 0x64, 0x78, 0xd3, 0x31, 0x47, 0x6f, 0x23, 0x02, 0x81, 0xe3, + 0xce, 0x6f, 0xdd, 0x77, 0x36, 0x8b, 0xce, 0x60, 0xa5, 0xec, 0x63, 0x3f, 0x6b, 0x6a, + 0xb0, 0x4a, 0x0c, 0xed, 0x9b, 0x95, 0x83, 0x6b, 0x5e, 0xe7, 0x74, 0x23, 0x43, 0xa3, + 0x6e, 0xcc, 0x0b, 0x92, 0xf6, 0x09, 0x07, 0x6e, 0x40, 0xa1, 0x2b, 0x97, 0xe6, 0x49, + 0x94, 0x43, + ], + &[ + 0xa8, 0x51, 0x60, 0x00, 0x75, 0x69, 0xf3, 0xb1, 0x9e, 0x82, 0x20, 0x06, 0x4b, 0xc3, + 0x37, 0x66, 0x32, 0x8f, 0x5f, 0x87, 0xed, 0x0f, 0xdd, 0xf7, 0x79, 0x56, 0x0f, 0x5c, + 0xf3, 0x78, 0xb4, 0x47, 0x8a, 0x18, 0x26, 0x4a, 0x70, 0x35, 0xcf, 0xc2, 0x81, 0xf9, + 0x07, 0x21, 0xf6, 0xb5, 0xf2, 0xb2, 0xf3, 0xed, 0xb9, 0x4b, 0x03, 0xfe, 0x30, 0x84, + 0xba, 0xbd, 0xed, 0x42, 0x07, 0x4b, 0x13, 0xed, 0x74, 0xd1, 0x0d, 0x42, 0x70, 0x9a, + 0xf8, 0xff, 0x39, 0x1c, 0xab, 0x6c, 0xe7, 0xbc, 0x26, 0x55, 0x64, 0x69, 0x5c, 0x48, + 0x72, 0x43, 0x2d, 0x04, 0x99, 0x50, 0x35, 0x03, 0x3b, 0x3e, 0xfd, 0xb3, 0x86, 0xfd, + 0x57, 0x2b, 0x03, 0xc3, 0x40, 0x9f, 0xe7, 0x9d, 0x39, 0xf2, 0x68, 0x48, 0x6f, 0xda, + 0x88, 0x56, 0x82, 0x74, 0x55, 0xea, 0x0b, 0xa7, 0x50, 0x4b, 0xff, 0x14, 0xf5, 0xee, + 0xce, 0xb9, + ], + &[ + 0x67, 0x51, 0xc4, 0x41, 0x89, 0x48, 0x06, 0x33, 0xd7, 0x86, 0x1b, 0x1c, 0x35, 0x12, + 0x6c, 0x10, 0x37, 0xad, 0x78, 0xc6, 0x47, 0xbe, 0x86, 0xea, 0x3e, 0x09, 0x17, 0x04, + 0xe0, 0x13, 0x9b, 0xac, 0x02, 0xcb, 0x6d, 0x4c, 0x56, 0xc2, 0xcc, 0xfc, 0x88, 0x98, + 0x96, 0x2c, 0xd1, 0x7f, 0xcd, 0x36, 0xe3, 0x26, 0x1f, 0x63, 0xe2, 0xc1, 0x82, 0x4d, + 0x84, 0x14, 0x11, 0x00, 0x1c, 0x02, 0x00, 0x58, 0xa1, 0xb1, 0x42, 0x60, 0x56, 0x4f, + 0x0e, 0x7b, 0xf2, 0xd2, 0x89, 0xd9, 0xb0, 0xce, 0x1d, 0xd9, 0x19, 0x84, 0x18, 0x35, + 0xbe, 0x18, 0x9d, 0xed, 0x96, 0x26, 0x15, 0x7e, 0x96, 0x54, 0xbf, 0x10, 0x4d, 0x25, + 0x22, 0x4f, 0xeb, 0x40, 0x91, 0x3b, 0x1f, 0xb6, 0x54, 0x9f, 0x1e, 0x52, 0x2d, 0x39, + 0x9c, 0x91, 0x54, 0xac, 0x41, 0xd1, 0xb6, 0x88, 0x52, 0xe1, 0x89, 0x02, 0xd7, 0x56, + 0x01, 0xd5, + ], + ), + // RSA2048 + ( + &[ + 0xb5, 0x85, 0x27, 0x1a, 0xda, 0xf4, 0x0d, 0x95, 0x13, 0xcc, 0x6a, 0x46, 0x88, 0x35, + 0x99, 0xc9, 0x83, 0x57, 0xc8, 0xc5, 0xa3, 0x12, 0x1c, 0x68, 0xae, 0x00, 0x80, 0xe2, + 0x3e, 0x8c, 0x2e, 0x4e, 0x9f, 0xae, 0x21, 0x58, 0x31, 0x54, 0x82, 0x59, 0xd6, 0x5d, + 0x5e, 0x96, 0x3a, 0x0e, 0x17, 0xf5, 0x03, 0xdb, 0x10, 0x6a, 0xdd, 0xea, 0x85, 0x2c, + 0x60, 0x1d, 0xde, 0x06, 0xf1, 0x6d, 0x53, 0xc2, 0xaa, 0x2d, 0x5e, 0x3e, 0xa0, 0x31, + 0xb9, 0x90, 0xa7, 0x2d, 0xeb, 0x7c, 0x84, 0xde, 0xdf, 0xe8, 0xe9, 0xcb, 0x19, 0xcc, + 0xea, 0xd2, 0x62, 0xf6, 0x99, 0xfe, 0x1a, 0xf0, 0xa8, 0x7c, 0x4b, 0x56, 0x75, 0x31, + 0x66, 0x86, 0x0a, 0x23, 0x0b, 0xb5, 0xf9, 0xa2, 0xe8, 0xe3, 0x09, 0xa3, 0xee, 0x5e, + 0xfc, 0x87, 0x65, 0xe8, 0x86, 0x46, 0xd8, 0xa4, 0xd2, 0x6a, 0xd7, 0xbe, 0x3d, 0x3b, + 0xeb, 0xd8, 0xee, 0x25, 0xd5, 0x3a, 0xe4, 0xcf, 0xca, 0xad, 0x76, 0x0a, 0x2e, 0x51, + 0x67, 0x5a, 0x6e, 0xb9, 0xb9, 0x4c, 0xee, 0x14, 0xa0, 0x0a, 0x45, 0x18, 0x8f, 0x7c, + 0xcd, 0x6d, 0x71, 0x30, 0x3c, 0x91, 0x67, 0x1a, 0x77, 0x2f, 0x11, 0x3a, 0x18, 0x6c, + 0x87, 0xe3, 0x22, 0xb9, 0x8a, 0x6a, 0xc2, 0x91, 0x26, 0xe7, 0x6b, 0x0e, 0x2e, 0xe0, + 0x8b, 0x89, 0x07, 0x5c, 0x5b, 0x47, 0x6c, 0x63, 0x2d, 0x9d, 0x1a, 0x94, 0xa1, 0xea, + 0xc2, 0x4d, 0xd2, 0xb6, 0x9d, 0x04, 0xaa, 0xd7, 0x61, 0xb4, 0x8f, 0x45, 0xba, 0xbf, + 0xb9, 0x60, 0xaf, 0xac, 0x3a, 0x89, 0xb9, 0x32, 0x20, 0xe8, 0xa0, 0xc0, 0x01, 0x4c, + 0xda, 0xe4, 0x2c, 0x62, 0x7e, 0x58, 0xfd, 0xd1, 0x31, 0xf0, 0x43, 0x88, 0x19, 0x1c, + 0x4b, 0x53, 0x3e, 0x3c, 0x85, 0x7c, 0xbd, 0xfc, 0x64, 0x56, 0xc6, 0x4d, 0x7c, 0x35, + 0x97, 0xd6, 0x36, 0x61, + ], + &[ + 0x47, 0xe3, 0x73, 0x08, 0x44, 0xbc, 0xb1, 0x00, 0x60, 0x75, 0xed, 0x84, 0xff, 0x7e, + 0xd2, 0xe8, 0x26, 0xd7, 0x46, 0x51, 0x57, 0x72, 0xdd, 0xc3, 0x6b, 0x5e, 0x11, 0xad, + 0x08, 0x7e, 0x75, 0xfc, 0x77, 0x6a, 0xfc, 0x13, 0xb4, 0x7d, 0xb6, 0x9e, 0x23, 0xb2, + 0x98, 0xba, 0x40, 0x45, 0xc2, 0xa1, 0x2b, 0xa4, 0xbf, 0x8c, 0xc3, 0x54, 0x94, 0xe7, + 0x6d, 0x2d, 0x86, 0xf8, 0x12, 0xf7, 0x6c, 0x5b, 0xc5, 0x0f, 0xf0, 0xaa, 0x36, 0xc1, + 0x5a, 0xaf, 0x7a, 0x36, 0x4a, 0x73, 0xe7, 0x1f, 0x69, 0x68, 0x11, 0xe7, 0x78, 0xd1, + 0x5a, 0x12, 0x76, 0x55, 0x19, 0xc9, 0xb4, 0x1b, 0xa9, 0x6e, 0x88, 0x5b, 0xb6, 0x50, + 0x19, 0x3d, 0x6e, 0x98, 0x50, 0x94, 0x02, 0x48, 0xcd, 0x98, 0xd5, 0x01, 0x92, 0x6f, + 0x15, 0xed, 0xfd, 0xa3, 0x28, 0x42, 0xb8, 0x9c, 0x16, 0x25, 0x70, 0x4a, 0x0c, 0x70, + 0x45, 0xc7, 0xda, 0x1e, 0x03, 0xed, 0x36, 0x98, 0x23, 0x1d, 0xbb, 0xa2, 0x51, 0x2f, + 0x37, 0x61, 0x55, 0xbf, 0x54, 0x1e, 0xf7, 0xd5, 0xb9, 0xfa, 0x33, 0x9f, 0x0d, 0x0d, + 0xe2, 0x12, 0x41, 0x26, 0x46, 0x8d, 0xee, 0x9d, 0x46, 0x06, 0xda, 0x17, 0xea, 0xec, + 0x59, 0x55, 0x8b, 0x4f, 0xa9, 0x59, 0x7e, 0xdd, 0x1b, 0xed, 0x45, 0x85, 0x06, 0xc3, + 0x1b, 0xed, 0x9f, 0x21, 0xd6, 0x13, 0xf8, 0x25, 0x0d, 0x98, 0x30, 0xaf, 0x95, 0xd2, + 0x4c, 0x74, 0xd2, 0x38, 0x30, 0xc6, 0x73, 0x37, 0xf5, 0x6e, 0x6c, 0x9c, 0x9c, 0x80, + 0x32, 0x68, 0x8d, 0x18, 0x34, 0xe4, 0x5b, 0xc2, 0x09, 0xbd, 0x5e, 0x19, 0xe4, 0x6c, + 0x32, 0x55, 0xbc, 0x63, 0x05, 0xcd, 0x61, 0xca, 0xab, 0xcf, 0x05, 0x99, 0xfb, 0xf1, + 0xc3, 0x07, 0xef, 0xb4, 0x34, 0xb3, 0xfb, 0x9c, 0xd3, 0x43, 0x6d, 0xab, 0x71, 0xf6, + 0x5f, 0xf0, 0xf6, 0x3d, + ], + &[ + 0x85, 0x7a, 0xb1, 0xfe, 0x08, 0x90, 0x74, 0x33, 0x2a, 0xfa, 0x8a, 0x4c, 0x61, 0xd4, + 0x86, 0xed, 0x04, 0x30, 0xcf, 0x8d, 0x92, 0xa8, 0xff, 0x5f, 0xd6, 0xdb, 0x0c, 0x4b, + 0x8f, 0x92, 0xcf, 0x0c, 0xc4, 0xd9, 0x5d, 0xa9, 0x71, 0x9f, 0x69, 0xef, 0x9d, 0x1e, + 0x81, 0xad, 0x25, 0x3a, 0x20, 0x8a, 0x0e, 0x0a, 0x71, 0x2f, 0x16, 0x4f, 0x20, 0xa2, + 0xbe, 0x03, 0x28, 0xaa, 0x6e, 0x64, 0xde, 0x18, 0x44, 0x41, 0x81, 0xde, 0xbb, 0xd7, + 0x1c, 0x8a, 0x20, 0x7f, 0x1e, 0xed, 0xde, 0xab, 0x85, 0x99, 0xac, 0xa6, 0x87, 0xa1, + 0xf0, 0xa1, 0xe4, 0x4c, 0x15, 0x46, 0xba, 0xc7, 0x75, 0xc8, 0x7c, 0x2e, 0x2b, 0xdc, + 0x80, 0x43, 0x5e, 0x81, 0xe1, 0xe2, 0xb6, 0x72, 0xf5, 0x37, 0xe3, 0xe5, 0x6a, 0xe6, + 0x58, 0x07, 0xa0, 0x77, 0xe0, 0x7a, 0x33, 0x2e, 0x02, 0xe4, 0xbd, 0xc2, 0x39, 0x2f, + 0x07, 0xc2, 0x99, 0xcb, 0x2f, 0xa6, 0x89, 0x1a, 0xc4, 0xaf, 0xf4, 0x9f, 0xc1, 0x93, + 0x7a, 0x6d, 0xd2, 0x62, 0x9c, 0x53, 0x25, 0x3b, 0xac, 0x4d, 0x55, 0x89, 0x3c, 0x81, + 0x5e, 0xea, 0x4b, 0x1f, 0x63, 0x74, 0x86, 0x0f, 0xc5, 0x05, 0xfa, 0xa8, 0x58, 0x8f, + 0xe7, 0x3e, 0x53, 0xd2, 0x55, 0xeb, 0x90, 0x57, 0x39, 0xf9, 0x10, 0x94, 0xe3, 0x1c, + 0xab, 0xcf, 0x15, 0x9e, 0x2e, 0x09, 0xd6, 0x53, 0xbc, 0xd4, 0x9e, 0xe4, 0xfb, 0x5d, + 0x9b, 0x8f, 0xc0, 0x16, 0x4b, 0x95, 0x9f, 0x1d, 0x66, 0xb4, 0x83, 0xc3, 0xc5, 0xa9, + 0x15, 0x04, 0xc8, 0xb0, 0x8e, 0xf9, 0x46, 0x13, 0x52, 0x49, 0x39, 0x69, 0x3d, 0xc1, + 0x4c, 0xb8, 0x73, 0x62, 0x51, 0x94, 0xd3, 0x76, 0x86, 0xb7, 0x35, 0xa3, 0x9a, 0x96, + 0x27, 0x3d, 0x4c, 0x70, 0x73, 0xd7, 0x57, 0x4d, 0x1f, 0x40, 0x8c, 0xb2, 0x4a, 0x45, + 0x57, 0xb6, 0x15, 0xc5, + ], + ), + ( + &[ + 0xb9, 0xb4, 0x5e, 0x3d, 0x40, 0x4a, 0x62, 0xc7, 0xa3, 0x10, 0xc2, 0x0d, 0x8d, 0xd8, + 0x90, 0xa3, 0x4a, 0x20, 0x83, 0x8f, 0x74, 0x0e, 0x3c, 0xb9, 0x39, 0x05, 0x61, 0x56, + 0xa6, 0x37, 0x6f, 0x1b, 0xfa, 0x5f, 0xe7, 0xbd, 0x4d, 0x36, 0x93, 0x80, 0x35, 0x88, + 0xa1, 0xee, 0xf3, 0xbc, 0x37, 0x33, 0x89, 0xea, 0x18, 0x66, 0x65, 0x69, 0x36, 0x35, + 0xcc, 0x0a, 0x0b, 0x1d, 0x49, 0xbc, 0x5a, 0x39, 0x67, 0x9a, 0x74, 0xa1, 0x8f, 0xdd, + 0x3f, 0xa6, 0x3b, 0xac, 0xa1, 0x21, 0x81, 0xa0, 0xc7, 0xb9, 0x30, 0xf0, 0x11, 0x10, + 0xb5, 0xac, 0xf7, 0x20, 0x92, 0x97, 0x49, 0xa7, 0x37, 0x15, 0xc9, 0xd9, 0x8c, 0xae, + 0x8d, 0x7a, 0xa4, 0x9e, 0xaf, 0x73, 0xcd, 0x76, 0xd4, 0x57, 0xe4, 0x59, 0xac, 0x7a, + 0x9b, 0x8c, 0xf8, 0xfa, 0x3f, 0xa4, 0x30, 0x60, 0xa1, 0x13, 0xbc, 0xd6, 0x32, 0x72, + 0x50, 0xbc, 0x02, 0x7c, 0x2a, 0x5a, 0xe1, 0x50, 0x2e, 0x66, 0xd4, 0x91, 0xc8, 0x3a, + 0x28, 0x38, 0x41, 0xd4, 0x8a, 0xef, 0x04, 0xd5, 0x58, 0xb9, 0x02, 0xf9, 0x6d, 0x68, + 0xc1, 0x80, 0xc7, 0x96, 0x0f, 0xd0, 0x42, 0x22, 0x13, 0x8e, 0xa8, 0xf1, 0x7a, 0x4e, + 0xc5, 0xe3, 0xb8, 0x10, 0x81, 0x9f, 0x76, 0x7e, 0xb0, 0xb4, 0xfb, 0xc7, 0xd2, 0x1f, + 0xf1, 0x5e, 0x2c, 0x51, 0xbb, 0xf4, 0xe7, 0xe9, 0xd4, 0x1e, 0x23, 0x97, 0x14, 0xe6, + 0x07, 0x53, 0x25, 0x7a, 0xd7, 0xde, 0xb8, 0xab, 0x62, 0x23, 0x3e, 0x83, 0x16, 0xbf, + 0x89, 0xb1, 0x33, 0x0c, 0x78, 0x49, 0xa7, 0xd7, 0x51, 0xae, 0xd1, 0x05, 0x89, 0x9e, + 0xe5, 0x0f, 0x8a, 0x3c, 0x73, 0x18, 0x9d, 0xa5, 0xf1, 0xf3, 0xbb, 0x6a, 0xa4, 0x9f, + 0x9e, 0xc1, 0x2c, 0x0f, 0xb1, 0xfc, 0xe4, 0xe5, 0x13, 0x86, 0x19, 0x86, 0xb4, 0x39, + 0x6a, 0x70, 0xc6, 0xc1, + ], + &[ + 0xab, 0x16, 0x0b, 0x04, 0x6f, 0x28, 0x98, 0xdc, 0xc7, 0xd4, 0x76, 0x93, 0x3a, 0x2d, + 0x5d, 0x03, 0xb3, 0x15, 0x45, 0x5f, 0x72, 0x52, 0x73, 0x8b, 0x49, 0x87, 0x35, 0x68, + 0x38, 0xf6, 0x35, 0x3d, 0x17, 0x6c, 0x27, 0xf9, 0xf5, 0x1a, 0xe4, 0xc5, 0x67, 0x8c, + 0x9b, 0x73, 0xa3, 0xc5, 0xb1, 0x2d, 0xa0, 0x4f, 0xb5, 0x6f, 0x10, 0xda, 0xdf, 0x80, + 0xac, 0x9c, 0x4c, 0x25, 0x0d, 0x7b, 0xa3, 0xbb, 0xe3, 0x41, 0x1f, 0x56, 0x81, 0x4e, + 0x1a, 0x87, 0xb1, 0xce, 0x97, 0x1c, 0x61, 0x6a, 0x98, 0xd6, 0x7a, 0xc9, 0x91, 0x4f, + 0x4d, 0xb1, 0x2e, 0x74, 0x29, 0xd9, 0x8b, 0x97, 0xac, 0x5e, 0x3c, 0x7a, 0x5a, 0xeb, + 0xad, 0x98, 0x61, 0xf5, 0x78, 0x3b, 0x3d, 0xfd, 0xce, 0x1f, 0xb3, 0x57, 0x12, 0x5a, + 0x5a, 0xd8, 0x83, 0xc1, 0x39, 0xc4, 0xb0, 0x75, 0x35, 0xb1, 0x13, 0x76, 0x5b, 0x3f, + 0x8a, 0x34, 0x39, 0xef, 0xf9, 0x1c, 0xa2, 0x4c, 0x7d, 0x93, 0x99, 0xbc, 0x7d, 0xbb, + 0xff, 0xf1, 0xf3, 0xd5, 0xe6, 0x87, 0x23, 0x48, 0x02, 0xf8, 0xee, 0x54, 0xab, 0x20, + 0x3d, 0xcc, 0x7e, 0x05, 0x22, 0x83, 0x4b, 0x1d, 0x55, 0x93, 0xcd, 0x4a, 0xe3, 0x51, + 0xb6, 0xcf, 0x58, 0x92, 0x26, 0x20, 0xd5, 0xa3, 0xde, 0x57, 0xe4, 0x89, 0x67, 0x4f, + 0xe0, 0x2a, 0x8e, 0x9c, 0xaf, 0xf6, 0x1a, 0x24, 0x65, 0x8b, 0x7c, 0x24, 0xfa, 0x51, + 0xcb, 0x0e, 0xd3, 0xe0, 0x3e, 0x83, 0x3e, 0xce, 0x93, 0x14, 0x7c, 0xac, 0x12, 0xd4, + 0x2e, 0x20, 0x0c, 0x21, 0xe4, 0x46, 0x3a, 0xab, 0xf0, 0xc6, 0x55, 0x77, 0xf4, 0xaf, + 0x81, 0x0e, 0x9c, 0xd8, 0xda, 0x87, 0xa7, 0xfa, 0xaf, 0x76, 0x5d, 0x43, 0xaf, 0x95, + 0xa5, 0x80, 0x21, 0x70, 0xee, 0xe5, 0x3b, 0x02, 0x7c, 0x67, 0x61, 0xde, 0x02, 0x5f, + 0x93, 0x07, 0x7e, 0x01, + ], + &[ + 0x9d, 0x55, 0x61, 0x46, 0x1b, 0x3e, 0xb9, 0x34, 0x13, 0xdd, 0xec, 0x7f, 0x32, 0xff, + 0xe5, 0x5b, 0x9c, 0x37, 0xda, 0xe7, 0x35, 0x89, 0xf6, 0x72, 0x92, 0x0c, 0x27, 0xce, + 0xa4, 0x2f, 0x80, 0x9d, 0x48, 0xde, 0xfd, 0x91, 0x6d, 0x09, 0xba, 0x28, 0xd8, 0x3a, + 0x86, 0x2b, 0xa5, 0x8e, 0xa1, 0x1d, 0x74, 0x36, 0xd9, 0x94, 0x87, 0x1a, 0xef, 0xb4, + 0x6d, 0x56, 0xf4, 0x0e, 0x97, 0xc9, 0x3d, 0x73, 0x22, 0x3e, 0x0c, 0xcb, 0x83, 0xdb, + 0x2d, 0x90, 0x2d, 0x0a, 0x50, 0xf2, 0x97, 0x48, 0x36, 0xa5, 0x5f, 0xe2, 0x96, 0xdf, + 0x0d, 0x09, 0x70, 0x3e, 0x7e, 0x28, 0x21, 0xb4, 0xf4, 0x74, 0xa7, 0x5c, 0x76, 0x3e, + 0x7c, 0x43, 0x0b, 0x89, 0x1e, 0x2f, 0x29, 0x3a, 0x4a, 0x01, 0xdf, 0x15, 0x3f, 0x39, + 0xc5, 0x57, 0xf9, 0x8a, 0x93, 0x17, 0xd9, 0x39, 0xea, 0x4e, 0x8c, 0xfd, 0x94, 0xfa, + 0xae, 0x31, 0x38, 0xae, 0x07, 0xe4, 0x2a, 0x8d, 0xc9, 0xf6, 0xb0, 0x2f, 0x42, 0x40, + 0x67, 0x82, 0x30, 0xdc, 0x69, 0x7c, 0x16, 0xa6, 0x57, 0x08, 0x5c, 0xbd, 0x69, 0xfb, + 0xf0, 0x69, 0xfa, 0x06, 0x69, 0x77, 0x32, 0x95, 0x24, 0x0a, 0xf0, 0x9a, 0xb4, 0xa3, + 0xce, 0x84, 0xc6, 0x6d, 0xdf, 0x75, 0xb3, 0x01, 0x5d, 0x50, 0x10, 0x74, 0x62, 0xdd, + 0x5b, 0x19, 0x71, 0xcb, 0xf7, 0xbd, 0xa8, 0x6f, 0xd3, 0xb5, 0x60, 0xd2, 0x90, 0x62, + 0x1c, 0x0c, 0xe9, 0xf9, 0xe6, 0x77, 0x72, 0x9a, 0x0f, 0xeb, 0x3a, 0xaf, 0xca, 0x13, + 0xb6, 0xc1, 0xf0, 0xb3, 0xcd, 0x30, 0x3b, 0x34, 0xad, 0xcc, 0x19, 0x87, 0x8a, 0xaf, + 0xf2, 0xa8, 0x96, 0x80, 0xea, 0xda, 0xbd, 0xb8, 0xf6, 0x2f, 0xe5, 0xe0, 0x85, 0xf5, + 0x59, 0x80, 0x26, 0xa6, 0x11, 0xfb, 0x43, 0xe8, 0xe6, 0xcc, 0xdd, 0x86, 0x77, 0x32, + 0x10, 0x09, 0x26, 0x56, + ], + ), + // RSA4096 + ( + &[ + 0xac, 0x1b, 0x1c, 0x45, 0x5e, 0x91, 0x59, 0x14, 0x06, 0x35, 0xd5, 0xba, 0xf0, 0xde, + 0x7c, 0x8d, 0x4d, 0x67, 0xc0, 0x0e, 0xf5, 0x30, 0x62, 0x6c, 0xef, 0xc0, 0xa9, 0xc0, + 0x1d, 0xb0, 0x5c, 0xcb, 0x1d, 0xff, 0x9b, 0xe8, 0x23, 0x3e, 0xf6, 0xec, 0xfe, 0xb7, + 0x88, 0x0b, 0x5a, 0x43, 0x98, 0xdc, 0x52, 0x32, 0x14, 0x0c, 0xea, 0x7e, 0x2e, 0xbe, + 0xe2, 0xb2, 0xea, 0x48, 0xe1, 0xca, 0x04, 0xa6, 0x40, 0x13, 0x23, 0x88, 0xda, 0xbf, + 0x9b, 0x51, 0x77, 0x67, 0x15, 0xef, 0x55, 0x09, 0xaf, 0xc1, 0xf6, 0x85, 0x68, 0x6a, + 0xeb, 0x1b, 0x95, 0x62, 0x38, 0x95, 0x91, 0xd2, 0x19, 0x23, 0xa2, 0x18, 0xd4, 0xd1, + 0x7f, 0x9b, 0x53, 0xab, 0x2f, 0x56, 0x3e, 0xd8, 0x0e, 0x9f, 0x7e, 0xe3, 0xd0, 0x2e, + 0xbf, 0x9a, 0x6f, 0xf9, 0xc3, 0xa4, 0x68, 0x83, 0x43, 0x02, 0x3d, 0xc9, 0xf5, 0x2b, + 0xb5, 0x17, 0xe7, 0xe4, 0xcb, 0x43, 0x83, 0x1a, 0xe3, 0xad, 0xe3, 0x64, 0xf5, 0x33, + 0xd6, 0xb3, 0x53, 0x43, 0xc6, 0x38, 0x13, 0xb7, 0xa6, 0x60, 0xf0, 0x2f, 0x85, 0x94, + 0xcf, 0xe2, 0x47, 0x9b, 0x2e, 0x6b, 0x44, 0x62, 0x0a, 0x7e, 0xec, 0xdf, 0x09, 0x78, + 0x83, 0x29, 0xcd, 0x00, 0x6d, 0xbe, 0x1a, 0x1b, 0xe7, 0xbb, 0x72, 0x3f, 0xb3, 0x3f, + 0xcc, 0x76, 0xa1, 0x1d, 0x18, 0x13, 0x60, 0xbc, 0x10, 0xd1, 0x46, 0x23, 0x0c, 0xb3, + 0xf0, 0x00, 0x2d, 0xf4, 0x1b, 0x31, 0xcc, 0x95, 0x3b, 0x3c, 0x8b, 0x01, 0x0d, 0xca, + 0x1a, 0x81, 0xc5, 0x04, 0xda, 0xad, 0xab, 0x14, 0x3f, 0xd2, 0x73, 0x39, 0xd3, 0x4b, + 0xa0, 0xfd, 0xf9, 0xf5, 0x5c, 0x5b, 0x3e, 0xaa, 0xd8, 0xc5, 0x23, 0x6c, 0xfc, 0xff, + 0x71, 0xcc, 0x3b, 0xe8, 0x74, 0x33, 0xed, 0xcf, 0x3b, 0x4a, 0xab, 0xe5, 0x0b, 0x69, + 0x3a, 0xce, 0x03, 0x2e, 0xda, 0x6a, 0x5c, 0x46, 0x36, 0xb2, 0x6f, 0xcf, 0x89, 0x66, + 0xcb, 0x7b, 0x83, 0xe5, 0x78, 0x11, 0xcd, 0x1d, 0xc6, 0xa2, 0x21, 0xf1, 0x81, 0x5b, + 0xf4, 0xfe, 0x59, 0xea, 0x88, 0xc8, 0x3e, 0x11, 0xd4, 0x9d, 0xf8, 0x70, 0xe8, 0x39, + 0x61, 0x6d, 0x17, 0xe8, 0xbc, 0x19, 0x1f, 0xc2, 0xd6, 0x85, 0x5d, 0x80, 0x1d, 0x37, + 0xe4, 0xea, 0x95, 0xfe, 0xa5, 0x21, 0x81, 0xdc, 0xeb, 0x1f, 0x80, 0xe1, 0xba, 0x7c, + 0xad, 0xb2, 0x89, 0x9a, 0x2a, 0x6e, 0x72, 0xab, 0x44, 0x3c, 0x5a, 0x99, 0x5f, 0x5b, + 0xd1, 0x7f, 0x56, 0xbc, 0x39, 0x99, 0x28, 0x32, 0x13, 0xb0, 0x3e, 0x17, 0x61, 0x3d, + 0xc5, 0x1b, 0x6a, 0x32, 0x88, 0x3c, 0x06, 0x07, 0x0c, 0x61, 0x6e, 0x90, 0x67, 0x61, + 0xf3, 0x7d, 0x21, 0x5e, 0x2b, 0x00, 0x1a, 0x1f, 0xd5, 0x16, 0x90, 0xce, 0x1a, 0x12, + 0x0f, 0x3a, 0x0c, 0x82, 0xce, 0xe0, 0x7b, 0x46, 0x63, 0xf4, 0xb9, 0xae, 0xcc, 0xa5, + 0x65, 0xcb, 0xa0, 0x38, 0xf7, 0x93, 0x31, 0x74, 0x1b, 0x3c, 0x85, 0xf9, 0x2b, 0x98, + 0xef, 0xee, 0x9a, 0x74, 0x6c, 0x34, 0x41, 0x7e, 0x38, 0x86, 0xbe, 0x25, 0xf9, 0x41, + 0xbd, 0x9e, 0x34, 0x3a, 0xe9, 0x24, 0x7c, 0x1a, 0x27, 0xe7, 0x36, 0x9f, 0x03, 0x0b, + 0x64, 0x76, 0xf5, 0xb0, 0xf3, 0xb1, 0xdb, 0x61, 0x30, 0x6b, 0xa3, 0x3a, 0x8e, 0x4b, + 0x01, 0xa6, 0x48, 0x08, 0xdb, 0x66, 0xb4, 0x3d, 0xbb, 0x28, 0x80, 0x90, 0xcb, 0x1b, + 0x20, 0x20, 0x99, 0xce, 0xb5, 0x85, 0x11, 0xd5, 0x51, 0x81, 0xa1, 0x97, 0x76, 0x71, + 0xbe, 0x1c, 0xc4, 0x51, 0x82, 0xbe, 0xbd, 0x8f, 0xe6, 0xd9, 0x94, 0xee, 0x59, 0xb6, + 0x29, 0xd1, 0x12, 0xa6, 0x03, 0x30, 0xf9, 0x91, 0x67, 0xea, 0x95, 0xc7, 0x34, 0xa5, + 0x42, 0xbd, 0x6a, 0x97, 0x62, 0xf0, 0xc6, 0xb9, + ], + &[ + 0x85, 0xb9, 0x16, 0xd1, 0x4b, 0x76, 0x31, 0xb9, 0x5e, 0x4d, 0xec, 0x00, 0x31, 0x71, + 0x1d, 0x63, 0x89, 0x16, 0x28, 0xe3, 0x36, 0x5d, 0x5e, 0xcc, 0x77, 0xc8, 0xc1, 0xdc, + 0x54, 0xf5, 0x18, 0x54, 0x75, 0xbd, 0x8a, 0x7c, 0xe7, 0x0d, 0xe0, 0x3c, 0x2a, 0x79, + 0x9d, 0xc9, 0xfc, 0x5b, 0x73, 0x65, 0x14, 0xb4, 0x76, 0x61, 0xc6, 0xbd, 0x3e, 0x42, + 0xf0, 0xcf, 0xc5, 0x3b, 0xd5, 0xbb, 0xea, 0xba, 0xe6, 0x24, 0x38, 0xc2, 0xf7, 0xfc, + 0x52, 0x89, 0x0c, 0xf6, 0x5a, 0xd3, 0xb7, 0xc6, 0x2b, 0xfa, 0xd0, 0x39, 0xbd, 0xf4, + 0xfd, 0x32, 0x54, 0x72, 0x99, 0xb6, 0x95, 0x33, 0xa2, 0x76, 0xce, 0x56, 0xee, 0xdc, + 0xcc, 0x82, 0x7a, 0x93, 0x12, 0xd7, 0xb7, 0x42, 0x96, 0xb2, 0x14, 0x9c, 0x9b, 0xc0, + 0x06, 0xfd, 0xcf, 0x2d, 0x48, 0x76, 0xf1, 0x01, 0xb4, 0x4a, 0x04, 0x8a, 0x6b, 0xe5, + 0x86, 0xf2, 0xc9, 0x2a, 0x3b, 0x48, 0xfc, 0x90, 0x23, 0x01, 0x94, 0x22, 0x78, 0x66, + 0xcb, 0xd6, 0x4f, 0xc6, 0xe4, 0x37, 0xe4, 0x12, 0x18, 0xc3, 0x4f, 0x3c, 0x0d, 0x55, + 0x9f, 0xe4, 0x98, 0x70, 0x99, 0xb3, 0x9c, 0xd1, 0x74, 0x88, 0x44, 0x31, 0xd4, 0x21, + 0x00, 0x00, 0xb5, 0x99, 0x34, 0xab, 0xb5, 0x8b, 0xa3, 0x15, 0x40, 0xe2, 0xff, 0xba, + 0x7d, 0x7a, 0x5b, 0x1a, 0xc8, 0xff, 0x1d, 0x75, 0x62, 0xb7, 0xc8, 0x00, 0x29, 0xb9, + 0x91, 0xac, 0x02, 0x08, 0x9e, 0x2c, 0xa6, 0x61, 0xf9, 0x41, 0xc4, 0x5b, 0x90, 0x44, + 0x4d, 0x2e, 0x31, 0x4f, 0xe1, 0x3d, 0x79, 0x24, 0xe5, 0xa5, 0xf6, 0x03, 0xa3, 0x0a, + 0x2b, 0x4f, 0xcb, 0x2c, 0x7a, 0x93, 0x26, 0xf1, 0x06, 0x36, 0xbe, 0x9c, 0xcb, 0x43, + 0x0b, 0x3f, 0x7d, 0xf8, 0xd2, 0x79, 0x1a, 0xb8, 0x9e, 0xc0, 0x19, 0x29, 0x81, 0x21, + 0x45, 0x20, 0x96, 0xd5, 0x45, 0x50, 0xf1, 0x2a, 0xb3, 0xe9, 0x98, 0x0c, 0x86, 0x27, + 0xde, 0x0a, 0x7a, 0x2d, 0xdd, 0xf6, 0x42, 0xb9, 0xda, 0x8a, 0x22, 0x17, 0xbd, 0x6c, + 0x72, 0xda, 0xf7, 0xea, 0x67, 0x78, 0x48, 0xe6, 0x33, 0xd0, 0x00, 0xd7, 0x72, 0xe4, + 0xb8, 0x81, 0x66, 0xea, 0x1a, 0x9f, 0x4b, 0x45, 0xbe, 0x92, 0xa1, 0xea, 0x77, 0xd7, + 0xc6, 0xba, 0x7b, 0x27, 0x0c, 0x52, 0x24, 0xad, 0xd0, 0x14, 0xab, 0xf7, 0xe5, 0x42, + 0xc3, 0x2e, 0xe5, 0x3d, 0x23, 0x2c, 0x11, 0xdc, 0x89, 0x15, 0xd7, 0x61, 0xce, 0x6c, + 0xce, 0x16, 0x44, 0xc4, 0xf7, 0xe4, 0x6c, 0x3c, 0xea, 0x63, 0xf7, 0x43, 0x72, 0x78, + 0xcd, 0xab, 0xf7, 0x2e, 0xbe, 0xa5, 0x6a, 0x72, 0x9d, 0x91, 0x2b, 0x2a, 0xe3, 0xdd, + 0x5c, 0x55, 0xe0, 0xee, 0x8b, 0x05, 0x27, 0xfb, 0x16, 0xde, 0x72, 0x9e, 0x40, 0xec, + 0x67, 0xa1, 0x0a, 0xed, 0x9b, 0xde, 0x79, 0x48, 0x8d, 0x20, 0x75, 0x5b, 0x10, 0xd7, + 0xe6, 0xd4, 0x38, 0x1a, 0x85, 0xc0, 0xdf, 0xd2, 0x31, 0x64, 0x88, 0x50, 0xeb, 0xa2, + 0x63, 0x33, 0x92, 0xb7, 0xbf, 0x98, 0xb1, 0x7d, 0x16, 0xe5, 0x1e, 0x86, 0x33, 0xf5, + 0x57, 0x48, 0x49, 0x08, 0xb0, 0xb8, 0x6b, 0xf8, 0xc5, 0x55, 0xb2, 0xf4, 0x46, 0x1e, + 0x0c, 0xad, 0xe8, 0x55, 0x5d, 0xee, 0x67, 0xea, 0xbf, 0x3a, 0x2e, 0xb3, 0xf3, 0x15, + 0xf8, 0x9f, 0xe6, 0x47, 0xb6, 0x82, 0x2f, 0x60, 0x90, 0xf6, 0x30, 0x6b, 0x8d, 0xee, + 0x39, 0x20, 0x39, 0xf0, 0x64, 0xfd, 0x7e, 0xfb, 0x05, 0x92, 0xb4, 0xbe, 0x39, 0x13, + 0xeb, 0x0d, 0x8d, 0x5e, 0xee, 0xf0, 0xf1, 0x19, 0x34, 0x0f, 0xde, 0x8d, 0x68, 0x7b, + 0x49, 0x9d, 0xb8, 0xe2, 0xa7, 0x75, 0x65, 0xc7, 0xe2, 0x1a, 0xc1, 0x9a, 0x5f, 0x73, + 0x51, 0xc4, 0x55, 0xa3, 0x5f, 0x92, 0x63, 0x95, + ], + &[ + 0x5f, 0xcb, 0xe7, 0x42, 0x4c, 0xe1, 0x44, 0x0e, 0x5c, 0xe8, 0xfa, 0x52, 0x65, 0xff, + 0xea, 0x14, 0x57, 0x79, 0xe3, 0x6a, 0x2f, 0x89, 0xfa, 0xdc, 0x06, 0x48, 0xbe, 0x79, + 0xf8, 0x54, 0x25, 0x52, 0x2c, 0xd6, 0x0b, 0x69, 0xb6, 0x83, 0x63, 0xc5, 0xd0, 0x63, + 0x61, 0xe2, 0x31, 0xb0, 0xc2, 0x50, 0x8f, 0xce, 0x27, 0x1f, 0xab, 0x50, 0xef, 0xc2, + 0x1c, 0x63, 0x44, 0x07, 0x86, 0x7b, 0xb8, 0x67, 0x34, 0x90, 0x7e, 0x60, 0xd2, 0xe5, + 0x1e, 0x9b, 0xa5, 0x44, 0x4d, 0x0d, 0x32, 0x83, 0x9f, 0x18, 0xb4, 0x80, 0x7e, 0xa6, + 0xd5, 0x2d, 0xff, 0x8c, 0xb9, 0x0d, 0x36, 0xee, 0xc4, 0x5d, 0xd1, 0x55, 0xec, 0x64, + 0xeb, 0x46, 0xa7, 0x67, 0x83, 0x6c, 0x85, 0x39, 0x06, 0xbc, 0xf2, 0x00, 0x0f, 0xd4, + 0xb6, 0x03, 0xdc, 0xb2, 0xe6, 0x72, 0xa0, 0x33, 0x0c, 0x24, 0x67, 0x61, 0xa2, 0x76, + 0x54, 0x5e, 0x20, 0xd1, 0xfc, 0x82, 0x7d, 0x64, 0x54, 0xbd, 0x22, 0xdf, 0x5b, 0xf0, + 0x1d, 0x4c, 0x15, 0x4a, 0x11, 0x33, 0xac, 0x28, 0x33, 0x57, 0x08, 0x47, 0xf3, 0x1b, + 0x67, 0xac, 0x80, 0x92, 0xc7, 0x1d, 0x3b, 0xea, 0xb3, 0x1f, 0xef, 0x45, 0x23, 0x0e, + 0x10, 0xb0, 0xba, 0x0a, 0x7b, 0x44, 0x10, 0xee, 0xfa, 0xe4, 0xc4, 0xf6, 0xef, 0x53, + 0x78, 0x41, 0x0d, 0x29, 0xd4, 0x39, 0x8e, 0x2d, 0xbf, 0xc3, 0xd7, 0x71, 0xf1, 0x80, + 0xfb, 0xa9, 0xbd, 0x53, 0x59, 0xd4, 0x9c, 0x06, 0x2e, 0x66, 0x67, 0xa6, 0x1a, 0x17, + 0x51, 0x1b, 0xc7, 0x6f, 0xdb, 0xe7, 0xa1, 0xf3, 0x07, 0x4f, 0x07, 0x2b, 0x0e, 0x73, + 0x98, 0x0f, 0x1b, 0xf0, 0xe5, 0x5e, 0x78, 0xe7, 0xcf, 0x4e, 0x09, 0xfe, 0xcd, 0xf8, + 0xba, 0x64, 0x33, 0x25, 0x24, 0xec, 0xd7, 0xa8, 0xc6, 0x20, 0xb3, 0x37, 0x34, 0x48, + 0x84, 0xd5, 0x40, 0xcd, 0xa0, 0x37, 0x6c, 0x07, 0xa4, 0x1d, 0x99, 0xaf, 0x27, 0x54, + 0xfa, 0xb8, 0x77, 0x84, 0x5f, 0x27, 0xe7, 0x6c, 0x8e, 0x10, 0x68, 0x75, 0x52, 0x43, + 0xde, 0x81, 0xbe, 0x33, 0xad, 0x3f, 0x34, 0x16, 0x8b, 0x53, 0x46, 0xe9, 0xa3, 0x00, + 0x0f, 0x81, 0x02, 0xed, 0x30, 0xdd, 0x84, 0x53, 0x22, 0xd6, 0xdc, 0xf7, 0x1f, 0x11, + 0xf8, 0xd5, 0x23, 0xcb, 0x92, 0xe9, 0x70, 0xc9, 0xde, 0xd2, 0x3b, 0x7e, 0xd2, 0x3b, + 0x35, 0xd4, 0xff, 0xa5, 0x81, 0xbc, 0x7c, 0x73, 0x0d, 0xf6, 0xad, 0x4c, 0xc0, 0x27, + 0x75, 0x2f, 0xf4, 0xb1, 0x03, 0x52, 0x55, 0x45, 0x3b, 0xfb, 0x50, 0x6b, 0x3d, 0x18, + 0x5a, 0x0b, 0xc3, 0x16, 0x47, 0xb5, 0x29, 0x99, 0x7a, 0xed, 0xe1, 0x7a, 0xc8, 0x2f, + 0xaa, 0xee, 0x4c, 0xfa, 0xca, 0x44, 0x53, 0x5c, 0x1f, 0x7a, 0xb8, 0x27, 0x7c, 0xf4, + 0xd2, 0xc7, 0xd8, 0xd5, 0xae, 0x56, 0x45, 0x52, 0xd6, 0x7e, 0x8b, 0x36, 0x05, 0xd4, + 0xa3, 0x70, 0xe2, 0xa1, 0x04, 0x3d, 0xde, 0x0c, 0x2e, 0x49, 0x73, 0xe5, 0xbc, 0x7b, + 0x1b, 0xc3, 0xba, 0xb9, 0x37, 0xb9, 0x35, 0x13, 0x88, 0xce, 0x1f, 0x86, 0xa1, 0xed, + 0x8e, 0x16, 0x8b, 0xeb, 0x8f, 0x01, 0x2a, 0x54, 0x0a, 0x30, 0xe4, 0x7f, 0x5e, 0x13, + 0x4e, 0xe9, 0x0c, 0x60, 0xb4, 0xcc, 0xec, 0x00, 0x5e, 0x96, 0x47, 0x48, 0xe5, 0x03, + 0xf6, 0xba, 0x1b, 0xac, 0x9f, 0x45, 0xcd, 0x19, 0x8c, 0x9e, 0x21, 0x1b, 0x85, 0xec, + 0x56, 0x0c, 0xe9, 0xad, 0xb6, 0xd0, 0xfa, 0x0a, 0xd3, 0x64, 0x60, 0xc8, 0xa1, 0x16, + 0x58, 0x7b, 0xfc, 0x48, 0x2c, 0xaf, 0x48, 0xfe, 0xa9, 0x2d, 0x44, 0xa7, 0xc6, 0x59, + 0x11, 0xd3, 0xc9, 0xb7, 0x52, 0xbb, 0x00, 0x58, 0xef, 0xe2, 0x47, 0x0b, 0x7e, 0x68, + 0x37, 0xe6, 0x71, 0xfa, 0xe5, 0x93, 0x59, 0x21, + ], + ), + ( + &[ + 0xbf, 0xc9, 0xf7, 0x2a, 0xf7, 0x03, 0x9e, 0x40, 0x52, 0xe7, 0x52, 0x78, 0x46, 0x98, + 0x82, 0x27, 0x76, 0x2b, 0x11, 0x83, 0xea, 0xbf, 0xec, 0x82, 0x98, 0xf7, 0xd5, 0xdf, + 0x9d, 0xbc, 0x87, 0xdd, 0xc6, 0x08, 0xa1, 0x8f, 0x3d, 0x72, 0x45, 0x3e, 0xe4, 0x93, + 0x8f, 0x10, 0x15, 0x7f, 0x80, 0xdb, 0xd2, 0x65, 0x5d, 0x96, 0x33, 0x10, 0x60, 0x76, + 0xeb, 0x90, 0x9a, 0xf2, 0x7a, 0xed, 0x7d, 0x49, 0xc3, 0x02, 0x41, 0x34, 0xde, 0xf1, + 0xee, 0xe0, 0x7d, 0x36, 0xf1, 0x71, 0x8d, 0x15, 0xeb, 0xfd, 0xd5, 0x94, 0xac, 0xfb, + 0x95, 0x19, 0x83, 0x4c, 0xbd, 0xc7, 0xbe, 0x4b, 0x4a, 0x41, 0xe8, 0xdc, 0x0d, 0x72, + 0x00, 0x46, 0xa2, 0x5f, 0x57, 0x67, 0xb2, 0x36, 0xf5, 0x0d, 0xf8, 0x5c, 0xe4, 0xe3, + 0xe5, 0xea, 0xc1, 0x38, 0x46, 0x36, 0xa5, 0x7e, 0xba, 0xb2, 0xda, 0xcc, 0xb6, 0xf2, + 0xf1, 0xee, 0xb9, 0x57, 0x54, 0x07, 0x6a, 0xf7, 0xb2, 0xcb, 0x71, 0xd8, 0xcd, 0x86, + 0x29, 0x96, 0x75, 0x5e, 0xb1, 0x90, 0xe2, 0xc0, 0x14, 0xdd, 0x8b, 0x5d, 0x5b, 0xe2, + 0x32, 0x51, 0xb5, 0xce, 0x9e, 0xd8, 0xd2, 0x71, 0xba, 0x0f, 0x91, 0x6b, 0xb0, 0xe1, + 0x7e, 0xdc, 0x52, 0x5f, 0xf7, 0x64, 0x28, 0x39, 0xa5, 0x1a, 0xfd, 0x14, 0x89, 0xfe, + 0x05, 0x1c, 0x1d, 0x6a, 0x84, 0x82, 0x5d, 0xb4, 0xaa, 0x49, 0x50, 0x9f, 0xbf, 0x2a, + 0xed, 0xb6, 0x57, 0x99, 0x6c, 0x01, 0x3e, 0x9f, 0xa3, 0xe1, 0x18, 0x5d, 0x86, 0x85, + 0x64, 0xca, 0xe1, 0xd1, 0x73, 0x18, 0x9e, 0xd7, 0xfa, 0x40, 0x2a, 0x1e, 0xf8, 0x75, + 0x0b, 0xe7, 0xb8, 0xed, 0xd7, 0x06, 0x8e, 0x56, 0x0c, 0xa1, 0xeb, 0xc4, 0xb9, 0x5b, + 0x9b, 0x24, 0x5d, 0xe4, 0x8c, 0x66, 0xb7, 0x4c, 0xd0, 0x6f, 0xdb, 0xd8, 0x40, 0x77, + 0xd7, 0xbb, 0xc9, 0x51, 0x33, 0x82, 0x53, 0x8d, 0xb4, 0xde, 0xd1, 0xe5, 0x03, 0xda, + 0x62, 0x50, 0x66, 0x2b, 0x03, 0x7e, 0x07, 0x48, 0xc5, 0x20, 0x82, 0x7e, 0x93, 0xe7, + 0x2c, 0xc2, 0x1e, 0xf3, 0xdb, 0x94, 0x8a, 0x98, 0x4c, 0x32, 0x36, 0x56, 0x74, 0xed, + 0xda, 0x9e, 0xfd, 0xa7, 0xbf, 0x5f, 0x71, 0x8c, 0x8c, 0x64, 0x6e, 0xee, 0xc7, 0xd1, + 0x97, 0x68, 0xe3, 0xce, 0x23, 0x18, 0xfc, 0x90, 0xaa, 0x53, 0xc2, 0x63, 0x98, 0x64, + 0x80, 0x02, 0x70, 0xcf, 0xe4, 0x10, 0x79, 0x8f, 0x52, 0xb5, 0x04, 0xfd, 0x07, 0xdb, + 0xa3, 0xef, 0xd4, 0x71, 0x13, 0x50, 0x59, 0x16, 0x75, 0xa4, 0xf7, 0xe8, 0x34, 0x3a, + 0xa9, 0x31, 0x97, 0x85, 0x81, 0x1b, 0x7b, 0x23, 0x01, 0x05, 0x57, 0xf5, 0x68, 0xcc, + 0x44, 0x86, 0x5d, 0xff, 0xe3, 0xc6, 0x8f, 0xd1, 0x4a, 0x85, 0x15, 0xeb, 0x42, 0x41, + 0x14, 0x86, 0x90, 0x73, 0xec, 0x7b, 0x1c, 0xcc, 0x40, 0x8b, 0xee, 0x05, 0x1a, 0xd0, + 0x91, 0xfc, 0xc7, 0x86, 0xfd, 0xc4, 0xb2, 0x13, 0xb1, 0x85, 0xb1, 0x47, 0x1a, 0x7f, + 0xe0, 0x1e, 0x0c, 0xdd, 0x46, 0xdd, 0x1d, 0x30, 0x8e, 0x45, 0x45, 0xf0, 0x6b, 0x07, + 0x28, 0x2e, 0x58, 0x58, 0xf7, 0xb7, 0xb4, 0x13, 0xe4, 0xfc, 0xbf, 0x44, 0x52, 0x85, + 0x20, 0x31, 0x31, 0x5e, 0xf9, 0x3c, 0x65, 0xec, 0x98, 0x6b, 0x9b, 0x56, 0x34, 0xab, + 0xb7, 0x2f, 0xa6, 0x7a, 0xc3, 0x03, 0x04, 0x94, 0xed, 0x12, 0xb9, 0xe3, 0xac, 0xb5, + 0xc8, 0x6f, 0x96, 0x5c, 0x97, 0x63, 0xaf, 0x6f, 0x6a, 0x91, 0x1a, 0x2f, 0xd0, 0xc6, + 0x52, 0x55, 0x8c, 0x04, 0x6c, 0xf9, 0xf1, 0x72, 0x0f, 0xf8, 0x94, 0xfc, 0x6c, 0xbe, + 0x28, 0xa1, 0x0c, 0xda, 0x74, 0x00, 0xf6, 0xf3, 0x08, 0xa4, 0xdf, 0x53, 0x76, 0x89, + 0x52, 0xc4, 0x02, 0x84, 0x8f, 0xe3, 0xb6, 0x03, + ], + &[ + 0x23, 0x54, 0xa8, 0x64, 0xd0, 0xd6, 0x68, 0xcb, 0xbe, 0xba, 0x00, 0x76, 0x49, 0xc3, + 0x04, 0x8f, 0x12, 0x74, 0xc2, 0xa8, 0x43, 0x91, 0x91, 0x97, 0x49, 0x68, 0xb6, 0x8c, + 0x98, 0x39, 0x47, 0xea, 0x31, 0xf6, 0x1b, 0x15, 0x11, 0x23, 0xc0, 0xdf, 0xe2, 0x29, + 0xd0, 0xbc, 0x0c, 0xc9, 0xcd, 0x4a, 0x31, 0x8b, 0x1c, 0xdf, 0x73, 0x8e, 0xbb, 0xc6, + 0x8c, 0x84, 0xba, 0x16, 0x9b, 0x50, 0xae, 0xb8, 0xec, 0xe4, 0xb8, 0x70, 0x6d, 0xf5, + 0xb1, 0xa4, 0xc7, 0x4c, 0x5c, 0xd4, 0x27, 0x42, 0x77, 0x93, 0xee, 0x49, 0x92, 0x48, + 0x52, 0x62, 0x3d, 0xce, 0xe0, 0x53, 0x30, 0x9a, 0x1c, 0x16, 0xe2, 0x37, 0xcf, 0x7e, + 0x45, 0xd0, 0xbd, 0x4e, 0xc5, 0x02, 0x44, 0x51, 0x5d, 0x79, 0x72, 0x5c, 0x62, 0x8a, + 0x1d, 0x2b, 0xce, 0xe6, 0x78, 0x00, 0xcf, 0x21, 0xf6, 0x70, 0xc6, 0x5f, 0xda, 0x00, + 0x0d, 0x53, 0x85, 0xef, 0x31, 0x7a, 0xa0, 0x58, 0xfb, 0x26, 0x01, 0x56, 0x08, 0x1e, + 0x84, 0x00, 0xc4, 0xa4, 0x6a, 0x1f, 0x9f, 0xb5, 0xf4, 0xe2, 0x0f, 0x2f, 0x66, 0xa2, + 0xd7, 0xd4, 0x37, 0xa2, 0xd5, 0x9e, 0x69, 0xbe, 0x2b, 0xa1, 0x7c, 0x8f, 0x93, 0x29, + 0x27, 0x3e, 0x9d, 0x2a, 0x32, 0x9f, 0xcf, 0xcd, 0x36, 0xbe, 0x2f, 0x0b, 0x1e, 0x94, + 0x9e, 0x0a, 0x5c, 0xdc, 0xe7, 0x86, 0x40, 0x8e, 0xec, 0xa3, 0xce, 0xe7, 0x6e, 0xc7, + 0x10, 0xbd, 0x7b, 0x8b, 0xb6, 0xda, 0xcf, 0xd7, 0x86, 0xd0, 0x0b, 0xb6, 0x06, 0xf7, + 0x01, 0xe7, 0x62, 0x0c, 0x3c, 0xa0, 0xb7, 0x7a, 0x60, 0x0e, 0x7b, 0xf3, 0xf2, 0x9f, + 0x55, 0x4f, 0x1d, 0xc1, 0x2a, 0xd0, 0x79, 0x5e, 0x1e, 0xbb, 0xa1, 0x7c, 0x3f, 0x0d, + 0x42, 0x1a, 0x43, 0xf5, 0xbb, 0x6b, 0x9c, 0xae, 0xd8, 0xe3, 0x12, 0x63, 0xd7, 0x14, + 0x7c, 0xb8, 0x8a, 0x50, 0x4a, 0x02, 0x99, 0x4f, 0xcf, 0x9c, 0x8c, 0x73, 0x69, 0xd5, + 0x82, 0x52, 0x3e, 0xeb, 0x40, 0xc8, 0x55, 0xb6, 0xde, 0xaa, 0x98, 0x79, 0x07, 0x01, + 0x4b, 0x0d, 0x37, 0x6e, 0x5a, 0xbc, 0xb1, 0x70, 0x53, 0x64, 0x6d, 0x9a, 0xfa, 0x22, + 0x06, 0x94, 0x1b, 0x11, 0x14, 0x6d, 0xec, 0x81, 0x25, 0x24, 0x82, 0x4d, 0x8f, 0x4d, + 0x41, 0x45, 0xd5, 0xd5, 0x3c, 0x26, 0xb4, 0x32, 0xbf, 0x59, 0x7a, 0x9c, 0x05, 0x15, + 0x10, 0x81, 0x7a, 0xc6, 0x56, 0x93, 0x5d, 0xf0, 0xfa, 0xed, 0xd4, 0x6b, 0x4b, 0xa2, + 0xef, 0xe1, 0x4f, 0x78, 0xe2, 0xab, 0x09, 0x7c, 0xe1, 0x4b, 0xbc, 0x5b, 0xfb, 0x04, + 0x0a, 0xd2, 0xf8, 0x18, 0x52, 0x3d, 0xa1, 0xff, 0x78, 0x2e, 0x7f, 0xb1, 0x77, 0xbb, + 0x1f, 0x1b, 0xfe, 0x34, 0x26, 0x14, 0x36, 0xf3, 0xab, 0xfe, 0x5f, 0x6d, 0xa1, 0x2f, + 0x0d, 0x70, 0xe1, 0xf4, 0xce, 0xe3, 0xe2, 0x93, 0xc2, 0xef, 0x41, 0xb9, 0x8d, 0x26, + 0x34, 0xf3, 0xf4, 0x4d, 0xeb, 0x19, 0x8f, 0x1c, 0x2f, 0x17, 0x67, 0x78, 0x98, 0xba, + 0xe0, 0x6d, 0x8b, 0xff, 0x02, 0x64, 0x6c, 0x6e, 0x4c, 0x6b, 0xed, 0xee, 0x00, 0x01, + 0xd7, 0x39, 0x87, 0xf8, 0x98, 0xe1, 0x64, 0x1a, 0xde, 0x1c, 0xdd, 0x12, 0xe2, 0x27, + 0x78, 0xac, 0x8d, 0x89, 0x2e, 0x8c, 0x23, 0x88, 0x59, 0xb6, 0x3e, 0xe5, 0x92, 0x65, + 0xe3, 0x16, 0x31, 0x0f, 0x71, 0x39, 0x18, 0x6e, 0x62, 0xa1, 0xe2, 0x2f, 0xe2, 0xe2, + 0x3a, 0x3b, 0x4a, 0x70, 0x1b, 0x00, 0x5d, 0xd2, 0x00, 0xa5, 0x7f, 0x53, 0x9d, 0xe4, + 0x9e, 0x2e, 0x0c, 0x9b, 0xad, 0x83, 0xc0, 0x5f, 0x93, 0x2c, 0x94, 0x8d, 0x4f, 0x81, + 0xce, 0x15, 0x95, 0x7b, 0xc1, 0x0b, 0xcd, 0x58, 0x32, 0x0c, 0xb3, 0x2e, 0x0c, 0xba, + 0x8f, 0x86, 0xcb, 0xba, 0xfb, 0x11, 0x89, 0xb1, + ], + &[ + 0x92, 0x15, 0x78, 0x80, 0x3f, 0x0f, 0x22, 0x90, 0xe7, 0x8e, 0x88, 0x81, 0x3c, 0x70, + 0x3d, 0x21, 0x1d, 0xea, 0x26, 0x52, 0x7d, 0xe3, 0x20, 0x9e, 0x86, 0x5e, 0x77, 0x8b, + 0x32, 0x5e, 0x80, 0x27, 0x9f, 0x6f, 0xa3, 0xf0, 0x76, 0xc1, 0x3e, 0x04, 0xe2, 0x19, + 0xd6, 0xc8, 0x11, 0x86, 0x11, 0x82, 0xb6, 0x96, 0x50, 0x37, 0x05, 0xbb, 0xbe, 0x44, + 0xe8, 0x70, 0x39, 0xcf, 0x5c, 0x75, 0xc3, 0xf3, 0x96, 0x58, 0x42, 0x17, 0x24, 0xb2, + 0x61, 0x76, 0x79, 0xf3, 0x75, 0x54, 0xff, 0xed, 0x1b, 0xf4, 0x33, 0x75, 0x15, 0xc5, + 0x72, 0x51, 0x62, 0xbb, 0xf5, 0xa8, 0xfe, 0x05, 0xa4, 0x5c, 0x35, 0xc7, 0x51, 0xa2, + 0xb8, 0x42, 0x2e, 0x4b, 0x15, 0xd1, 0x6a, 0x5f, 0x5e, 0xd4, 0xef, 0xdd, 0x2f, 0x31, + 0xc2, 0x0c, 0x01, 0xba, 0x38, 0x29, 0x0b, 0xc0, 0xd6, 0xa7, 0x90, 0x3d, 0xb1, 0x3a, + 0xcc, 0x0b, 0xd1, 0x8f, 0xb1, 0xf3, 0x49, 0xae, 0x80, 0x59, 0x10, 0x3c, 0x2b, 0x47, + 0xc9, 0x78, 0x07, 0xfb, 0x48, 0x8b, 0xac, 0x76, 0x48, 0x04, 0xf5, 0x74, 0x2a, 0xf6, + 0xd5, 0xfe, 0xa7, 0x8c, 0x6c, 0x52, 0x46, 0x5d, 0xa7, 0x1e, 0x80, 0x74, 0xb0, 0xcf, + 0x73, 0xaa, 0x28, 0xec, 0x36, 0x68, 0x33, 0x78, 0xa5, 0xf3, 0x4b, 0x02, 0xe9, 0x02, + 0x90, 0x5e, 0xbe, 0x2d, 0x9e, 0x8d, 0x12, 0xfa, 0xcf, 0x5b, 0x24, 0x96, 0x76, 0xb6, + 0x0c, 0xa5, 0x33, 0xe9, 0xea, 0x17, 0x57, 0xe7, 0x74, 0x3a, 0xde, 0x0f, 0xea, 0x97, + 0x23, 0x62, 0x8a, 0x7e, 0xb8, 0x4d, 0x4a, 0x65, 0x57, 0xec, 0x0c, 0x03, 0xe5, 0xe5, + 0xfb, 0xde, 0xc5, 0xc2, 0x1d, 0x6c, 0x5b, 0x95, 0x54, 0xd9, 0x6f, 0x89, 0xfe, 0x11, + 0x31, 0xf8, 0xad, 0x13, 0xec, 0x38, 0xc6, 0x16, 0x5b, 0xed, 0x4c, 0x2c, 0xc0, 0xa3, + 0xd6, 0xbf, 0x93, 0x83, 0xa1, 0xae, 0x07, 0x29, 0x5e, 0xad, 0x4b, 0xd5, 0x92, 0x38, + 0x31, 0xdd, 0x54, 0x03, 0x7d, 0xeb, 0xba, 0x2a, 0x05, 0x0e, 0xcc, 0xb6, 0xb1, 0x22, + 0xb8, 0xd6, 0xac, 0xd3, 0xe1, 0x31, 0x5c, 0x6e, 0xff, 0x26, 0xf3, 0x9b, 0xd1, 0xc8, + 0x33, 0x92, 0xa1, 0xc2, 0xe9, 0xf0, 0x9d, 0x1d, 0x04, 0x39, 0x56, 0xb1, 0xea, 0x03, + 0x6b, 0x7f, 0xbc, 0xa0, 0xd4, 0x11, 0x04, 0x02, 0xed, 0x2e, 0x18, 0x8c, 0x5a, 0xb6, + 0x6d, 0xfd, 0xd1, 0x78, 0x0e, 0x82, 0xaf, 0x58, 0x2d, 0xae, 0xed, 0x07, 0x44, 0x6d, + 0x7a, 0xf0, 0x02, 0xdd, 0xaa, 0x35, 0x84, 0xfd, 0xd9, 0x61, 0x0e, 0x74, 0x7d, 0x19, + 0x82, 0x98, 0xa2, 0xb9, 0xbb, 0x7a, 0x31, 0x6d, 0xf0, 0x38, 0xf4, 0x2f, 0x87, 0x07, + 0x0f, 0x1b, 0xc4, 0x8f, 0xad, 0x45, 0x52, 0x4c, 0xf3, 0x6b, 0x03, 0xbb, 0x3f, 0x15, + 0x39, 0x2e, 0xc2, 0x9f, 0xfe, 0x6d, 0xc5, 0xe3, 0xaf, 0xe3, 0x11, 0x3a, 0x93, 0x0f, + 0x22, 0x99, 0x68, 0xbc, 0x1c, 0x13, 0xdc, 0x21, 0x3b, 0x5a, 0x5f, 0xa4, 0x85, 0x6a, + 0x76, 0xc9, 0x03, 0x25, 0x59, 0xa9, 0xf8, 0x3b, 0x49, 0x2a, 0x70, 0x1e, 0x0f, 0x7a, + 0x10, 0x8b, 0x9d, 0x93, 0x78, 0x34, 0xd7, 0x4c, 0xcc, 0x5f, 0xd4, 0x75, 0x99, 0x93, + 0x11, 0xaa, 0x31, 0xe3, 0xa6, 0x85, 0xbf, 0x22, 0x71, 0xa6, 0xa9, 0xb2, 0x8f, 0xcb, + 0x25, 0x24, 0xa7, 0xf0, 0xc7, 0x67, 0x2e, 0xfa, 0x17, 0x16, 0xe2, 0x9b, 0xbe, 0x17, + 0xf6, 0x34, 0x9e, 0xc2, 0xc3, 0x94, 0x0f, 0x9e, 0xae, 0x87, 0x12, 0x8c, 0x70, 0xab, + 0x41, 0x74, 0x65, 0xfd, 0x09, 0x71, 0xeb, 0x50, 0x12, 0xcd, 0x6d, 0x36, 0xd8, 0x9f, + 0xc4, 0x0f, 0xb5, 0xff, 0xde, 0x8d, 0xd0, 0xf7, 0xeb, 0x82, 0xbb, 0xda, 0xe1, 0xd4, + 0x94, 0x35, 0x9e, 0x3e, 0x4a, 0xa1, 0xf3, 0x51, + ], + ), + ]; +} + +mod ecdsa { + pub const PRIVATE_KEY: [u8; 32] = [ + 0x30, 0x8d, 0x6c, 0x77, 0xcc, 0x43, 0xf7, 0xb8, 0x4f, 0x44, 0x74, 0xdc, 0x2f, 0x99, 0xf6, + 0x33, 0x3e, 0x26, 0x8a, 0x0c, 0x94, 0x4c, 0xde, 0x56, 0xff, 0xb5, 0x27, 0xb7, 0x7f, 0xa6, + 0x11, 0x0c, + ]; + + pub const PUBLIC_KEY: [u8; 64] = [ + 0x16, 0xa6, 0xbd, 0x9a, 0x66, 0x66, 0x36, 0xd0, 0x72, 0x86, 0xde, 0x78, 0xb9, 0xa1, 0xe7, + 0xf6, 0xdd, 0x67, 0x75, 0xb2, 0xc6, 0xf4, 0x2c, 0xcf, 0x83, 0x2d, 0xe4, 0x5e, 0x1e, 0x22, + 0x9d, 0x84, 0x0a, 0xca, 0x0d, 0xdd, 0xe8, 0xf5, 0xc8, 0x2f, 0x84, 0x10, 0xb5, 0x62, 0xc2, + 0x3a, 0x46, 0xde, 0xcd, 0xcb, 0x59, 0x6e, 0x40, 0x02, 0xcb, 0x10, 0xc6, 0x2f, 0x5b, 0x5e, + 0xb5, 0xf2, 0xa7, 0xd7, + ]; + + pub const K_SIGNATURE_PAIRS: [([u8; 32], [u8; 64]); 10] = [ + ( + [ + 0x48, 0x4a, 0x19, 0x66, 0x02, 0x50, 0x0e, 0xf2, 0xd0, 0xbe, 0x90, 0x84, 0x23, 0x8e, + 0x45, 0x09, 0x6c, 0x23, 0x8b, 0x1b, 0x74, 0xa8, 0x6b, 0x17, 0x46, 0x62, 0x75, 0xd2, + 0xfa, 0x27, 0x7e, 0x1b, + ], + [ + 0x21, 0xea, 0x0f, 0xfd, 0x35, 0x43, 0xdf, 0x7a, 0xdb, 0xf5, 0x4f, 0x88, 0x0e, 0x9d, + 0xd2, 0xa7, 0x26, 0x4f, 0x2f, 0x96, 0xe9, 0x85, 0x5f, 0x67, 0xa9, 0x82, 0x46, 0xfe, + 0x46, 0xef, 0x92, 0x9d, 0x3c, 0x59, 0x7c, 0x22, 0x4b, 0x69, 0x80, 0xf7, 0x01, 0x46, + 0x09, 0xce, 0x13, 0x59, 0xfd, 0x21, 0xd1, 0x45, 0x65, 0xfb, 0xb0, 0x82, 0x1b, 0x91, + 0xce, 0x1e, 0x87, 0xf5, 0xe5, 0xc8, 0xdc, 0x9c, + ], + ), + ( + [ + 0xea, 0x40, 0xe8, 0x9d, 0xf6, 0x63, 0xf4, 0x3e, 0x71, 0xf2, 0x6b, 0x7f, 0xcd, 0xa0, + 0x15, 0x59, 0x13, 0x4f, 0xa9, 0x17, 0xbd, 0x5f, 0xbc, 0xf3, 0x36, 0xfb, 0x48, 0x14, + 0x8f, 0x59, 0x99, 0x1d, + ], + [ + 0x9a, 0x84, 0x64, 0x3b, 0xd1, 0xb8, 0xe2, 0xa6, 0xe3, 0xc7, 0x96, 0x9b, 0xfa, 0x00, + 0xac, 0x65, 0x19, 0xa8, 0x3e, 0x22, 0x2e, 0x40, 0x7d, 0x90, 0x98, 0x92, 0xce, 0x3b, + 0x77, 0x4e, 0x8c, 0x41, 0xe7, 0xa1, 0xcd, 0xb1, 0xc4, 0x0a, 0xc0, 0x73, 0xfa, 0x87, + 0x5f, 0xa5, 0xae, 0xcf, 0x27, 0x14, 0x06, 0x38, 0x9f, 0x4c, 0x7f, 0xaa, 0xf9, 0x76, + 0x6e, 0x49, 0x03, 0x0c, 0xc8, 0x33, 0x26, 0x03, + ], + ), + ( + [ + 0x99, 0xde, 0xf2, 0x6b, 0xa6, 0xfe, 0x92, 0x0f, 0xd6, 0x33, 0x3a, 0x1b, 0x21, 0x2c, + 0xcb, 0xd2, 0x50, 0x81, 0x57, 0xad, 0x26, 0x31, 0xea, 0x56, 0x23, 0x94, 0x69, 0x3b, + 0xc3, 0xe7, 0x96, 0xd7, + ], + [ + 0x47, 0x1a, 0x16, 0x6b, 0xde, 0x2e, 0x34, 0xb3, 0xc6, 0x80, 0xa2, 0x18, 0xed, 0xa7, + 0xfa, 0xc6, 0x7f, 0xfc, 0x77, 0xae, 0x80, 0xce, 0x18, 0x90, 0x51, 0x1f, 0x4d, 0x23, + 0x8a, 0x96, 0x62, 0x25, 0xa7, 0x5a, 0xc7, 0x47, 0x68, 0xa2, 0xf0, 0x76, 0x5e, 0x01, + 0x6b, 0x29, 0xb2, 0x9d, 0xba, 0x3b, 0x71, 0x8a, 0x7c, 0xfd, 0xaa, 0x49, 0x53, 0xe0, + 0x90, 0x62, 0xce, 0x06, 0x95, 0x55, 0xd4, 0xc4, + ], + ), + ( + [ + 0x91, 0xda, 0x2c, 0xea, 0x22, 0xc3, 0x08, 0x44, 0x5c, 0x01, 0x0e, 0x2b, 0x00, 0x74, + 0x44, 0x05, 0x14, 0x50, 0x25, 0x92, 0xb3, 0xde, 0xe9, 0xcd, 0xb0, 0x67, 0x25, 0x10, + 0x26, 0x8a, 0x66, 0xb6, + ], + [ + 0x89, 0xaa, 0x32, 0x68, 0x08, 0xbf, 0x3f, 0xd8, 0xbb, 0x13, 0xc5, 0x51, 0xa6, 0x0e, + 0x13, 0x3f, 0xb5, 0x6f, 0x96, 0xcd, 0x7d, 0x9f, 0xe7, 0xd4, 0x17, 0xef, 0xad, 0x93, + 0x14, 0xed, 0x4f, 0x0f, 0xdb, 0x34, 0xc1, 0xc3, 0xf4, 0xc9, 0x11, 0x9e, 0xd7, 0xe7, + 0x23, 0xbc, 0xd3, 0x5c, 0x73, 0x57, 0xd5, 0x74, 0x75, 0x90, 0xaf, 0x4e, 0x60, 0x47, + 0x57, 0xe0, 0x16, 0xc2, 0x0d, 0x9e, 0xce, 0x44, + ], + ), + ( + [ + 0x3d, 0x3d, 0x65, 0x81, 0x9d, 0xc3, 0xd1, 0x23, 0xde, 0x2d, 0xe0, 0x92, 0x99, 0x7d, + 0x0b, 0xb5, 0xab, 0x93, 0x02, 0x0a, 0x8b, 0x0d, 0x37, 0xe3, 0xe0, 0x0f, 0xf7, 0x91, + 0x60, 0x39, 0xf4, 0x97, + ], + [ + 0x56, 0x99, 0xc2, 0x70, 0x77, 0x34, 0x71, 0x9a, 0xdb, 0xcf, 0xb3, 0xc1, 0x0a, 0x5d, + 0x2a, 0x18, 0xbd, 0x35, 0xcc, 0x46, 0x6c, 0xfb, 0x87, 0x0a, 0xe2, 0xc2, 0x6f, 0xdf, + 0x23, 0x70, 0x2c, 0x49, 0xc0, 0xd7, 0x2a, 0x54, 0xf6, 0xd6, 0x46, 0x5f, 0xb0, 0x59, + 0xe0, 0x70, 0x58, 0xae, 0x64, 0x9c, 0x3f, 0x2d, 0x48, 0xad, 0xf6, 0x66, 0xe9, 0x03, + 0x88, 0xf7, 0x0a, 0x0e, 0x2a, 0xec, 0xba, 0x12, + ], + ), + ( + [ + 0x4e, 0xa9, 0xce, 0xda, 0xce, 0xe2, 0xe9, 0x58, 0x43, 0xcd, 0x90, 0x70, 0x75, 0xc6, + 0xe8, 0x58, 0x19, 0x74, 0x09, 0x0a, 0x75, 0xa3, 0xfb, 0xbd, 0x38, 0x97, 0xba, 0x92, + 0xb3, 0x87, 0x81, 0x88, + ], + [ + 0x4a, 0x20, 0xe6, 0xf3, 0xf0, 0x96, 0xc4, 0xad, 0x6b, 0xe4, 0x95, 0xae, 0xeb, 0xee, + 0xa9, 0xb8, 0x90, 0x45, 0x87, 0xfb, 0x32, 0x8e, 0x30, 0xce, 0x49, 0xaa, 0x11, 0x7f, + 0x11, 0x2a, 0xba, 0xa1, 0x54, 0xe0, 0xb3, 0x68, 0x25, 0x76, 0x5c, 0xf9, 0x0b, 0x46, + 0xdf, 0x8d, 0x8b, 0x99, 0x1b, 0x9d, 0x2d, 0x9f, 0xfb, 0x52, 0xcc, 0x32, 0xb2, 0x4c, + 0x2a, 0x93, 0xff, 0x23, 0xe5, 0xf7, 0x88, 0x9f, + ], + ), + ( + [ + 0x8d, 0xbc, 0xea, 0x73, 0x54, 0x1b, 0x93, 0x29, 0xc6, 0xb6, 0x82, 0xbc, 0xd2, 0xa6, + 0xeb, 0x68, 0x09, 0x9c, 0x64, 0x97, 0x34, 0xc9, 0x3c, 0xe8, 0x56, 0xd7, 0x3a, 0xdb, + 0x32, 0x78, 0xb6, 0x0a, + ], + [ + 0x91, 0x1a, 0x54, 0x4e, 0xc3, 0x77, 0x3b, 0x37, 0x48, 0x84, 0xbc, 0x84, 0xc2, 0x6d, + 0x32, 0x9b, 0xc9, 0x5f, 0x24, 0x7c, 0x4d, 0x29, 0xc7, 0xd6, 0xd5, 0x23, 0xf5, 0x25, + 0x49, 0x6f, 0xf0, 0xd3, 0xa3, 0x0f, 0xf7, 0x2a, 0xa9, 0x86, 0xb1, 0xd1, 0xf0, 0x31, + 0xa5, 0x71, 0x40, 0x8c, 0xc4, 0x0f, 0x56, 0xa0, 0x8c, 0x4b, 0xfc, 0x7d, 0xe5, 0x98, + 0x90, 0xdb, 0xcd, 0x68, 0xe9, 0x4b, 0x4f, 0x9c, + ], + ), + ( + [ + 0x2b, 0xe8, 0x71, 0x47, 0x76, 0x0c, 0x8e, 0x96, 0x7c, 0xcf, 0x2b, 0x78, 0xc2, 0x89, + 0xbd, 0xef, 0x8d, 0x2f, 0x7a, 0xe7, 0xa0, 0xab, 0x8b, 0x84, 0xa8, 0x43, 0xe6, 0x33, + 0x36, 0x67, 0xcd, 0x08, + ], + [ + 0x6d, 0xa1, 0x3e, 0xf9, 0xf0, 0x53, 0x89, 0x67, 0xb0, 0xf4, 0xe3, 0x86, 0xb3, 0x56, + 0x7a, 0x9a, 0xcf, 0xba, 0x94, 0xb8, 0xba, 0xbf, 0xb6, 0xa0, 0x7f, 0xaa, 0xc4, 0xd8, + 0xcb, 0x2c, 0x3a, 0xf4, 0x11, 0xc4, 0x3a, 0x17, 0x52, 0x10, 0x5d, 0xde, 0x72, 0x5d, + 0x5a, 0xc1, 0xd9, 0x3a, 0x5f, 0x56, 0xb8, 0x79, 0x78, 0x4c, 0x71, 0xb3, 0x05, 0x69, + 0x52, 0x63, 0x06, 0xe8, 0xe3, 0xe2, 0xfa, 0x10, + ], + ), + ( + [ + 0x87, 0x2f, 0x09, 0x31, 0x90, 0xcf, 0xeb, 0x70, 0x96, 0x9a, 0x67, 0x59, 0xa9, 0x8b, + 0x40, 0xe3, 0xfe, 0xfd, 0xeb, 0x37, 0x53, 0xcf, 0x62, 0xfe, 0x27, 0x13, 0x7b, 0xe6, + 0x08, 0xf0, 0x3d, 0x1c, + ], + [ + 0x7c, 0x93, 0x91, 0x44, 0x1e, 0x84, 0x07, 0xc2, 0x22, 0xd3, 0x92, 0x3b, 0xb0, 0xfe, + 0x41, 0xe8, 0x8d, 0x1e, 0x1d, 0xd5, 0x56, 0x56, 0x05, 0x31, 0x44, 0xc8, 0xa2, 0x4b, + 0xee, 0xdc, 0x8c, 0x36, 0x3f, 0xc5, 0xe9, 0xfa, 0x57, 0x1e, 0x20, 0x5f, 0xc5, 0x97, + 0xd1, 0xe8, 0x84, 0xaf, 0x74, 0x10, 0xc6, 0x08, 0x6b, 0x3e, 0xea, 0x61, 0x7c, 0x9a, + 0x77, 0x54, 0x31, 0x8b, 0x3b, 0x8b, 0x04, 0xc5, + ], + ), + ( + [ + 0xca, 0x8b, 0x60, 0xf1, 0x88, 0x6d, 0xb6, 0xf7, 0x33, 0x4f, 0xcc, 0x39, 0x9c, 0xf4, + 0x82, 0xe7, 0xde, 0x42, 0x37, 0x8d, 0xb9, 0x97, 0xa6, 0x5e, 0x4b, 0x0e, 0xc8, 0xaa, + 0x09, 0xbc, 0xee, 0xac, + ], + [ + 0x69, 0xc1, 0x7c, 0x3f, 0xaa, 0x06, 0x0a, 0x00, 0x0d, 0xdf, 0x08, 0x1d, 0x38, 0xe3, + 0xa8, 0xc5, 0xe7, 0xa5, 0x80, 0xbd, 0x48, 0x27, 0xdf, 0x20, 0x12, 0x36, 0x9f, 0x4b, + 0xfd, 0x59, 0x2a, 0x92, 0x95, 0x71, 0xa3, 0x0c, 0x58, 0x7d, 0x6a, 0x6d, 0x7b, 0x1f, + 0xc1, 0x43, 0x5a, 0x6d, 0x55, 0x8e, 0xe0, 0xc5, 0x76, 0xc6, 0xf0, 0xdc, 0x92, 0x77, + 0x23, 0x14, 0x49, 0xb6, 0xa6, 0xe5, 0x1d, 0x1c, + ], + ), + ]; +} diff --git a/boards/feather_m4_can/examples/pwm.rs b/boards/feather_m4_can/examples/pwm.rs new file mode 100644 index 000000000000..93703c354880 --- /dev/null +++ b/boards/feather_m4_can/examples/pwm.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] + +// Pulse Width Modulation +// +// cargo build --features="unproven" + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use cortex_m_rt::entry; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::pwm::Pwm4; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut delay = Delay::new(core.SYST, &mut clocks); + let pins = bsp::Pins::new(peripherals.PORT); + let red_led: bsp::RedLedPwm = pins.d13.into(); + + let gclk0 = clocks.gclk0(); + let mut pwm4 = Pwm4::new( + &clocks.tc4_tc5(&gclk0).unwrap(), + 1.khz(), + peripherals.TC4, + hal::pwm::TC4Pinout::Pa23(red_led), + &mut peripherals.MCLK, + ); + let max_duty = pwm4.get_max_duty(); + + // The red led will light up and toggle between 2 brightness + // levels every second. + loop { + pwm4.set_duty(max_duty / 2); + delay.delay_ms(1000u16); + pwm4.set_duty(max_duty / 8); + delay.delay_ms(1000u16); + } +} diff --git a/boards/feather_m4_can/examples/serial.rs b/boards/feather_m4_can/examples/serial.rs new file mode 100644 index 000000000000..8be0b1b49196 --- /dev/null +++ b/boards/feather_m4_can/examples/serial.rs @@ -0,0 +1,55 @@ +#![no_std] +#![no_main] + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{entry, periph_alias, pin_alias}; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::gclk::genctrl::SRC_A; +use hal::pac::gclk::pchctrl::GEN_A; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::time::Hertz; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + clocks.configure_gclk_divider_and_source(GEN_A::GCLK2, 1, SRC_A::DFLL, false); + + let pins = bsp::Pins::new(peripherals.PORT); + let uart_rx = pin_alias!(pins.uart_rx); + let uart_tx = pin_alias!(pins.uart_tx); + let mut delay = Delay::new(core.SYST, &mut clocks); + let uart_sercom = periph_alias!(peripherals.uart_sercom); + + let mut uart = bsp::uart( + &mut clocks, + Hertz(19200), + uart_sercom, + &mut peripherals.MCLK, + uart_rx, + uart_tx, + ); + + loop { + for byte in b"Hello, world!" { + nb::block!(uart.write(*byte)).unwrap(); + } + delay.delay_ms(1000u16); + } +} diff --git a/boards/feather_m4_can/examples/sleeping_timer_rtc.rs b/boards/feather_m4_can/examples/sleeping_timer_rtc.rs new file mode 100644 index 000000000000..5c0e66d899f5 --- /dev/null +++ b/boards/feather_m4_can/examples/sleeping_timer_rtc.rs @@ -0,0 +1,91 @@ +//! Uses an RTC in standby mode to blink an LED for maximum power savings. +#![no_std] +#![no_main] + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{interrupt, CorePeripherals, Peripherals, RTC}; +use hal::prelude::*; +use hal::rtc; +use hal::sleeping_delay::SleepingDelay; + +use core::sync::atomic; +use cortex_m::peripheral::NVIC; + +/// Shared atomic between RTC interrupt and sleeping_delay module +static INTERRUPT_FIRED: atomic::AtomicBool = atomic::AtomicBool::new(false); + +#[entry] +fn main() -> ! { + // Configure all of our peripherals/clocks + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let _clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + // Configure the RTC. a 1024 Hz clock is configured for us when enabling our + // main clock + let timer = rtc::Rtc::count32_mode(peripherals.RTC, 1024.hz(), &mut peripherals.MCLK); + let mut sleeping_delay = SleepingDelay::new(timer, &INTERRUPT_FIRED); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + // enable interrupts + unsafe { + core.NVIC.set_priority(interrupt::RTC, 2); + NVIC::unmask(interrupt::RTC); + } + + // Turn off unnecessary peripherals + peripherals.MCLK.ahbmask.modify(|_, w| { + w.usb_().clear_bit(); + w.dmac_().clear_bit() + }); + peripherals.MCLK.apbamask.modify(|_, w| { + w.eic_().clear_bit(); + w.wdt_().clear_bit() + }); + peripherals.MCLK.apbbmask.modify(|_, w| { + w.usb_().clear_bit(); + w.nvmctrl_().clear_bit(); + w.dsu_().clear_bit() + }); + + // Configure our red LED and blink forever, sleeping between! + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + loop { + red_led.set_low().unwrap(); + sleeping_delay.delay_ms(1_000u32); + red_led.set_high().unwrap(); + sleeping_delay.delay_ms(100u32); + } +} + +#[interrupt] +fn RTC() { + // Let the sleepingtimer know that the interrupt fired, and clear it + INTERRUPT_FIRED.store(true, atomic::Ordering::Relaxed); + unsafe { + RTC::ptr() + .as_ref() + .unwrap() + .mode0() + .intflag + .modify(|_, w| w.cmp0().set_bit()); + } +} diff --git a/boards/feather_m4_can/examples/smart_eeprom.rs b/boards/feather_m4_can/examples/smart_eeprom.rs new file mode 100644 index 000000000000..ce8c39f15d19 --- /dev/null +++ b/boards/feather_m4_can/examples/smart_eeprom.rs @@ -0,0 +1,225 @@ +#![no_std] +#![no_main] +#![allow(clippy::bool_comparison)] + +use bsp::ehal; +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use ehal::digital::v2::ToggleableOutputPin; +use hal::clock::GenericClockController; +use hal::nvm::{smart_eeprom, Nvm}; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::usb::UsbBus; + +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; + +use core::sync::atomic; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + let mut nvm = Nvm::new(peripherals.NVMCTRL); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + pins.usb_dm, + pins.usb_dp, + peripherals.USB, + &mut clocks, + &mut peripherals.MCLK, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB_OTHER, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT0, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT1, 1); + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + } + + while USER_PRESENT.load(atomic::Ordering::Acquire) == false { + cycle_delay(25 * 1024 * 1024); + red_led.toggle().ok(); + } + + match nvm.smart_eeprom() { + Ok(se) => { + use smart_eeprom::SmartEepromMode::*; + let mut seeprom = match se { + Unlocked(se) => se, + Locked(se) => se.unlock(), + }; + let write_buf = [0x01u8, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; + serial_writeln!("Write dummy data"); + seeprom.set(0x10, &write_buf); + seeprom.set(0x18, &write_buf); + serial_writeln!("Read dummy data"); + let mut read_buf = [0u8; 14]; + seeprom.get(0x14, &mut read_buf); + read_buf.rotate_left(4); + if read_buf[..8] == write_buf { + serial_writeln!("Smart EEPROM test successful"); + } else { + serial_writeln!("Smart EEPROM test failed"); + } + } + Err(e) => { + serial_writeln!("Failed to initialize Smart EEPROM: {:?}!", e); + } + } + + loop { + cycle_delay(5 * 1024 * 1024); + red_led.toggle().ok(); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +static USER_PRESENT: atomic::AtomicBool = atomic::AtomicBool::new(false); + +/// Borrows the global singleton `UsbSerial` for a brief period with interrupts +/// disabled +/// +/// # Arguments +/// `borrower`: The closure that gets run borrowing the global `UsbSerial` +/// +/// # Safety +/// the global singleton `UsbSerial` can be safely borrowed because we disable +/// interrupts while it is being borrowed, guaranteeing that interrupt handlers +/// like `USB` cannot mutate `UsbSerial` while we are as well. +/// +/// # Panic +/// If `init` has not been called and we haven't initialized our global +/// singleton `UsbSerial`, we will panic. +fn usbserial_get(borrower: T) -> R +where + T: Fn(&mut SerialPort) -> R, +{ + usb_free(|_| unsafe { + let usb_serial = USB_SERIAL.as_mut().expect("UsbSerial not initialized"); + borrower(usb_serial) + }) +} + +/// Execute closure `f` in an interrupt-free context. +/// +/// This as also known as a "critical section". +#[inline] +fn usb_free(f: F) -> R +where + F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R, +{ + NVIC::mask(interrupt::USB_OTHER); + NVIC::mask(interrupt::USB_TRCPT0); + NVIC::mask(interrupt::USB_TRCPT1); + + let r = f(unsafe { &cortex_m::interrupt::CriticalSection::new() }); + + unsafe { + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + }; + + r +} + +/// Writes the given message out over USB serial. +/// +/// # Arguments +/// * println args: variable arguments passed along to `core::write!` +/// +/// # Warning +/// as this function deals with a static mut, and it is also accessed in the +/// USB interrupt handler, we both have unsafe code for unwrapping a static mut +/// as well as disabling of interrupts while we do so. +/// +/// # Safety +/// the only time the static mut is used, we have interrupts disabled so we know +/// we have sole access +#[macro_export] +macro_rules! serial_writeln { + ($($tt:tt)+) => {{ + use core::fmt::Write; + + let mut s: heapless::String<256> = heapless::String::new(); + core::write!(&mut s, $($tt)*).unwrap(); + usbserial_get(|usbserial| { + usbserial.write(s.as_bytes()).ok(); + usbserial.write("\r\n".as_bytes()).ok(); + }); + }}; +} + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + if count > 0 { + USER_PRESENT.store(true, atomic::Ordering::Release); + } + serial.write(&buf[..count]).unwrap(); + }; + }; + } + }; +} + +#[interrupt] +fn USB_OTHER() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT0() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT1() { + poll_usb(); +} diff --git a/boards/feather_m4_can/examples/timers.rs b/boards/feather_m4_can/examples/timers.rs new file mode 100644 index 000000000000..fee4bf54ffbd --- /dev/null +++ b/boards/feather_m4_can/examples/timers.rs @@ -0,0 +1,51 @@ +#![no_std] +#![no_main] + +// Simple Peripheral Timer Example + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::Peripherals; +use hal::prelude::*; + +use hal::timer::TimerCounter; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + // Using the red LED as the feedback for this simple timer example. + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + + // gclk0 represents a configured clock using the 120MHz oscillator. + let gclk0 = clocks.gclk0(); + // Configure a clock for TC2 and TC3 peripherals + let timer_clock = clocks.tc2_tc3(&gclk0).unwrap(); + //Instantiate a timer object for the TC2 peripheral + let mut timer = TimerCounter::tc2_(&timer_clock, peripherals.TC2, &mut peripherals.MCLK); + // Start the timer such that it runs at 50Hz + timer.start(50u32.hz()); + + // Toggle the red LED at the frequency set by the timer above. + loop { + red_led.set_high().unwrap(); + nb::block!(timer.wait()).ok(); + red_led.set_low().unwrap(); + nb::block!(timer.wait()).ok(); + } +} diff --git a/boards/feather_m4_can/examples/trng.rs b/boards/feather_m4_can/examples/trng.rs new file mode 100644 index 000000000000..020ace584c13 --- /dev/null +++ b/boards/feather_m4_can/examples/trng.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +// True Random Number Generator - trng + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; + +use hal::trng::Trng; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + // We will use the red led and a delay in this simplest possible + // demonstration of the random number generator. + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led = pins.d13.into_push_pull_output(); + let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks); + + // Create a struct as a representation of the random number generator peripheral + let trng = Trng::new(&mut peripherals.MCLK, peripherals.TRNG); + + // Simple loop that blinks the red led with random on and off times that are + // sourced from the random number generator. + loop { + red_led.set_high().unwrap(); + delay.delay_ms(trng.random_u8()); + red_led.set_low().unwrap(); + delay.delay_ms(trng.random_u8()); + } +} diff --git a/boards/feather_m4_can/examples/uart.rs b/boards/feather_m4_can/examples/uart.rs new file mode 100644 index 000000000000..82cf613eece8 --- /dev/null +++ b/boards/feather_m4_can/examples/uart.rs @@ -0,0 +1,104 @@ +//! This example showcases the uart::v2 module. + +#![no_std] +#![no_main] + +use cortex_m::asm; +use panic_halt as _; + +use bsp::hal; +use bsp::pac; +use feather_m4_can as bsp; + +use bsp::{entry, periph_alias, pin_alias}; +use hal::clock::GenericClockController; +use hal::dmac::{DmaController, PriorityLevel}; +use hal::prelude::*; + +use pac::Peripherals; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + + let mut mclk = peripherals.MCLK; + let dmac = peripherals.DMAC; + let pins = bsp::Pins::new(peripherals.PORT); + + // Take RX and TX pins + let uart_rx = pin_alias!(pins.uart_rx); + let uart_tx = pin_alias!(pins.uart_tx); + + // Setup DMA channels for later use + let mut dmac = DmaController::init(dmac, &mut peripherals.PM); + let channels = dmac.split(); + + let chan0 = channels.0.init(PriorityLevel::LVL0); + let chan1 = channels.1.init(PriorityLevel::LVL0); + + let uart_sercom = periph_alias!(peripherals.uart_sercom); + + // Setup UART peripheral + let uart = bsp::uart( + &mut clocks, + 9600.hz(), + uart_sercom, + &mut mclk, + uart_rx, + uart_tx, + ); + + // Split uart in rx + tx halves + let (mut rx, mut tx) = uart.split(); + + // Get a 50 byte buffer to store data to send/receive + const LENGTH: usize = 50; + let rx_buffer: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap(); + let tx_buffer: &'static mut [u8; LENGTH] = + cortex_m::singleton!(: [u8; LENGTH] = [0x00; LENGTH]).unwrap(); + + // For fun, store numbers from 0 to 49 in buffer + for (i, c) in tx_buffer.iter_mut().enumerate() { + *c = i as u8; + } + + // Send data in a blocking way + for c in tx_buffer.iter() { + nb::block!(tx.write(*c)).unwrap(); + } + + // We'll now receive data in a blocking way + rx.flush_rx_buffer(); + for c in rx_buffer.iter_mut() { + *c = nb::block!(rx.read()).unwrap(); + } + + // Finally, we'll receive AND send data at the same time with DMA + + // Setup a DMA transfer to send our data asynchronously. + // We'll set the waker to be a no-op + let tx_dma = tx.send_with_dma(tx_buffer, chan0, |_| {}); + + // Setup a DMA transfer to receive our data asynchronously. + // Again, we'll set the waker to be a no-op + let rx_dma = rx.receive_with_dma(rx_buffer, chan1, |_| {}); + + // Wait for transmit DMA transfer to complete + let (_chan0, _tx_buffer, _tx) = tx_dma.wait(); + + // Wait for receive DMA transfer to complete + let (_chan1, _rx_buffer, _rx) = rx_dma.wait(); + + loop { + // Go to sleep + asm::wfi(); + } +} diff --git a/boards/feather_m4_can/examples/uart_poll_echo.rs b/boards/feather_m4_can/examples/uart_poll_echo.rs new file mode 100644 index 000000000000..e4e5b58033a2 --- /dev/null +++ b/boards/feather_m4_can/examples/uart_poll_echo.rs @@ -0,0 +1,77 @@ +#![no_std] +#![no_main] + +// Simple example of reading and writing to the UART +// +// Polls the UART in the main loop. Reads any bytes in +// and echoes them back out. You need to connect a +// TTL level serial port to the TX and RX pins in order +// to see the uart working. + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{entry, periph_alias, pin_alias}; +use hal::clock::GenericClockController; +use hal::delay::Delay; +use hal::pac::gclk::genctrl::SRC_A; +use hal::pac::gclk::pchctrl::GEN_A; +use hal::pac::{CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::time::Hertz; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + clocks.configure_gclk_divider_and_source(GEN_A::GCLK2, 1, SRC_A::DFLL, false); + + let pins = bsp::Pins::new(peripherals.PORT); + let mut delay = Delay::new(core.SYST, &mut clocks); + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // Take RX and TX pins + let uart_rx = pin_alias!(pins.uart_rx); + let uart_tx = pin_alias!(pins.uart_tx); + let uart_sercom = periph_alias!(peripherals.uart_sercom); + + let mut uart = bsp::uart( + &mut clocks, + Hertz(19200), + uart_sercom, + &mut peripherals.MCLK, + uart_rx, + uart_tx, + ); + + // Write out a message on start up + for byte in b"Hello, world!" { + nb::block!(uart.write(*byte)).unwrap(); + } + + loop { + match uart.read() { + Ok(byte) => { + nb::block!(uart.write(byte)).unwrap(); + + // Blink the red led to show that a character has arrived + red_led.set_high().unwrap(); + delay.delay_ms(2u16); + red_led.set_low().unwrap(); + } + Err(_) => delay.delay_ms(5u16), + }; + } +} diff --git a/boards/feather_m4_can/examples/usb_echo.rs b/boards/feather_m4_can/examples/usb_echo.rs new file mode 100644 index 000000000000..eece8513a032 --- /dev/null +++ b/boards/feather_m4_can/examples/usb_echo.rs @@ -0,0 +1,114 @@ +#![no_std] +#![no_main] + +use bsp::hal; +use feather_m4_can as bsp; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use hal::clock::GenericClockController; +use hal::pac::{interrupt, CorePeripherals, Peripherals}; +use hal::prelude::*; +use hal::usb::UsbBus; + +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_serial::{SerialPort, USB_CLASS_CDC}; + +use cortex_m::asm::delay as cycle_delay; +use cortex_m::peripheral::NVIC; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + let mut clocks = GenericClockController::with_external_32kosc( + peripherals.GCLK, + &mut peripherals.MCLK, + &mut peripherals.OSC32KCTRL, + &mut peripherals.OSCCTRL, + &mut peripherals.NVMCTRL, + ); + let pins = bsp::Pins::new(peripherals.PORT); + let mut red_led: bsp::RedLed = pins.d13.into(); + + let bus_allocator = unsafe { + USB_ALLOCATOR = Some(bsp::usb_allocator( + pins.usb_dm, + pins.usb_dp, + peripherals.USB, + &mut clocks, + &mut peripherals.MCLK, + )); + USB_ALLOCATOR.as_ref().unwrap() + }; + + unsafe { + USB_SERIAL = Some(SerialPort::new(bus_allocator)); + USB_BUS = Some( + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + .manufacturer("Fake company") + .product("Serial port") + .serial_number("TEST") + .device_class(USB_CLASS_CDC) + .build(), + ); + } + + unsafe { + core.NVIC.set_priority(interrupt::USB_OTHER, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT0, 1); + core.NVIC.set_priority(interrupt::USB_TRCPT1, 1); + NVIC::unmask(interrupt::USB_OTHER); + NVIC::unmask(interrupt::USB_TRCPT0); + NVIC::unmask(interrupt::USB_TRCPT1); + } + + loop { + cycle_delay(5 * 1024 * 1024); + red_led.toggle().unwrap(); + } +} + +static mut USB_ALLOCATOR: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_SERIAL: Option> = None; + +fn poll_usb() { + unsafe { + if let Some(usb_dev) = USB_BUS.as_mut() { + if let Some(serial) = USB_SERIAL.as_mut() { + usb_dev.poll(&mut [serial]); + let mut buf = [0u8; 64]; + + if let Ok(count) = serial.read(&mut buf) { + for (i, c) in buf.iter().enumerate() { + if i >= count { + break; + } + serial.write(&[*c]).unwrap(); + } + }; + }; + }; + }; +} + +#[interrupt] +fn USB_OTHER() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT0() { + poll_usb(); +} + +#[interrupt] +fn USB_TRCPT1() { + poll_usb(); +} diff --git a/boards/feather_m4_can/memory.x b/boards/feather_m4_can/memory.x new file mode 100644 index 000000000000..073403cecc8e --- /dev/null +++ b/boards/feather_m4_can/memory.x @@ -0,0 +1,8 @@ +MEMORY +{ + /* Leave 16k for the default bootloader on the Feather M4 */ + FLASH (rx) : ORIGIN = 0x00000000 + 16K, LENGTH = 512K - 16K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K +} +_stack_start = ORIGIN(RAM) + LENGTH(RAM); + diff --git a/boards/feather_m4_can/src/lib.rs b/boards/feather_m4_can/src/lib.rs new file mode 100644 index 000000000000..df839b63012c --- /dev/null +++ b/boards/feather_m4_can/src/lib.rs @@ -0,0 +1,295 @@ +#![no_std] +#![deny(missing_docs)] + +//! Board support crate for Adafruit's Feather M4 CAN Express, +//! an ATSAME51-based board in Feather form factor. + +#[cfg(feature = "rt")] +pub use cortex_m_rt::entry; + +pub use atsamd_hal as hal; +pub use hal::ehal; +pub use hal::pac; + +use hal::clock::GenericClockController; +use hal::sercom::{ + i2c, spi, + uart::{self, BaudMode, Oversampling}, + IoSet1, UndocIoSet1, +}; +use hal::time::Hertz; + +#[cfg(feature = "usb")] +use hal::usb::usb_device::bus::UsbBusAllocator; +#[cfg(feature = "usb")] +pub use hal::usb::UsbBus; + +hal::bsp_peripherals!( + SERCOM1 { SpiSercom } + SERCOM2 { I2cSercom } + SERCOM5 { UartSercom } +); + +hal::bsp_pins!( + PA02 { + /// Analog pin 0. Can act as a true analog output + /// as it has a DAC (which is not currently supported + /// by this hal) as well as input. + name: a0, + } + PA05 { + /// Analog Pin 1 + name: a1, + } + PB08 { + /// Analog Pin 2 + name: a2, + } + PB09 { + /// Analog Pin 3 + name: a3, + } + PA04 { + /// Analog Pin 4 + name: a4, + } + PA06 { + /// Analog Pin 5 + name: a5, + } + PB01 { + /// Analog Vdiv (1/2 resistor divider for monitoring the battery) + name: battery, + } + + PB17 { + /// Pin 0, UART rx + name: d0, + aliases: { + AlternateC: UartRx + } + } + PB12 { + /// PB12: TCAN1051H on-board CAN device standby pin + aliases: { + PushPullOutput: Tcan1051S + } + } + PB14 { + /// PB14: TCAN1051H on-board CAN device transmit pin + aliases: { + AlternateH: Tcan1051Tx + } + } + PB15 { + /// PB15: TCAN1051H on-board CAN device receive pin + aliases: { + AlternateH: Tcan1051Rx + } + } + PB16 { + /// Pin 1, UART tx + name: d1, + aliases: { + AlternateC: UartTx + } + } + PA14 { + /// Pin 4, PWM capable + name: d4, + } + PA16 { + /// Pin 5, PWM capable + name: d5, + } + PA18 { + /// Pin 6, PWM capable + name: d6, + } + PB03 { + /// Neopixel Pin + name: neopixel, + } + PA19 { + /// Pin 9, PWM capable. Also analog input (A7) + name: d9, + } + PA20 { + /// Pin 10, PWM capable + name: d10, + } + PA21 { + /// Pin 11, PWM capable + name: d11, + } + PA22 { + /// Pin 12, PWM capable + name: d12, + } + PA23 { + /// Pin 13, which is also attached to the red LED. PWM capable. + name: d13, + aliases: { + PushPullOutput: RedLed, + AlternateE: RedLedPwm + } + } + PA12 { + /// The I2C data line + name: sda, + aliases: { + AlternateC: Sda + } + } + PA13 { + /// The I2C clock line + name: scl, + aliases: { + AlternateC: Scl + } + } + PA17 { + /// The SPI SCK + name: sck, + aliases: { + AlternateC: Sclk + } + } + PB23 { + /// The SPI MOSI + name: mosi, + aliases: { + AlternateC: Mosi + } + } + PB22 { + /// The SPI MISO + name: miso, + aliases: { + AlternateC: Miso + } + } + PA24 { + /// The USB D- pad + name: usb_dm, + aliases: { + AlternateH: UsbDm + } + } + PA25 { + /// The USB D+ pad + name: usb_dp, + aliases: { + AlternateH: UsbDp + } + } +); + +/// SPI pads for the labelled SPI peripheral +/// +/// You can use these pads with other, user-defined [`spi::Config`]urations. +pub type SpiPads = spi::Pads; + +/// SPI master for the labelled SPI peripheral +/// +/// This type implements [`FullDuplex`](ehal::spi::FullDuplex). +pub type Spi = spi::Spi, spi::Duplex>; + +/// Convenience for setting up the labelled SPI peripheral. +/// This powers up SERCOM1 and configures it for use as an +/// SPI Master in SPI Mode 0. +pub fn spi_master( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: SpiSercom, + mclk: &mut pac::MCLK, + sclk: impl Into, + mosi: impl Into, + miso: impl Into, +) -> Spi { + let gclk0 = clocks.gclk0(); + let clock = clocks.sercom1_core(&gclk0).unwrap(); + let freq = clock.freq(); + let (miso, mosi, sclk) = (miso.into(), mosi.into(), sclk.into()); + let pads = spi::Pads::default().data_in(miso).data_out(mosi).sclk(sclk); + spi::Config::new(mclk, sercom, pads, freq) + .baud(baud) + .spi_mode(spi::MODE_0) + .enable() +} + +/// I2C pads for the labelled I2C peripheral +/// +/// You can use these pads with other, user-defined [`i2c::Config`]urations. +pub type I2cPads = i2c::Pads; + +/// I2C master for the labelled I2C peripheral +/// +/// This type implements [`Read`](ehal::blocking::i2c::Read), +/// [`Write`](ehal::blocking::i2c::Write) and +/// [`WriteRead`](ehal::blocking::i2c::WriteRead). +pub type I2c = i2c::I2c>; + +/// Convenience for setting up the labelled SDA, SCL pins to +/// operate as an I2C master running at the specified frequency. +pub fn i2c_master( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: I2cSercom, + mclk: &mut pac::MCLK, + sda: impl Into, + scl: impl Into, +) -> I2c { + let gclk0 = clocks.gclk0(); + let clock = &clocks.sercom2_core(&gclk0).unwrap(); + let freq = clock.freq(); + let baud = baud.into(); + let pads = i2c::Pads::new(sda.into(), scl.into()); + i2c::Config::new(mclk, sercom, pads, freq) + .baud(baud) + .enable() +} + +/// UART pads for the labelled RX & TX pins +pub type UartPads = uart::Pads; + +/// UART device for the labelled RX & TX pins +pub type Uart = uart::Uart, uart::Duplex>; + +/// Convenience for setting up the labelled RX, TX pins to +/// operate as a UART device running at the specified baud. +pub fn uart( + clocks: &mut GenericClockController, + baud: impl Into, + sercom: UartSercom, + mclk: &mut pac::MCLK, + rx: impl Into, + tx: impl Into, +) -> Uart { + let gclk0 = clocks.gclk0(); + + let clock = &clocks.sercom5_core(&gclk0).unwrap(); + let baud = baud.into(); + let pads = uart::Pads::default().rx(rx.into()).tx(tx.into()); + uart::Config::new(mclk, sercom, pads, clock.freq()) + .baud(baud, BaudMode::Fractional(Oversampling::Bits16)) + .enable() +} + +#[cfg(feature = "usb")] +/// Convenience function for setting up USB +pub fn usb_allocator( + dm: impl Into, + dp: impl Into, + usb: pac::USB, + clocks: &mut GenericClockController, + mclk: &mut pac::MCLK, +) -> UsbBusAllocator { + use pac::gclk::{genctrl::SRC_A, pchctrl::GEN_A}; + + clocks.configure_gclk_divider_and_source(GEN_A::GCLK2, 1, SRC_A::DFLL, false); + let usb_gclk = clocks.get_gclk(GEN_A::GCLK2).unwrap(); + let usb_clock = &clocks.usb(&usb_gclk).unwrap(); + let (dm, dp) = (dm.into(), dp.into()); + UsbBusAllocator::new(UsbBus::new(usb_clock, mclk, dm, dp, usb)) +} diff --git a/crates.json b/crates.json index 3bb88ce25d65..bec417213701 100644 --- a/crates.json +++ b/crates.json @@ -45,6 +45,11 @@ "build": "cargo build --examples --all-features", "target": "thumbv7em-none-eabihf" }, + "feather_m4_can": { + "tier": 1, + "build": "cargo build --examples --all-features", + "target": "thumbv7em-none-eabihf" + }, "gemma_m0": { "tier": 2, "build": "cargo build --examples --all-features",