use std::num::{NonZeroU32, NonZeroU64};
use crate::soc::{
Builder, Bus, Clock, Cpu, EraseBlocks, Gate, Layout, Peripheral, Region, WriteModify,
};
use super::generated;
use super::peripherals;
pub static MEMORY: [Region; 11] = [
Region::ram("itcm", 0x0000_0000, 64 << 10),
Region::flash("flash", 0x0800_0000, 2 << 20)
.erased_in(&SECTORS)
.erased_to(0xFF)
.programmed_by("Flash"),
Region::system("system1", 0x1FF0_0000, 128 << 10),
Region::system("system2", 0x1FF4_0000, 128 << 10),
Region::ram("dtcm", 0x2000_0000, 128 << 10),
Region::ram("axi_sram", 0x2400_0000, 512 << 10),
Region::ram("sram1", 0x3000_0000, 128 << 10),
Region::ram("sram2", 0x3002_0000, 128 << 10),
Region::ram("sram3", 0x3004_0000, 32 << 10),
Region::ram("sram4", 0x3800_0000, 64 << 10),
Region::ram("backup_sram", 0x3880_0000, 4 << 10),
];
pub(crate) static SECTORS: [EraseBlocks; 1] = [EraseBlocks {
size: NonZeroU64::new(128 << 10).expect("a sector has bytes"),
count: NonZeroU32::new(16).expect("two banks of eight"),
}];
pub static CPU: Cpu = generated::CPU;
const CONFIGURED: [&str; 3] = ["MODER", "OSPEEDR", "PUPDR"];
const PORT_RESETS: [(&str, [u64; CONFIGURED.len()]); 11] = [
("GPIOA", [0xABFF_FFFF, 0x0C00_0000, 0x6400_0000]),
("GPIOB", [0xFFFF_FEBF, 0x0000_00C0, 0x0000_0100]),
("GPIOC", [0xFFFF_FFFF, 0, 0]),
("GPIOD", [0xFFFF_FFFF, 0, 0]),
("GPIOE", [0xFFFF_FFFF, 0, 0]),
("GPIOF", [0xFFFF_FFFF, 0, 0]),
("GPIOG", [0xFFFF_FFFF, 0, 0]),
("GPIOH", [0xFFFF_FFFF, 0, 0]),
("GPIOI", [0xFFFF_FFFF, 0, 0]),
("GPIOJ", [0xFFFF_FFFF, 0, 0]),
("GPIOK", [0xFFFF_FFFF, 0, 0]),
];
const CLOCK_RESETS: [(&str, &str, u64); 11] = [
("RSR", "C1_RSR", 0x00FE_0000),
("APB4ENR", "C1_APB4ENR", 0x0001_0000),
("AHB3LPENR", "C1_AHB3LPENR", 0xF001_5131),
("AHB1LPENR", "C1_AHB1LPENR", 0x1E03_C023),
("AHB2LPENR", "C1_AHB2LPENR", 0xE000_0271),
("AHB4LPENR", "C1_AHB4LPENR", 0x3128_07FF),
("APB3LPENR", "C1_APB3LPENR", 0x0000_0058),
("APB1LLPENR", "C1_APB1LLPENR", 0xE8FF_CBFF),
("APB1HLPENR", "C1_APB1HLPENR", 0x0000_0136),
("APB2LPENR", "C1_APB2LPENR", 0x31D7_3033),
("APB4LPENR", "C1_APB4LPENR", 0x0421_DEAA),
];
const RESETS: [(&str, &str, u64); 10] = [
("RCC", "CR", 0x0000_0001),
("PWR", "CR3", 0x0000_0046),
("Flash", "CR1", 0x0000_0031),
("Flash", "OPTCR", 0x0000_0001),
("Flash", "OPTCR_", 0x0000_0001),
("Flash", "CRCCR1", 0x001C_0000),
("Flash", "CRCCR2", 0x001C_0000),
("DBGMCU", "IDC", 0x2003_6450),
(
peripherals::otg::INSTANCES[0],
"OTG_HS_GRSTCTL",
peripherals::otg::GRSTCTL_RESET,
),
(
peripherals::otg::INSTANCES[1],
"OTG_HS_GRSTCTL",
peripherals::otg::GRSTCTL_RESET,
),
];
const INTERRUPTS: [(&str, &[u16]); 27] = [
("TIM12", &[43]),
("TIM13", &[44]),
("TIM14", &[45]),
("ADC2", &[18]),
("DMA1", &[11, 12, 13, 14, 15, 16, 17, 47]),
("DMA2", &[56, 57, 58, 59, 60, 68, 69, 70]),
("EXTI", &[1, 6, 7, 8, 9, 10, 23, 40]),
("HRTIM_Master", &[103]),
("HRTIM_TIMA", &[104]),
("HRTIM_TIMB", &[105]),
("HRTIM_TIMC", &[106]),
("HRTIM_TIMD", &[107]),
("HRTIM_TIME", &[108]),
("HRTIM_Common", &[109]),
("OTG1_HS_HOST", &[]),
("OTG2_HS_HOST", &[]),
("OTG2_HS_GLOBAL", &[98, 99, 100, 101]),
("SDMMC1", &[49]),
("SDMMC2", &[124]),
("DELAY_Block_SDMMC1", &[]),
("DELAY_Block_SDMMC2", &[]),
("DELAY_Block_QUADSPI", &[]),
("PWR", &[149]),
("DAC", &[54]),
("RAMECC1", &[145]),
("RAMECC2", &[145]),
("RAMECC3", &[145]),
];
fn instances() -> impl Iterator<Item = Peripheral> {
generated::PERIPHERALS
.iter()
.map(|&peripheral| {
INTERRUPTS
.iter()
.find(|(name, _)| *name == peripheral.name)
.map_or(peripheral, |&(_, interrupts)| Peripheral {
interrupts,
..peripheral
})
})
.chain(std::iter::once(crate::cpu::armv7m::PERIPHERAL))
}
fn flash_corrections(mut builder: Builder) -> Builder {
for register in peripherals::flash::OPTION_REGISTERS {
let value = peripherals::flash::OptionBytes::FACTORY.word(register);
builder = builder.reset("Flash", register.spec().name, value);
}
builder = builder
.field("Flash", "CR1", "LOCK1", peripherals::flash::LOCK1)
.field("Flash", "CR2", "LOCK2", peripherals::flash::LOCK2);
for register in ["OPTCR", "OPTCR_"] {
builder = builder
.field("Flash", register, "OPTLOCK", peripherals::flash::OPTLOCK)
.field(
"Flash",
register,
"SWAP_BANK",
peripherals::flash::SWAP_BANK,
);
}
for register in peripherals::flash::READ_ONLY {
builder = builder.write_modify("Flash", register.spec().name, WriteModify::Modify);
}
builder
}
fn syscfg_exti_rng_corrections(mut builder: Builder) -> Builder {
builder = builder
.reset("EXTI", "CPUIMR2", 0xFFF5_FFFF)
.reset("EXTI", "CPUIMR3", 0x018B_FFFF);
for register in peripherals::exti::PENDING {
builder = builder.write_modify("EXTI", register.spec().name, WriteModify::OneToClear);
}
builder
.field("SYSCFG", "PWRCR", "ODEN", peripherals::syscfg::ODEN)
.field("SYSCFG", "CCCSR", "READY", peripherals::syscfg::READY)
.field("RNG", "SR", "SEIS", peripherals::rng::SEIS)
.field("RNG", "SR", "CEIS", peripherals::rng::CEIS)
}
fn timer_corrections(mut builder: Builder) -> Builder {
let every_timer = peripherals::tim::TIMERS
.iter()
.chain(&peripherals::tim::ADVANCED_TIMERS)
.chain(&peripherals::tim::BASIC_TIMERS)
.chain([
&peripherals::tim::TIMER_15,
&peripherals::tim::TIMER_16,
&peripherals::tim::TIMER_17,
]);
for timer in every_timer {
builder = builder
.write_modify(timer.instance, "SR", WriteModify::ZeroToClear)
.reset(timer.instance, "ARR", timer.counter.mask());
}
for timer in &peripherals::tim::ADVANCED_TIMERS {
builder = builder
.reset(timer.instance, "AF1", peripherals::tim::BREAK_INPUT_ENABLED)
.reset(timer.instance, "AF2", peripherals::tim::BREAK_INPUT_ENABLED);
}
builder
.reset("TIM15", "AF1", peripherals::tim::BREAK_INPUT_ENABLED)
.reset("TIM16", "TIM16_AF1", peripherals::tim::BREAK_INPUT_ENABLED)
.reset("TIM17", "TIM17_AF1", peripherals::tim::BREAK_INPUT_ENABLED)
}
fn rcc_corrections(mut builder: Builder) -> Builder {
for (register, field) in peripherals::rcc::field_corrections() {
builder = builder.field("RCC", register, field.name, field);
}
builder = builder.write_modify("RCC", "CIFR", WriteModify::Modify);
for (instance, register, field) in peripherals::rcc::CLOCK_GATES {
builder = builder.gate(instance, Gate::by("RCC", register, field));
}
for (instance, register, field) in peripherals::rcc::RESET_LINES {
builder = builder.reset_by(instance, Gate::by("RCC", register, field));
}
for (instance, output) in peripherals::rcc::UNGATED {
builder = builder.clocked_by(instance, Clock::output("RCC", output));
}
for (nth, (instance, _)) in peripherals::rcc::GATED_CLOCKS.iter().enumerate() {
builder = builder.clocked_by(
instance,
Clock::output("RCC", peripherals::rcc::Rcc::gated(nth)),
);
}
builder.reads_requests("RCC")
}
fn pwr_corrections(mut builder: Builder) -> Builder {
builder = builder.reset("PWR", "D3CR", peripherals::pwr::D3CR_RESET);
for field in &peripherals::pwr::WKUP_FLAGS {
builder = builder.field("PWR", "WKUPFR", field.name, *field);
}
builder
.gate_writes("RCC", &["BDCR"], Gate::by("PWR", "CR1", "DBP"))
.gate_writes("RTC", &[], Gate::by("PWR", "CR1", "DBP"))
}
fn spi_corrections(mut builder: Builder) -> Builder {
for instance in peripherals::spi::INSTANCES {
for (register, corrected) in peripherals::spi::FIELD_CORRECTIONS {
builder = builder.field(
instance.name,
register.spec().name,
corrected.name,
*corrected,
);
}
for register in peripherals::spi::READ_ONLY {
builder =
builder.write_modify(instance.name, register.spec().name, WriteModify::Modify);
}
}
builder
}
fn i2c_corrections(mut builder: Builder) -> Builder {
for instance in &peripherals::i2c::INSTANCES {
builder = builder
.field(instance.name, "CR2", "START", peripherals::i2c::START)
.field(instance.name, "CR2", "STOP", peripherals::i2c::STOP)
.field(instance.name, "CR2", "NACK", peripherals::i2c::NACK)
.field(instance.name, "CR2", "PECBYTE", peripherals::i2c::PECBYTE)
.field(instance.name, "ISR", "TXE", peripherals::i2c::TXE)
.field(instance.name, "ISR", "TXIS", peripherals::i2c::TXIS);
}
builder
}
fn dma_corrections(mut builder: Builder) -> Builder {
for reader in ["DMAMUX1", "DMAMUX2", "DMA1", "DMA2", "BDMA"] {
builder = builder.reads_requests(reader);
}
let every_timer = peripherals::tim::TIMERS
.iter()
.chain(&peripherals::tim::ADVANCED_TIMERS)
.chain(&peripherals::tim::BASIC_TIMERS)
.chain([
&peripherals::tim::TIMER_15,
&peripherals::tim::TIMER_16,
&peripherals::tim::TIMER_17,
]);
for timer in every_timer {
builder = builder.reads_requests(timer.instance);
}
builder
}
fn adc_corrections(mut builder: Builder) -> Builder {
for converter in peripherals::adc::Converter::ALL {
let instance = converter.instance();
for (register, value) in peripherals::adc::RESETS {
builder = builder.reset(instance, register, value);
}
for command in &peripherals::adc::CR_COMMANDS {
builder = builder.field(instance, "CR", command.name, *command);
}
builder = builder
.field(instance, "CR", "BOOST", peripherals::adc::BOOST)
.declare(instance, "ISR", &peripherals::adc::LDORDY)
.declare(instance, "SMPR1", &peripherals::adc::SMP0);
}
for (converter, block) in [
("ADC1", peripherals::adc::Block::Adc12),
("ADC2", peripherals::adc::Block::Adc12),
("ADC3", peripherals::adc::Block::Adc3),
] {
builder = builder.clocked_by(
converter,
Clock::output(block.instance(), peripherals::adc::ANALOG_CLOCK),
);
}
builder = builder.reads_requests(peripherals::adc::Converter::Adc3.instance());
for block in peripherals::adc::Block::ALL {
builder = builder.reads_requests(block.instance());
}
builder
}
const OTG_DEVICE_BLOCK: u64 = 0x420;
pub fn builder() -> Builder {
let mut builder = Bus::builder(CPU, &MEMORY, instances())
.undefined_reads_zero()
.grow_block("OTG1_HS_DEVICE", OTG_DEVICE_BLOCK)
.grow_block("OTG2_HS_DEVICE", OTG_DEVICE_BLOCK);
for (instance, register, value) in RESETS {
builder = builder.reset(instance, register, value);
}
for (register, copy, value) in CLOCK_RESETS {
builder = builder
.reset("RCC", register, value)
.reset("RCC", copy, value);
}
for (port, values) in PORT_RESETS {
for (register, value) in CONFIGURED.iter().zip(values) {
builder = builder.reset(port, register, value);
}
}
builder = timer_corrections(builder);
builder = rcc_corrections(builder);
builder = pwr_corrections(builder);
builder = dma_corrections(builder);
let builder = flash_corrections(builder);
let builder = spi_corrections(builder);
let builder = i2c_corrections(builder);
let builder = adc_corrections(builder);
let builder = syscfg_exti_rng_corrections(builder);
wiring(builder)
}
fn wiring(mut builder: Builder) -> Builder {
builder = builder.reads_requests("EXTI");
for instance in &peripherals::spi::INSTANCES {
builder = builder.reads_requests(instance.name);
}
builder
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_bus_carries_every_instance_and_the_memory_of_section_2_4() {
let soc = builder().build().unwrap();
assert_eq!(
soc.peripherals().count(),
generated::PERIPHERALS.len() + 1,
"every instance of the file, and the data watchpoint unit"
);
assert!(soc.peripheral(crate::cpu::armv7m::DWT).is_some());
assert_eq!(soc.cpu().interrupt_lines, 150);
let ram: u64 = MEMORY
.iter()
.filter(|region| region.name.contains("sram") && region.name != "backup_sram")
.map(|region| region.size)
.sum();
assert_eq!(ram, 864 << 10, "section 2.4's 864 Kbytes of system SRAM");
let sectors: u64 = SECTORS
.iter()
.map(|blocks| blocks.size.get() * u64::from(blocks.count.get()))
.sum();
assert_eq!(sectors, MEMORY[1].size, "the sectors cover the flash");
}
#[test]
fn the_flash_array_is_programmed_by_the_flash_interface_and_by_nothing_else() {
let programmed: Vec<&str> = MEMORY
.iter()
.filter_map(|region| region.programmer.map(|_| region.name))
.collect();
assert_eq!(programmed, ["flash"], "the user flash and nothing more");
assert_eq!(MEMORY[1].programmer, Some("Flash"));
let soc = builder().build().unwrap();
assert!(
soc.peripheral("Flash").is_some(),
"the instance the region names"
);
}
#[test]
fn every_corrected_instance_carries_table_147s_lines_and_no_two_share_one_the_table_does_not() {
const SHARED: [u16; 6] = [18, 43, 44, 45, 54, 145];
let soc = builder().build().unwrap();
for (name, lines) in INTERRUPTS {
let instance = soc
.peripheral(name)
.unwrap_or_else(|| panic!("{name} is an instance"));
assert_eq!(instance.interrupts, lines, "{name}");
}
let mut holders: std::collections::BTreeMap<u16, Vec<&str>> =
std::collections::BTreeMap::new();
for instance in soc.peripherals() {
for &line in instance.interrupts {
holders.entry(line).or_default().push(instance.name);
}
}
for (line, names) in holders {
assert!(
names.len() == 1 || SHARED.contains(&line),
"line {line} is held by {names:?}"
);
}
}
#[test]
fn every_port_comes_out_of_reset_as_the_manual_gives_it() {
use super::generated::gpioa::Register;
use crate::soc::Layout;
const NAMED_BY_THE_MANUAL: [(&str, [u64; 3]); 2] = [
("GPIOA", [0xABFF_FFFF, 0x0C00_0000, 0x6400_0000]),
("GPIOB", [0xFFFF_FEBF, 0x0000_00C0, 0x0000_0100]),
];
const OTHER_PORTS: [u64; 3] = [0xFFFF_FFFF, 0, 0];
let configured = [Register::Moder, Register::Ospeedr, Register::Pupdr];
let named: Vec<&str> = configured
.iter()
.map(|register| register.spec().name)
.collect();
assert_eq!(
named, CONFIGURED,
"the corrections name these three, in this order"
);
let soc = builder().build().unwrap();
for (port, _) in PORT_RESETS {
let values = NAMED_BY_THE_MANUAL
.iter()
.find(|(named, _)| *named == port)
.map_or(OTHER_PORTS, |(_, values)| *values);
let registers = soc
.registers(port)
.unwrap_or_else(|| panic!("{port} is an instance"));
for (register, expected) in configured.iter().zip(values) {
assert_eq!(
registers.get(*register),
expected,
"{port} {}",
register.spec().name
);
}
}
}
#[test]
fn the_clock_power_flash_and_debug_registers_come_out_of_reset_as_the_manual_gives_them() {
use super::generated::{dbgmcu, flash, pwr, rcc};
let soc = builder().build().unwrap();
let rcc_registers = soc.registers("RCC").unwrap();
assert_eq!(rcc_registers.get(rcc::Register::Cr), 0x0000_0001);
assert_eq!(rcc_registers.get(rcc::Register::Rsr), 0x00FE_0000);
assert_eq!(rcc_registers.get(rcc::Register::C1Rsr), 0x00FE_0000);
assert_eq!(rcc_registers.get(rcc::Register::Apb4enr), 0x0001_0000);
assert_eq!(rcc_registers.get(rcc::Register::Apb4lpenr), 0x0421_DEAA);
assert_eq!(rcc_registers.get(rcc::Register::C1Apb2lpenr), 0x31D7_3033);
assert_eq!(
soc.registers("PWR").unwrap().get(pwr::Register::Cr3),
0x0000_0046
);
let flash_registers = soc.registers("Flash").unwrap();
assert_eq!(flash_registers.get(flash::Register::Cr1), 0x0000_0031);
assert_eq!(flash_registers.get(flash::Register::Cr2), 0x0000_0031);
assert_eq!(flash_registers.get(flash::Register::Optcr), 0x0000_0001);
assert_eq!(flash_registers.get(flash::Register::Optcr_), 0x0000_0001);
assert_eq!(flash_registers.get(flash::Register::Crccr2), 0x001C_0000);
assert_eq!(
soc.registers("DBGMCU").unwrap().get(dbgmcu::Register::Idc),
0x2003_6450
);
}
}