use cortexm7::support::atomic;
use enum_primitive::cast::FromPrimitive;
use enum_primitive::enum_from_primitive;
use kernel::hil;
use kernel::platform::chip::ClockInterface;
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{ReadOnly, ReadWrite, WriteOnly};
use kernel::utilities::StaticRef;
use crate::ccm;
#[repr(C)]
struct GpioRegisters {
dr: ReadWrite<u32>,
gdir: ReadWrite<u32>,
psr: ReadOnly<u32>,
icr1: ReadWrite<u32>,
icr2: ReadWrite<u32>,
imr: ReadWrite<u32>,
isr: ReadWrite<u32>,
edge_sel: ReadWrite<u32>,
_reserved1: [u8; 100],
dr_set: WriteOnly<u32>,
dr_clear: WriteOnly<u32>,
dr_toggle: WriteOnly<u32>,
}
const GPIO1_BASE: StaticRef<GpioRegisters> =
unsafe { StaticRef::new(0x401B8000 as *const GpioRegisters) };
const GPIO2_BASE: StaticRef<GpioRegisters> =
unsafe { StaticRef::new(0x401BC000 as *const GpioRegisters) };
const GPIO3_BASE: StaticRef<GpioRegisters> =
unsafe { StaticRef::new(0x401C0000 as *const GpioRegisters) };
const GPIO4_BASE: StaticRef<GpioRegisters> =
unsafe { StaticRef::new(0x401C4000 as *const GpioRegisters) };
const GPIO5_BASE: StaticRef<GpioRegisters> =
unsafe { StaticRef::new(0x400C0000 as *const GpioRegisters) };
enum_from_primitive! {
#[repr(u16)]
enum GpioPort {
GPIO1 = 0b000,
GPIO2 = 0b001,
GPIO3 = 0b010,
GPIO4 = 0b011,
GPIO5 = 0b100,
}
}
const fn gpio_id(port: GpioPort, offset: u16) -> u16 {
((port as u16) << 6) | offset & 0x3F
}
#[repr(u16)]
#[derive(Copy, Clone)]
pub enum PinId {
AdB0_00 = gpio_id(GpioPort::GPIO1, 0),
AdB0_01 = gpio_id(GpioPort::GPIO1, 1),
AdB0_02 = gpio_id(GpioPort::GPIO1, 2),
AdB0_03 = gpio_id(GpioPort::GPIO1, 3),
AdB0_04 = gpio_id(GpioPort::GPIO1, 4),
AdB0_05 = gpio_id(GpioPort::GPIO1, 5),
AdB0_06 = gpio_id(GpioPort::GPIO1, 6),
AdB0_07 = gpio_id(GpioPort::GPIO1, 7),
AdB0_08 = gpio_id(GpioPort::GPIO1, 8),
AdB0_09 = gpio_id(GpioPort::GPIO1, 9),
AdB0_10 = gpio_id(GpioPort::GPIO1, 10),
AdB0_11 = gpio_id(GpioPort::GPIO1, 11),
AdB0_12 = gpio_id(GpioPort::GPIO1, 12),
AdB0_13 = gpio_id(GpioPort::GPIO1, 13),
AdB0_14 = gpio_id(GpioPort::GPIO1, 14),
AdB0_15 = gpio_id(GpioPort::GPIO1, 15),
AdB1_00 = gpio_id(GpioPort::GPIO1, 16),
AdB1_01 = gpio_id(GpioPort::GPIO1, 17),
AdB1_02 = gpio_id(GpioPort::GPIO1, 18),
AdB1_03 = gpio_id(GpioPort::GPIO1, 19),
AdB1_04 = gpio_id(GpioPort::GPIO1, 20),
AdB1_05 = gpio_id(GpioPort::GPIO1, 21),
AdB1_06 = gpio_id(GpioPort::GPIO1, 22),
AdB1_07 = gpio_id(GpioPort::GPIO1, 23),
AdB1_08 = gpio_id(GpioPort::GPIO1, 24),
AdB1_09 = gpio_id(GpioPort::GPIO1, 25),
AdB1_10 = gpio_id(GpioPort::GPIO1, 26),
AdB1_11 = gpio_id(GpioPort::GPIO1, 27),
AdB1_12 = gpio_id(GpioPort::GPIO1, 28),
AdB1_13 = gpio_id(GpioPort::GPIO1, 29),
AdB1_14 = gpio_id(GpioPort::GPIO1, 30),
AdB1_15 = gpio_id(GpioPort::GPIO1, 31),
B0_00 = gpio_id(GpioPort::GPIO2, 0),
B0_01 = gpio_id(GpioPort::GPIO2, 1),
B0_02 = gpio_id(GpioPort::GPIO2, 2),
B0_03 = gpio_id(GpioPort::GPIO2, 3),
B0_04 = gpio_id(GpioPort::GPIO2, 4),
B0_05 = gpio_id(GpioPort::GPIO2, 5),
B0_06 = gpio_id(GpioPort::GPIO2, 6),
B0_07 = gpio_id(GpioPort::GPIO2, 7),
B0_08 = gpio_id(GpioPort::GPIO2, 8),
B0_09 = gpio_id(GpioPort::GPIO2, 9),
B0_10 = gpio_id(GpioPort::GPIO2, 10),
B0_11 = gpio_id(GpioPort::GPIO2, 11),
B0_12 = gpio_id(GpioPort::GPIO2, 12),
B0_13 = gpio_id(GpioPort::GPIO2, 13),
B0_14 = gpio_id(GpioPort::GPIO2, 14),
B0_15 = gpio_id(GpioPort::GPIO2, 15),
B1_00 = gpio_id(GpioPort::GPIO2, 16),
B1_01 = gpio_id(GpioPort::GPIO2, 17),
B1_02 = gpio_id(GpioPort::GPIO2, 18),
B1_03 = gpio_id(GpioPort::GPIO2, 19),
B1_04 = gpio_id(GpioPort::GPIO2, 20),
B1_05 = gpio_id(GpioPort::GPIO2, 21),
B1_06 = gpio_id(GpioPort::GPIO2, 22),
B1_07 = gpio_id(GpioPort::GPIO2, 23),
B1_08 = gpio_id(GpioPort::GPIO2, 24),
B1_09 = gpio_id(GpioPort::GPIO2, 25),
B1_10 = gpio_id(GpioPort::GPIO2, 26),
B1_11 = gpio_id(GpioPort::GPIO2, 27),
B1_12 = gpio_id(GpioPort::GPIO2, 28),
B1_13 = gpio_id(GpioPort::GPIO2, 29),
B1_14 = gpio_id(GpioPort::GPIO2, 30),
B1_15 = gpio_id(GpioPort::GPIO2, 31),
SdB1_00 = gpio_id(GpioPort::GPIO3, 0),
SdB1_01 = gpio_id(GpioPort::GPIO3, 1),
SdB1_02 = gpio_id(GpioPort::GPIO3, 2),
SdB1_03 = gpio_id(GpioPort::GPIO3, 3),
SdB1_04 = gpio_id(GpioPort::GPIO3, 4),
SdB1_05 = gpio_id(GpioPort::GPIO3, 5),
SdB1_06 = gpio_id(GpioPort::GPIO3, 6),
SdB1_07 = gpio_id(GpioPort::GPIO3, 7),
SdB1_08 = gpio_id(GpioPort::GPIO3, 8),
SdB1_09 = gpio_id(GpioPort::GPIO3, 9),
SdB1_10 = gpio_id(GpioPort::GPIO3, 10),
SdB1_11 = gpio_id(GpioPort::GPIO3, 11),
SdB0_00 = gpio_id(GpioPort::GPIO3, 12),
SdB0_01 = gpio_id(GpioPort::GPIO3, 13),
SdB0_02 = gpio_id(GpioPort::GPIO3, 14),
SdB0_03 = gpio_id(GpioPort::GPIO3, 15),
SdB0_04 = gpio_id(GpioPort::GPIO3, 16),
SdB0_05 = gpio_id(GpioPort::GPIO3, 17),
Emc32 = gpio_id(GpioPort::GPIO3, 18),
Emc33 = gpio_id(GpioPort::GPIO3, 19),
Emc34 = gpio_id(GpioPort::GPIO3, 20),
Emc35 = gpio_id(GpioPort::GPIO3, 21),
Emc36 = gpio_id(GpioPort::GPIO3, 22),
Emc37 = gpio_id(GpioPort::GPIO3, 23),
Emc38 = gpio_id(GpioPort::GPIO3, 24),
Emc39 = gpio_id(GpioPort::GPIO3, 25),
Emc40 = gpio_id(GpioPort::GPIO3, 26),
Emc41 = gpio_id(GpioPort::GPIO3, 27),
Emc00 = gpio_id(GpioPort::GPIO4, 0),
Emc01 = gpio_id(GpioPort::GPIO4, 1),
Emc02 = gpio_id(GpioPort::GPIO4, 2),
Emc03 = gpio_id(GpioPort::GPIO4, 3),
Emc04 = gpio_id(GpioPort::GPIO4, 4),
Emc05 = gpio_id(GpioPort::GPIO4, 5),
Emc06 = gpio_id(GpioPort::GPIO4, 6),
Emc07 = gpio_id(GpioPort::GPIO4, 7),
Emc08 = gpio_id(GpioPort::GPIO4, 8),
Emc09 = gpio_id(GpioPort::GPIO4, 9),
Emc10 = gpio_id(GpioPort::GPIO4, 10),
Emc11 = gpio_id(GpioPort::GPIO4, 11),
Emc12 = gpio_id(GpioPort::GPIO4, 12),
Emc13 = gpio_id(GpioPort::GPIO4, 13),
Emc14 = gpio_id(GpioPort::GPIO4, 14),
Emc15 = gpio_id(GpioPort::GPIO4, 15),
Emc16 = gpio_id(GpioPort::GPIO4, 16),
Emc17 = gpio_id(GpioPort::GPIO4, 17),
Emc18 = gpio_id(GpioPort::GPIO4, 18),
Emc19 = gpio_id(GpioPort::GPIO4, 19),
Emc20 = gpio_id(GpioPort::GPIO4, 20),
Emc21 = gpio_id(GpioPort::GPIO4, 21),
Emc22 = gpio_id(GpioPort::GPIO4, 22),
Emc23 = gpio_id(GpioPort::GPIO4, 23),
Emc24 = gpio_id(GpioPort::GPIO4, 24),
Emc25 = gpio_id(GpioPort::GPIO4, 25),
Emc26 = gpio_id(GpioPort::GPIO4, 26),
Emc27 = gpio_id(GpioPort::GPIO4, 27),
Emc28 = gpio_id(GpioPort::GPIO4, 28),
Emc29 = gpio_id(GpioPort::GPIO4, 29),
Emc30 = gpio_id(GpioPort::GPIO4, 30),
Emc31 = gpio_id(GpioPort::GPIO4, 31),
Wakeup = gpio_id(GpioPort::GPIO5, 0),
PmicOnReq = gpio_id(GpioPort::GPIO5, 1),
PmicStbyReq = gpio_id(GpioPort::GPIO5, 2),
}
impl PinId {
fn port(self) -> GpioPort {
GpioPort::from_u16((self as u16) >> 6).unwrap()
}
const fn offset(self) -> usize {
(self as usize) & 0x3F
}
}
pub enum Mode {
Input = 0b00,
Output = 0b01,
}
pub struct Port<'a, const N: usize> {
registers: StaticRef<GpioRegisters>,
clock: PortClock<'a>,
pins: [Pin<'a>; N],
}
impl<'a, const N: usize> Port<'a, N> {
const fn new(
registers: StaticRef<GpioRegisters>,
clock: PortClock<'a>,
pins: [Pin<'a>; N],
) -> Self {
Self {
registers,
clock,
pins,
}
}
pub fn is_enabled_clock(&self) -> bool {
self.clock.is_enabled()
}
pub fn enable_clock(&self) {
self.clock.enable();
}
pub fn disable_clock(&self) {
self.clock.disable();
}
pub const fn pin(&self, offset: usize) -> &Pin<'a> {
&self.pins[offset]
}
pub fn handle_interrupt(&self) {
let imr_val: u32 = self.registers.imr.get();
let isr_val = unsafe {
atomic(|| {
let isr_val = self.registers.isr.get();
self.registers.isr.set(isr_val);
isr_val
})
};
BitOffsets(isr_val)
.filter(|offset| imr_val & (1 << offset) != 0)
.filter_map(|offset| self.pins.get(offset as usize))
.for_each(|pin| {
pin.client.map(|client| client.fired());
});
}
}
type GPIO1<'a> = Port<'a, 32>;
type GPIO2<'a> = Port<'a, 32>;
type GPIO3<'a> = Port<'a, 28>;
type GPIO4<'a> = Port<'a, 32>;
type GPIO5<'a> = Port<'a, 3>;
impl<'a> Port<'a, 32> {
const fn new_32(registers: StaticRef<GpioRegisters>, clock: PortClock<'a>) -> Self {
Self::new(
registers,
clock,
[
Pin::new(registers, 00),
Pin::new(registers, 01),
Pin::new(registers, 02),
Pin::new(registers, 03),
Pin::new(registers, 04),
Pin::new(registers, 05),
Pin::new(registers, 06),
Pin::new(registers, 07),
Pin::new(registers, 08),
Pin::new(registers, 09),
Pin::new(registers, 10),
Pin::new(registers, 11),
Pin::new(registers, 12),
Pin::new(registers, 13),
Pin::new(registers, 14),
Pin::new(registers, 15),
Pin::new(registers, 16),
Pin::new(registers, 17),
Pin::new(registers, 18),
Pin::new(registers, 19),
Pin::new(registers, 20),
Pin::new(registers, 21),
Pin::new(registers, 22),
Pin::new(registers, 23),
Pin::new(registers, 24),
Pin::new(registers, 25),
Pin::new(registers, 26),
Pin::new(registers, 27),
Pin::new(registers, 28),
Pin::new(registers, 29),
Pin::new(registers, 30),
Pin::new(registers, 31),
],
)
}
const fn gpio1(ccm: &'a ccm::Ccm) -> GPIO1<'a> {
Self::new_32(
GPIO1_BASE,
PortClock(ccm::PeripheralClock::ccgr1(ccm, ccm::HCLK1::GPIO1)),
)
}
const fn gpio2(ccm: &'a ccm::Ccm) -> GPIO2<'a> {
Self::new_32(
GPIO2_BASE,
PortClock(ccm::PeripheralClock::ccgr0(ccm, ccm::HCLK0::GPIO2)),
)
}
const fn gpio4(ccm: &'a ccm::Ccm) -> GPIO4<'a> {
Self::new_32(
GPIO4_BASE,
PortClock(ccm::PeripheralClock::ccgr3(ccm, ccm::HCLK3::GPIO4)),
)
}
}
impl<'a> Port<'a, 28> {
const fn new_28(registers: StaticRef<GpioRegisters>, clock: PortClock<'a>) -> Self {
Self::new(
registers,
clock,
[
Pin::new(registers, 00),
Pin::new(registers, 01),
Pin::new(registers, 02),
Pin::new(registers, 03),
Pin::new(registers, 04),
Pin::new(registers, 05),
Pin::new(registers, 06),
Pin::new(registers, 07),
Pin::new(registers, 08),
Pin::new(registers, 09),
Pin::new(registers, 10),
Pin::new(registers, 11),
Pin::new(registers, 12),
Pin::new(registers, 13),
Pin::new(registers, 14),
Pin::new(registers, 15),
Pin::new(registers, 16),
Pin::new(registers, 17),
Pin::new(registers, 18),
Pin::new(registers, 19),
Pin::new(registers, 20),
Pin::new(registers, 21),
Pin::new(registers, 22),
Pin::new(registers, 23),
Pin::new(registers, 24),
Pin::new(registers, 25),
Pin::new(registers, 26),
Pin::new(registers, 27),
],
)
}
const fn gpio3(ccm: &'a ccm::Ccm) -> GPIO3<'a> {
Self::new_28(
GPIO3_BASE,
PortClock(ccm::PeripheralClock::ccgr2(ccm, ccm::HCLK2::GPIO3)),
)
}
}
impl<'a> Port<'a, 3> {
const fn new_3(registers: StaticRef<GpioRegisters>, clock: PortClock<'a>) -> Self {
Self::new(
registers,
clock,
[
Pin::new(registers, 00),
Pin::new(registers, 01),
Pin::new(registers, 02),
],
)
}
const fn gpio5(ccm: &'a ccm::Ccm) -> GPIO5<'a> {
Self::new_3(
GPIO5_BASE,
PortClock(ccm::PeripheralClock::ccgr1(ccm, ccm::HCLK1::GPIO5)),
)
}
}
#[non_exhaustive] pub struct Ports<'a> {
pub gpio1: GPIO1<'a>,
pub gpio2: GPIO2<'a>,
pub gpio3: GPIO3<'a>,
pub gpio4: GPIO4<'a>,
pub gpio5: GPIO5<'a>,
}
impl<'a> Ports<'a> {
pub const fn new(ccm: &'a ccm::Ccm) -> Self {
Self {
gpio1: GPIO1::gpio1(ccm),
gpio2: GPIO2::gpio2(ccm),
gpio3: GPIO3::gpio3(ccm),
gpio4: GPIO4::gpio4(ccm),
gpio5: GPIO5::gpio5(ccm),
}
}
pub fn pin(&self, pin: PinId) -> &Pin<'a> {
match pin.port() {
GpioPort::GPIO1 => &self.gpio1.pins[pin.offset()],
GpioPort::GPIO2 => &self.gpio2.pins[pin.offset()],
GpioPort::GPIO3 => &self.gpio3.pins[pin.offset()],
GpioPort::GPIO4 => &self.gpio4.pins[pin.offset()],
GpioPort::GPIO5 => &self.gpio5.pins[pin.offset()],
}
}
}
struct PortClock<'a>(ccm::PeripheralClock<'a>);
impl ClockInterface for PortClock<'_> {
fn is_enabled(&self) -> bool {
self.0.is_enabled()
}
fn enable(&self) {
self.0.enable();
}
fn disable(&self) {
self.0.disable();
}
}
pub struct Pin<'a> {
registers: StaticRef<GpioRegisters>,
offset: usize,
client: OptionalCell<&'a dyn hil::gpio::Client>,
}
trait U32Ext {
fn set_bit(self, offset: usize) -> Self;
fn clear_bit(self, offset: usize) -> Self;
fn is_bit_set(&self, offset: usize) -> bool;
}
impl U32Ext for u32 {
#[inline(always)]
fn set_bit(self, offset: usize) -> u32 {
self | (1 << offset)
}
#[inline(always)]
fn clear_bit(self, offset: usize) -> u32 {
self & !(1 << offset)
}
#[inline(always)]
fn is_bit_set(&self, offset: usize) -> bool {
(self & (1 << offset)) != 0
}
}
impl Pin<'_> {
pub fn from_pin_id(pin_id: PinId) -> Self {
Self::new(
match pin_id.port() {
GpioPort::GPIO1 => GPIO1_BASE,
GpioPort::GPIO2 => GPIO2_BASE,
GpioPort::GPIO3 => GPIO3_BASE,
GpioPort::GPIO4 => GPIO4_BASE,
GpioPort::GPIO5 => GPIO5_BASE,
},
pin_id.offset(),
)
}
const fn new(registers: StaticRef<GpioRegisters>, offset: usize) -> Self {
Pin {
registers,
offset,
client: OptionalCell::empty(),
}
}
fn get_mode(&self) -> Mode {
if self.registers.gdir.get().is_bit_set(self.offset) {
Mode::Output
} else {
Mode::Input
}
}
fn set_mode(&self, mode: Mode) {
let gdir = self.registers.gdir.get();
let gdir = match mode {
Mode::Input => gdir.clear_bit(self.offset),
Mode::Output => gdir.set_bit(self.offset),
};
self.registers.gdir.set(gdir);
}
fn set_output_high(&self) {
self.registers.dr_set.set(1 << self.offset);
}
fn set_output_low(&self) {
self.registers.dr_clear.set(1 << self.offset);
}
fn is_output_high(&self) -> bool {
self.registers.dr.get().is_bit_set(self.offset)
}
fn toggle_output(&self) -> bool {
self.registers.dr_toggle.set(1 << self.offset);
self.is_output_high()
}
fn read_input(&self) -> bool {
self.registers.psr.get().is_bit_set(self.offset)
}
fn mask_interrupt(&self) {
let imr = self.registers.imr.get();
let imr = imr.clear_bit(self.offset);
self.registers.imr.set(imr);
}
fn unmask_interrupt(&self) {
let imr = self.registers.imr.get();
let imr = imr.set_bit(self.offset);
self.registers.imr.set(imr);
}
fn clear_pending(&self) {
self.registers.isr.set(1 << self.offset); }
fn set_edge_sensitive(&self, sensitive: hil::gpio::InterruptEdge) {
use hil::gpio::InterruptEdge::{EitherEdge, FallingEdge, RisingEdge};
const RISING_EDGE_SENSITIVE: u32 = 0b10;
const FALLING_EDGE_SENSITIVE: u32 = 0b11;
let edge_sel = self.registers.edge_sel.get();
let icr_offset = (self.offset % 16) * 2;
let sensitive = match sensitive {
EitherEdge => {
let edge_sel = edge_sel.set_bit(self.offset);
self.registers.edge_sel.set(edge_sel);
return;
}
RisingEdge => RISING_EDGE_SENSITIVE << icr_offset,
FallingEdge => FALLING_EDGE_SENSITIVE << icr_offset,
};
let edge_sel = edge_sel.clear_bit(self.offset);
self.registers.edge_sel.set(edge_sel);
let icr_mask = 0b11 << icr_offset;
if self.offset < 16 {
let icr1 = self.registers.icr1.get();
let icr1 = (icr1 & !icr_mask) | sensitive;
self.registers.icr1.set(icr1);
} else {
let icr2 = self.registers.icr2.get();
let icr2 = (icr2 & !icr_mask) | sensitive;
self.registers.icr2.set(icr2);
}
}
}
impl hil::gpio::Configure for Pin<'_> {
fn make_output(&self) -> hil::gpio::Configuration {
self.set_mode(Mode::Output);
hil::gpio::Configuration::Output
}
fn make_input(&self) -> hil::gpio::Configuration {
self.set_mode(Mode::Input);
hil::gpio::Configuration::Input
}
fn deactivate_to_low_power(&self) {
}
fn disable_output(&self) -> hil::gpio::Configuration {
hil::gpio::Configuration::LowPower
}
fn disable_input(&self) -> hil::gpio::Configuration {
hil::gpio::Configuration::LowPower
}
fn set_floating_state(&self, _mode: hil::gpio::FloatingState) {}
fn floating_state(&self) -> hil::gpio::FloatingState {
hil::gpio::FloatingState::PullNone
}
fn configuration(&self) -> hil::gpio::Configuration {
match self.get_mode() {
Mode::Input => hil::gpio::Configuration::Input,
Mode::Output => hil::gpio::Configuration::Output,
}
}
}
impl hil::gpio::Output for Pin<'_> {
fn set(&self) {
self.set_output_high();
}
fn clear(&self) {
self.set_output_low();
}
fn toggle(&self) -> bool {
self.toggle_output()
}
}
impl hil::gpio::Input for Pin<'_> {
fn read(&self) -> bool {
self.read_input()
}
}
impl<'a> hil::gpio::Interrupt<'a> for Pin<'a> {
fn enable_interrupts(&self, mode: hil::gpio::InterruptEdge) {
unsafe {
atomic(|| {
self.mask_interrupt();
self.clear_pending();
self.set_edge_sensitive(mode);
self.unmask_interrupt();
});
}
}
fn disable_interrupts(&self) {
unsafe {
atomic(|| {
self.mask_interrupt();
self.clear_pending();
});
}
}
fn set_client(&self, client: &'a dyn hil::gpio::Client) {
self.client.set(client);
}
fn is_pending(&self) -> bool {
self.registers.isr.get().is_bit_set(self.offset)
}
}
struct BitOffsets(u32);
impl Iterator for BitOffsets {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.0 != 0 {
let offset = self.0.trailing_zeros();
self.0 &= self.0 - 1;
Some(offset)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let popcnt = self.0.count_ones() as usize;
(popcnt, Some(popcnt))
}
}
impl ExactSizeIterator for BitOffsets {}
#[cfg(test)]
mod tests {
use super::BitOffsets;
#[test]
fn bit_offsets() {
fn check(word: u32, expected: impl ExactSizeIterator<Item = u32>) {
let offsets = BitOffsets(word);
assert_eq!(offsets.len(), expected.len());
assert!(
offsets.eq(expected),
"Incorrect bit offsets for word {:#b}",
word
);
}
check(0, core::iter::empty());
check(u32::max_value(), 0..32);
check(u32::max_value() >> 1, 0..31);
check(u32::max_value() << 1, 1..32);
check(0x5555_5555, (0..32).step_by(2));
check(0xAAAA_AAAA, (0..32).skip(1).step_by(2));
}
}