use std::num::{NonZeroU32, NonZeroU64};
use crate::chip::memory_card::MemoryCard;
use crate::chip::sd;
use crate::device::Level;
use crate::emulator::InterruptLine;
use crate::machine::armv7m;
use crate::machine::{Board, Crystal, Supply, Target};
use crate::snapshot::store;
use crate::soc::Error;
use crate::soc::stm32f427::peripherals::adc::Channel;
use crate::soc::stm32f427::peripherals::gpio::Pin;
use crate::soc::stm32f427::peripherals::i2c;
use crate::soc::stm32f427::peripherals::spi::{Pattern, Select};
use crate::soc::stm32f427::peripherals::{SERIAL_PORTS, usart};
use crate::soc::stm32f427::{SERIAL_INTERFACES, Stm32f427};
const SELECTED: usize = 2;
pub const NAME: &str = "stm32f427-bench";
pub const MCU: &str = "stm32f427";
pub const MISO_PATTERN: [u16; 8] = [0x5A, 0xA5, 0x3C, 0xC3, 0x1234, 0xF0, 0x6B, 0x96];
pub const ON_PD10: [u16; 1] = [0xD1];
pub const ON_PD7: [u16; 1] = [0x71];
pub const RX_PATTERN: [u16; 5] = [0x1E, 0xE1, 0x2D, 0xD2, 0x4B];
pub const I2C_PEER_ADDRESS: u8 = 0x50;
pub const I2C_PATTERN: [u8; 3] = [0x2D, 0x3E, 0x4F];
pub const I2C_TEN_BIT_ADDRESS: u16 = 0x145;
pub const I2C_TEN_BIT_PATTERN: [u8; 2] = [0x5C, 0x6D];
pub const ON_ADC: [(u8, u32); 3] = [(2, 1650), (10, 1650), (3, 825)];
const CARD_BLOCKS: usize = 64;
fn card_contents() -> Vec<u8> {
(0..CARD_BLOCKS)
.flat_map(|block| {
let mut bytes = vec![0xC5_u8; sd::BLOCK];
bytes[0] = u8::try_from(block).expect("sixty-four blocks");
bytes
})
.collect()
}
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 VARIANT: armv7m::Variant = armv7m::Variant::M4;
const FPU: armv7m::Fpu = armv7m::Fpu::FpV4Sp;
const MPU_REGIONS: armv7m::MpuRegions = armv7m::MpuRegions::Eight;
const BOOT: &str = "flash";
const INSTRUCTIONS_PER_SECOND: NonZeroU64 = NonZeroU64::new(250_000_000).expect("a running core");
#[derive(Debug)]
pub struct Stm32f427Bench {
pub mcu: Stm32f427,
pub hse: Crystal,
pub rail: Supply,
}
impl Stm32f427Bench {
pub fn new() -> Result<Self, Error> {
let mut bench = Self {
mcu: Stm32f427::new()?,
hse: Crystal(8_000_000),
rail: Supply(3300),
};
bench.fit();
Ok(bench)
}
fn fit(&mut self) {
self.mcu.rcc.attach_crystals(self.hse.0, Crystal::NONE.0);
let rail = self.rail.0;
self.mcu.set_supply(rail);
for nth in 0..SERIAL_INTERFACES.len() {
if nth == SELECTED {
continue;
}
self.mcu.spi[nth].attach(Pattern::answering(MISO_PATTERN));
}
for (words, pin) in [(ON_PD10, 10), (ON_PD7, 7)] {
self.mcu.spi[SELECTED].attach_selected(
Pattern::answering(words),
Select {
port: "GPIOD",
pin: Pin::new(pin).expect("a port has sixteen pins"),
active: Level::Low,
},
);
}
for nth in 0..SERIAL_PORTS.len() {
self.mcu.usart[nth].attach(usart::Pattern::answering(RX_PATTERN));
}
self.mcu.i2c[0].attach(i2c::Pattern::answering(I2C_PEER_ADDRESS, I2C_PATTERN));
self.mcu.i2c[0].attach(i2c::TenBit::answering(
I2C_TEN_BIT_ADDRESS,
I2C_TEN_BIT_PATTERN,
));
for converter in &mut self.mcu.adc {
converter.set_reference(rail);
converter.set_backup_battery(rail);
for (number, millivolts) in ON_ADC {
let channel = Channel::new(number).expect("a pin channel");
converter.set_input(channel, millivolts);
}
}
self.mcu.sdio.attach(MemoryCard::holding(&card_contents()));
}
}
store!(Stm32f427Bench {
values: [],
parts: [mcu],
built: [hse, rail],
});
impl Board for Stm32f427Bench {
fn name(&self) -> &'static str {
NAME
}
fn target_names(&self) -> Vec<&'static str> {
vec![MCU]
}
fn mcu(&mut self, target: usize) -> Option<&mut dyn crate::mcu::Mcu> {
(target == 0).then_some(&mut self.mcu)
}
fn targets(&mut self) -> Vec<Target<'_>> {
let core = armv7m::Core {
variant: VARIANT,
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,
instructions_per_second: INSTRUCTIONS_PER_SECOND,
boot: BOOT,
}
.spec_with_parts(
&crate::cpu::armv7m::windows(crate::mcu::Mcu::bus(&self.mcu)),
FPU,
MPU_REGIONS,
);
vec![Target {
name: MCU,
mcu: &mut self.mcu,
core,
interrupt_line: InterruptLine::new,
}]
}
}
pub fn boxed() -> Result<Box<dyn Board>, Error> {
Ok(Box::new(Stm32f427Bench::new()?))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_bench_fits_its_crystal_to_the_high_speed_pin() {
let bench = Stm32f427Bench::new().unwrap();
assert_eq!(bench.hse, Crystal(8_000_000));
}
#[test]
fn the_bench_hangs_a_peer_on_every_serial_interface_the_part_carries() {
let bench = Stm32f427Bench::new().unwrap();
assert_eq!(bench.mcu.spi.len(), SERIAL_INTERFACES.len());
for interface in &bench.mcu.spi {
assert!(
interface.attached(),
"a bus with nothing on the far end proves only that a flag moved"
);
}
}
}