use std::num::{NonZeroU32, NonZeroU64};
use crate::machine::armv7m;
use crate::machine::{Board, Crystal, Supply, Target};
use crate::sidecar::InterruptLine;
use crate::soc::Error;
use crate::soc::stm32f427::Stm32f427;
pub const NAME: &str = "pixracer";
pub const MCU: &str = "stm32f427";
const CLOCK_HZ: NonZeroU64 = NonZeroU64::new(168_000_000).expect("a running clock");
const REFCLK_DIVIDER: NonZeroU32 = NonZeroU32::new(8).expect("a divider divides");
const PRIORITY_BITS: u8 = 4;
const BOOT: &str = "flash";
const ICOUNT_SHIFT: u32 = 2;
#[derive(Debug)]
pub struct Pixracer {
pub mcu: Stm32f427,
pub hse: Crystal,
pub lse: Crystal,
pub rail: Supply,
}
impl Pixracer {
pub fn new() -> Result<Self, Error> {
let mut board = Self {
mcu: Stm32f427::new()?,
hse: Crystal(24_000_000),
lse: Crystal(32_768),
rail: Supply(3300),
};
board.fit();
Ok(board)
}
fn fit(&mut self) {
self.mcu.rcc.attach_crystals(self.hse.0, self.lse.0);
let rail = self.rail.0;
let (power, registers) = self.mcu.power();
power.set_supply(rail, registers);
}
}
impl Board for Pixracer {
fn name(&self) -> &'static str {
NAME
}
fn target_names(&self) -> Vec<&'static str> {
vec![MCU]
}
fn targets(&mut self) -> Vec<Target<'_>> {
let sidecar = armv7m::Core {
model: armv7m::Model::M4,
clock_hz: CLOCK_HZ,
interrupt_lines: NonZeroU32::new(u32::from(
crate::mcu::Mcu::cpu(&self.mcu).interrupt_lines,
))
.expect("an MCU with interrupts"),
priority_bits: PRIORITY_BITS,
refclk_divider: REFCLK_DIVIDER,
icount_shift: ICOUNT_SHIFT,
boot: BOOT,
}
.command();
vec![Target {
name: MCU,
mcu: &mut self.mcu,
sidecar,
interrupt_line: InterruptLine::new,
}]
}
}
pub fn boxed() -> Result<Box<dyn Board>, Error> {
Ok(Box::new(Pixracer::new()?))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mcu::Mcu;
#[test]
fn the_board_has_its_mcu_and_reaches_a_model_by_field() {
let mut board = Pixracer::new().unwrap();
assert_eq!(board.name(), NAME);
let (clocks, registers) = board.mcu.clocks();
assert_eq!(
clocks.sysclk(registers),
16_000_000,
"the part wakes on its own oscillator"
);
assert_eq!(board.hse, Crystal(24_000_000));
}
#[test]
fn a_board_can_hang_a_chip_on_one_of_its_interfaces() {
use crate::soc::stm32f427::peripherals::spi::Peer;
#[derive(Debug, Default)]
struct Counter(u16);
impl Peer for Counter {
fn transfer(&mut self, mosi: u16) -> u16 {
self.0 = mosi.wrapping_add(1);
self.0
}
}
let mut board = Pixracer::new().unwrap();
assert!(
!board.mcu.spi[0].attached(),
"a Pixracer fits no chip yet, so nothing is on its first interface"
);
board.mcu.spi[0].attach(Counter::default());
assert!(board.mcu.spi[0].attached());
}
#[test]
fn the_machine_routes_every_cpu_line_and_no_more() {
let mut board = Pixracer::new().unwrap();
let lines = board.mcu.cpu().interrupt_lines;
let targets = board.targets();
let [target] = &targets[..] else {
panic!("one MCU on the board");
};
assert!((target.interrupt_line)(lines - 1).is_some());
assert_eq!(target.sidecar.binary, armv7m::BINARY);
}
#[test]
fn the_machine_takes_its_interrupt_count_from_the_part() {
let mut board = Pixracer::new().unwrap();
let lines = board.mcu.cpu().interrupt_lines;
let targets = board.targets();
let declared = targets[0]
.sidecar
.properties
.iter()
.find(|property| property.name == "num-irq")
.expect("the machine is told its interrupt count");
assert_eq!(
declared.value,
lines.to_string(),
"the count the engine is given is the count the MCU has"
);
}
}