use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use core::fmt;
use crate::bus::i2c::wires::{MasterEvent, MasterOp, MasterWires, MasterWiresState, pin as line};
use crate::bus::i2c::{
Ack, Address, BYTE_HALF_PERIODS, Direction, I2cBus, Link, START_HALF_PERIODS,
STOP_HALF_PERIODS, buses,
};
use crate::core::device::{Device, DeviceClass, PropertySpec, RealizeCtx, ResetKind, SinkPin};
use crate::core::error::{BusError, Error, Result};
use crate::core::props::{Props, ValueKind};
use crate::core::sched::{AccessKind, LazyHandle};
use crate::core::space::{AccessConstraints, MemAttrs, MemOps, MemResult, Region, RegionRef};
use crate::core::state::{ChunkReader, ChunkWriter, Sink, Source};
use crate::core::sync::{AtomicBool, AtomicU64, LockRank, Mutex, Ordering};
use crate::core::value::{Endian, Width};
use crate::core::wire::{Level, WireId, WireSource};
use crate::machine::realize::Instance;
#[cfg(all(test, feature = "dev-at24c"))]
mod tests;
const CLASS_NAME: &str = "st.i2c";
const STATE_VERSION: u32 = 1;
pub const REGISTER_BYTES: u64 = 0x28;
const CR1_PE: u32 = 1 << 0;
const CR1_START: u32 = 1 << 8;
const CR1_STOP: u32 = 1 << 9;
const CR1_ACK: u32 = 1 << 10;
const CR1_SWRST: u32 = 1 << 15;
const CR1_MASK: u32 = 0b1011_1111_1111_1011;
const CR2_FREQ: u32 = 0x3f;
const CR2_ITERREN: u32 = 1 << 8;
const CR2_ITEVTEN: u32 = 1 << 9;
const CR2_ITBUFEN: u32 = 1 << 10;
const CR2_MASK: u32 = CR2_FREQ | CR2_ITERREN | CR2_ITEVTEN | CR2_ITBUFEN | (1 << 11) | (1 << 12);
const OAR1_MASK: u32 = (1 << 15) | (1 << 14) | 0x3ff;
const OAR2_MASK: u32 = 0xff;
const SR1_SB: u32 = 1 << 0;
const SR1_ADDR: u32 = 1 << 1;
const SR1_BTF: u32 = 1 << 2;
const SR1_ADD10: u32 = 1 << 3;
const SR1_STOPF: u32 = 1 << 4;
const SR1_RXNE: u32 = 1 << 6;
const SR1_TXE: u32 = 1 << 7;
const SR1_BERR: u32 = 1 << 8;
const SR1_ARLO: u32 = 1 << 9;
const SR1_AF: u32 = 1 << 10;
const SR1_OVR: u32 = 1 << 11;
const SR1_ERRORS: u32 = SR1_BERR | SR1_ARLO | SR1_AF | SR1_OVR | (1 << 12) | (3 << 14);
const SR1_EVENTS: u32 = SR1_SB | SR1_ADDR | SR1_ADD10 | SR1_STOPF | SR1_BTF;
const SR2_MSL: u32 = 1 << 0;
const SR2_BUSY: u32 = 1 << 1;
const SR2_TRA: u32 = 1 << 2;
const CCR_VALUE: u32 = 0xfff;
const CCR_DUTY: u32 = 1 << 14;
const CCR_FS: u32 = 1 << 15;
const CCR_MASK: u32 = CCR_VALUE | CCR_DUTY | CCR_FS;
const TRISE_MASK: u32 = 0x3f;
const FLTR_MASK: u32 = 0x1f;
pub mod pin {
pub const EV: &str = "ev";
pub const ER: &str = "er";
}
const NO_EVENT: u64 = u64::MAX;
const STATE_RANK: LockRank = LockRank::new(0x4700);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
enum Stage {
#[default]
Idle,
Starting,
AddressWait,
Addr1,
Address2Wait,
Addr2,
AddrWait,
Tx,
Rx,
Stopping,
Held,
}
const fn stage_code(stage: Stage) -> u8 {
match stage {
Stage::Idle => 0,
Stage::Starting => 1,
Stage::AddressWait => 2,
Stage::Addr1 => 3,
Stage::Address2Wait => 4,
Stage::Addr2 => 5,
Stage::AddrWait => 6,
Stage::Tx => 7,
Stage::Rx => 8,
Stage::Stopping => 9,
Stage::Held => 10,
}
}
const fn stage_from_code(code: u8) -> Stage {
match code {
1 => Stage::Starting,
2 => Stage::AddressWait,
3 => Stage::Addr1,
4 => Stage::Address2Wait,
5 => Stage::Addr2,
6 => Stage::AddrWait,
7 => Stage::Tx,
8 => Stage::Rx,
9 => Stage::Stopping,
10 => Stage::Held,
_ => Stage::Idle,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Op {
Start,
Write(u8),
Read(Ack),
Stop,
}
impl Op {
const fn halves(self) -> u32 {
match self {
Op::Start => START_HALF_PERIODS,
Op::Write(_) | Op::Read(_) => BYTE_HALF_PERIODS,
Op::Stop => STOP_HALF_PERIODS,
}
}
const fn to_wire(self) -> MasterOp {
match self {
Op::Start => MasterOp::Start,
Op::Write(b) => MasterOp::Write(b),
Op::Read(a) => MasterOp::Read(a),
Op::Stop => MasterOp::Stop,
}
}
const fn code(self) -> (u8, u8) {
match self {
Op::Start => (1, 0),
Op::Write(b) => (2, b),
Op::Read(a) => (3, if a.is_ack() { 1 } else { 0 }),
Op::Stop => (4, 0),
}
}
const fn from_code(code: u8, operand: u8) -> Option<Op> {
match code {
1 => Some(Op::Start),
2 => Some(Op::Write(operand)),
3 => Some(Op::Read(if operand != 0 { Ack::Ack } else { Ack::Nack })),
4 => Some(Op::Stop),
_ => None,
}
}
}
#[derive(Debug)]
pub struct Stm32I2c {
shared: Arc<Shared>,
region: RegionRef,
}
struct Shared {
state: Mutex<State>,
link: Link,
bus: Option<Arc<I2cBus>>,
wires: Arc<MasterWires>,
ticks: AtomicU64,
next_event: AtomicU64,
ev: Mutex<Option<WireSource>>,
er: Mutex<Option<WireSource>>,
ev_level: AtomicBool,
er_level: AtomicBool,
lazy: Mutex<Option<LazyHandle>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct State {
ticks: u64,
cr1: u32,
cr2: u32,
oar1: u32,
oar2: u32,
ccr: u32,
trise: u32,
fltr: u32,
sr1: u32,
dr: u8,
tx_pending: bool,
msl: bool,
tra: bool,
stage: Stage,
dir: Direction,
ten: Option<u16>,
rx_done: bool,
sr1_read: bool,
op: Option<Op>,
halves_left: u32,
next_edge: u64,
high_half: bool,
}
impl Default for State {
fn default() -> State {
State {
ticks: 0,
cr1: 0,
cr2: 0,
oar1: 0,
oar2: 0,
ccr: 0,
trise: 2,
fltr: 0,
sr1: 0,
dr: 0,
tx_pending: false,
msl: false,
tra: false,
stage: Stage::Idle,
dir: Direction::Write,
ten: None,
rx_done: false,
sr1_read: false,
op: None,
halves_left: 0,
next_edge: 0,
high_half: false,
}
}
}
impl State {
const fn enabled(&self) -> bool {
self.cr1 & CR1_PE != 0
}
const fn scl(&self) -> (u64, u64) {
let ccr = (self.ccr & CCR_VALUE) as u64;
let ccr = if ccr == 0 { 1 } else { ccr };
if self.ccr & CCR_FS == 0 {
(ccr, ccr)
} else if self.ccr & CCR_DUTY == 0 {
(2 * ccr, ccr)
} else {
(16 * ccr, 9 * ccr)
}
}
const fn half_len(&self) -> u64 {
let (low, high) = self.scl();
if self.high_half { high } else { low }
}
const fn sr2(&self, busy: bool) -> u32 {
let mut v = 0;
if self.msl {
v |= SR2_MSL;
}
if busy {
v |= SR2_BUSY;
}
if self.tra {
v |= SR2_TRA;
}
v
}
const fn set(&mut self, bits: u32) {
self.sr1 |= bits;
}
const fn clear(&mut self, bits: u32) {
self.sr1 &= !bits;
}
const fn any(&self, bits: u32) -> bool {
self.sr1 & bits != 0
}
const fn stretching(&self) -> bool {
matches!(
self.stage,
Stage::AddressWait | Stage::Address2Wait | Stage::AddrWait
) || (matches!(self.stage, Stage::Tx | Stage::Rx) && self.any(SR1_BTF))
}
const fn ev(&self) -> bool {
if self.cr2 & CR2_ITEVTEN == 0 {
return false;
}
if self.any(SR1_EVENTS) {
return true;
}
self.cr2 & CR2_ITBUFEN != 0 && self.any(SR1_TXE | SR1_RXNE)
}
const fn er(&self) -> bool {
self.cr2 & CR2_ITERREN != 0 && self.any(SR1_ERRORS)
}
}
impl fmt::Debug for Shared {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Stm32I2cShared");
s.field("link", &self.link);
match self.state.try_lock() {
Some(state) => s.field("state", &*state).finish(),
None => s.field("state", &"<in use>").finish(),
}
}
}
impl Stm32I2c {
pub fn new(props: &Props) -> Result<Stm32I2c> {
let mut r = props.reader();
let link_name = alloc::string::ToString::to_string(r.require_str("link")?);
let bus_name = r.optional_str("bus")?.map(String::from);
r.finish()?;
let link = Link::from_name(&link_name).ok_or_else(|| Error::Config {
at: String::from(CLASS_NAME),
message: alloc::format!(
"`link` is `{link_name}`; it must be one of {:?} — see docs/buses/low-speed.md \
for which to pick",
Link::NAMES
),
})?;
if link == Link::Transactional && bus_name.is_none() {
return Err(Error::Config {
at: String::from(CLASS_NAME),
message: String::from(
"a `transactional` controller reaches its slaves through a named bus; give it \
`bus = \"i2c1\"` and name the same bus on each device",
),
});
}
let bus = bus_name
.as_deref()
.map(|name| buses::attach(props, name))
.transpose()?;
Ok(Stm32I2c::with_bus(link, bus))
}
#[must_use]
pub fn with_bus(link: Link, bus: Option<Arc<I2cBus>>) -> Stm32I2c {
let shared = Arc::new(Shared {
state: Mutex::with_rank(STATE_RANK, State::default()),
link,
bus,
wires: Arc::new(MasterWires::new()),
ticks: AtomicU64::new(0),
next_event: AtomicU64::new(NO_EVENT),
ev: Mutex::with_rank(LockRank::WIRE, None),
er: Mutex::with_rank(LockRank::WIRE, None),
ev_level: AtomicBool::new(false),
er_level: AtomicBool::new(false),
lazy: Mutex::with_rank(LockRank::WIRE, None),
});
let port = Arc::new(RegisterPort {
shared: Arc::clone(&shared),
});
let region = Arc::new(Region::io("i2c", REGISTER_BYTES, port as Arc<dyn MemOps>));
Stm32I2c { shared, region }
}
#[must_use]
pub fn link(&self) -> Link {
self.shared.link
}
#[must_use]
pub fn bus(&self) -> Option<&Arc<I2cBus>> {
self.shared.bus.as_ref()
}
#[must_use]
pub fn wires(&self) -> &Arc<MasterWires> {
&self.shared.wires
}
#[must_use]
pub fn ticks(&self) -> u64 {
self.shared.ticks.load(Ordering::Relaxed)
}
#[must_use]
pub fn sr1(&self) -> u32 {
self.shared.state.lock().sr1
}
#[must_use]
pub fn sr2(&self) -> u32 {
let busy = self.shared.bus_busy();
self.shared.state.lock().sr2(busy)
}
#[must_use]
pub fn stretching(&self) -> bool {
self.shared.state.lock().stretching()
}
#[must_use]
pub fn ev_level(&self) -> Level {
Level::from_bool(self.shared.ev_level.load(Ordering::Relaxed))
}
#[must_use]
pub fn er_level(&self) -> Level {
Level::from_bool(self.shared.er_level.load(Ordering::Relaxed))
}
pub fn advance_to(&self, target: u64) {
self.shared.advance_to(target);
}
}
impl Shared {
fn publish(&self, state: &State) {
self.ticks.store(state.ticks, Ordering::Relaxed);
self.next_event.store(
if state.op.is_some() {
state.next_edge.max(state.ticks.saturating_add(1))
} else {
NO_EVENT
},
Ordering::Relaxed,
);
}
fn bus_busy(&self) -> bool {
match self.link {
Link::Wired => self.wires.busy(),
Link::Transactional => self.bus.as_ref().is_some_and(|b| b.state().is_busy()),
}
}
fn sync(&self, attrs: MemAttrs) {
let handle = self.lazy.lock().clone();
let Some(handle) = handle else {
return;
};
let kind = if attrs.debug {
AccessKind::Debug
} else {
AccessKind::Guest
};
let _ = handle.sync(kind);
}
fn update_interrupts(&self) {
let (ev, er) = {
let state = self.state.lock();
(state.ev(), state.er())
};
self.ev_level.store(ev, Ordering::Relaxed);
self.er_level.store(er, Ordering::Relaxed);
let ev_port = self.ev.lock().clone();
let er_port = self.er.lock().clone();
if let Some(port) = ev_port {
port.set(Level::from_bool(ev));
}
if let Some(port) = er_port {
port.set(Level::from_bool(er));
}
}
fn decide(state: &mut State) -> Option<Op> {
if state.op.is_some() || !state.enabled() {
return None;
}
let start = state.cr1 & CR1_START != 0;
let stop = state.cr1 & CR1_STOP != 0;
match state.stage {
Stage::Idle => start.then_some(Op::Start),
Stage::Held => {
if stop {
Some(Op::Stop)
} else if start {
Some(Op::Start)
} else {
None
}
}
Stage::Addr1 | Stage::Addr2 if state.tx_pending => {
state.tx_pending = false;
Some(Op::Write(state.dr))
}
Stage::Tx => {
if state.tx_pending {
let byte = state.dr;
state.tx_pending = false;
state.set(SR1_TXE);
state.clear(SR1_BTF);
Some(Op::Write(byte))
} else if stop {
Some(Op::Stop)
} else if start {
Some(Op::Start)
} else {
None
}
}
Stage::Rx => {
if stop {
Some(Op::Stop)
} else if start {
Some(Op::Start)
} else if state.rx_done || state.any(SR1_BTF) {
None
} else {
let ack = if state.cr1 & CR1_ACK != 0 {
Ack::Ack
} else {
Ack::Nack
};
Some(Op::Read(ack))
}
}
_ => None,
}
}
fn pump(&self) {
let op = {
let mut state = self.state.lock();
let Some(op) = Shared::decide(&mut state) else {
self.publish(&state);
return;
};
state.op = Some(op);
state.halves_left = op.halves();
state.high_half = false;
state.next_edge = state.ticks.saturating_add(state.half_len());
state.stage = match op {
Op::Start => Stage::Starting,
Op::Stop => Stage::Stopping,
_ => state.stage,
};
self.publish(&state);
op
};
if self.link == Link::Wired {
self.wires.submit(op.to_wire());
}
}
fn half_step(&self) -> MasterEvent {
match self.link {
Link::Wired => self.wires.tick(),
Link::Transactional => self.half_step_transactional(),
}
}
fn half_step_transactional(&self) -> MasterEvent {
let Some(bus) = self.bus.as_ref() else {
let mut state = self.state.lock();
let op = state.op;
state.op = None;
return match op {
Some(Op::Start) => MasterEvent::Started,
Some(Op::Write(_)) => MasterEvent::Wrote(Ack::Nack),
Some(Op::Read(_)) => MasterEvent::Read(0xff),
Some(Op::Stop) => MasterEvent::Stopped,
None => MasterEvent::Idle,
};
};
if bus.stretching() {
return MasterEvent::Stretched;
}
let op = {
let mut state = self.state.lock();
let Some(op) = state.op else {
return MasterEvent::Idle;
};
state.halves_left = state.halves_left.saturating_sub(1);
if state.halves_left > 0 {
return MasterEvent::Working;
}
state.op = None;
op
};
match op {
Op::Start => MasterEvent::Started,
Op::Write(byte) => MasterEvent::Wrote(self.transactional_byte(byte)),
Op::Read(ack) => MasterEvent::Read(bus.read(ack)),
Op::Stop => {
bus.stop();
MasterEvent::Stopped
}
}
}
fn transactional_byte(&self, byte: u8) -> Ack {
let Some(bus) = self.bus.as_ref() else {
return Ack::Nack;
};
let (stage, ten) = {
let state = self.state.lock();
(state.stage, state.ten)
};
match stage {
Stage::Addr1 if Address::is_ten_bit_header(byte) => {
match Direction::from_bit(byte) {
Direction::Write => bus.ten_bit_header((byte >> 1) & 0b11),
Direction::Read => match ten {
Some(full) => bus.start(Address::Ten(full), Direction::Read),
None => Ack::Nack,
},
}
}
Stage::Addr1 => bus.start(Address::seven_from_byte(byte), Direction::from_bit(byte)),
Stage::Addr2 => {
let high = ten.map_or(0, |t| t >> 8);
let full = (high << 8) | u16::from(byte);
bus.start(Address::Ten(full), Direction::Write)
}
_ => bus.write(byte),
}
}
fn apply(&self, event: MasterEvent) {
let mut state = self.state.lock();
match event {
MasterEvent::Idle | MasterEvent::Working => {}
MasterEvent::Stretched => {
return;
}
MasterEvent::Started => {
state.op = None;
state.msl = true;
state.tra = false;
state.rx_done = false;
state.clear(SR1_TXE | SR1_BTF);
state.set(SR1_SB);
state.stage = Stage::AddressWait;
state.cr1 &= !CR1_START;
}
MasterEvent::Wrote(ack) => {
state.op = None;
self.on_wrote(&mut state, ack);
}
MasterEvent::Read(byte) => {
state.op = None;
self.on_read(&mut state, byte);
}
MasterEvent::Stopped => {
state.op = None;
state.msl = false;
state.tra = false;
state.rx_done = false;
state.ten = None;
state.tx_pending = false;
state.clear(SR1_TXE | SR1_BTF | SR1_SB | SR1_ADDR | SR1_ADD10);
state.cr1 &= !CR1_STOP;
state.stage = Stage::Idle;
}
MasterEvent::ArbitrationLost => {
state.op = None;
state.set(SR1_ARLO);
state.msl = false;
state.tra = false;
state.tx_pending = false;
state.clear(SR1_TXE | SR1_BTF | SR1_SB | SR1_ADDR | SR1_ADD10);
state.cr1 &= !(CR1_START | CR1_STOP);
state.stage = Stage::Idle;
}
}
state.high_half = !state.high_half;
self.publish(&state);
}
fn on_wrote(&self, state: &mut State, ack: Ack) {
if !ack.is_ack() {
state.set(SR1_AF);
state.stage = Stage::Held;
return;
}
match state.stage {
Stage::Addr1 => {
let byte = state.dr;
if Address::is_ten_bit_header(byte) && Direction::from_bit(byte) == Direction::Write
{
state.ten = Some(u16::from((byte >> 1) & 0b11) << 8);
state.set(SR1_ADD10);
state.stage = Stage::Address2Wait;
} else {
self.address_done(state);
}
}
Stage::Addr2 => {
let low = state.dr;
state.ten = state.ten.map(|t| (t & 0x300) | u16::from(low));
self.address_done(state);
}
Stage::Tx => {
state.set(SR1_TXE);
if !state.tx_pending {
state.set(SR1_BTF);
}
}
_ => {}
}
}
fn address_done(&self, state: &mut State) {
state.tra = state.dir == Direction::Write;
state.set(SR1_ADDR);
state.stage = Stage::AddrWait;
}
fn on_read(&self, state: &mut State, byte: u8) {
if state.any(SR1_RXNE) {
state.set(SR1_BTF);
}
state.dr = byte;
state.set(SR1_RXNE);
if state.cr1 & CR1_ACK == 0 {
state.rx_done = true;
}
}
fn advance_to(&self, target: u64) {
loop {
let step = {
let mut state = self.state.lock();
if state.op.is_none() {
state.ticks = state.ticks.max(target);
self.publish(&state);
false
} else if state.next_edge > target {
state.ticks = target.max(state.ticks);
self.publish(&state);
false
} else {
state.ticks = state.next_edge;
true
}
};
if !step {
return;
}
let event = self.half_step();
self.apply(event);
{
let mut state = self.state.lock();
state.next_edge = state.ticks.saturating_add(state.half_len());
self.publish(&state);
}
self.pump();
self.update_interrupts();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum After {
Nothing,
Pump,
Reset,
ReadAck(Ack),
}
struct RegisterPort {
shared: Arc<Shared>,
}
impl fmt::Debug for RegisterPort {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RegisterPort").finish_non_exhaustive()
}
}
impl RegisterPort {
fn read_register(&self, offset: u64, debug: bool, busy: bool) -> (u32, After) {
let mut state = self.shared.state.lock();
match offset {
0x00 => (state.cr1, After::Nothing),
0x04 => (state.cr2, After::Nothing),
0x08 => (state.oar1, After::Nothing),
0x0c => (state.oar2, After::Nothing),
0x10 => {
let value = u32::from(state.dr);
if debug {
return (value, After::Nothing);
}
state.clear(SR1_RXNE | SR1_BTF);
state.sr1_read = false;
(value, After::Pump)
}
0x14 => {
if !debug {
state.sr1_read = true;
}
(state.sr1, After::Nothing)
}
0x18 => {
let value = state.sr2(busy);
if debug || !state.sr1_read || !state.any(SR1_ADDR) {
return (value, After::Nothing);
}
state.clear(SR1_ADDR);
state.sr1_read = false;
if state.stage == Stage::AddrWait {
state.stage = if state.tra { Stage::Tx } else { Stage::Rx };
if state.tra {
state.set(SR1_TXE);
}
}
(value, After::Pump)
}
0x1c => (state.ccr, After::Nothing),
0x20 => (state.trise, After::Nothing),
0x24 => (state.fltr, After::Nothing),
_ => (0, After::Nothing),
}
}
fn write_register(&self, offset: u64, value: u32) -> After {
let mut state = self.shared.state.lock();
match offset {
0x00 => {
if value & CR1_SWRST != 0 {
let ticks = state.ticks;
*state = State {
ticks,
cr1: value & CR1_MASK,
..State::default()
};
return After::Reset;
}
let had_ack = state.cr1 & CR1_ACK != 0;
let arm = state.sr1_read;
state.cr1 = value & CR1_MASK;
if arm && state.any(SR1_STOPF) {
state.clear(SR1_STOPF);
state.sr1_read = false;
}
if !state.enabled() && state.op.is_none() {
state.sr1 = 0;
state.msl = false;
state.tra = false;
state.tx_pending = false;
state.stage = Stage::Idle;
}
let now_ack = state.cr1 & CR1_ACK != 0;
if had_ack != now_ack && matches!(state.op, Some(Op::Read(_))) {
let ack = if now_ack { Ack::Ack } else { Ack::Nack };
state.op = Some(Op::Read(ack));
return After::ReadAck(ack);
}
After::Pump
}
0x04 => {
state.cr2 = value & CR2_MASK;
After::Pump
}
0x08 => {
state.oar1 = value & OAR1_MASK;
After::Nothing
}
0x0c => {
state.oar2 = value & OAR2_MASK;
After::Nothing
}
0x10 => {
let byte = value as u8;
match state.stage {
Stage::AddressWait => {
if state.sr1_read {
state.clear(SR1_SB);
}
state.sr1_read = false;
state.dr = byte;
state.dir = Direction::from_bit(byte);
state.tx_pending = true;
state.stage = Stage::Addr1;
}
Stage::Address2Wait => {
if state.sr1_read {
state.clear(SR1_ADD10);
}
state.sr1_read = false;
state.dr = byte;
state.tx_pending = true;
state.stage = Stage::Addr2;
}
_ => {
state.dr = byte;
state.tx_pending = true;
state.clear(SR1_TXE | SR1_BTF);
state.sr1_read = false;
}
}
After::Pump
}
0x14 => {
state.sr1 &= value | !SR1_ERRORS;
After::Pump
}
0x18 => After::Nothing,
0x1c => {
state.ccr = value & CCR_MASK;
After::Nothing
}
0x20 => {
state.trise = value & TRISE_MASK;
After::Nothing
}
0x24 => {
state.fltr = value & FLTR_MASK;
After::Nothing
}
_ => After::Nothing,
}
}
fn finish(&self, after: After) {
match after {
After::Nothing => {}
After::Pump => {
self.shared.pump();
self.shared.update_interrupts();
}
After::Reset => {
self.shared.wires.reset();
self.shared.update_interrupts();
}
After::ReadAck(ack) => {
if self.shared.link == Link::Wired {
self.shared.wires.set_read_ack(ack);
}
self.shared.pump();
self.shared.update_interrupts();
}
}
}
}
impl MemOps for RegisterPort {
fn read(&self, offset: u64, dst: &mut [u8], attrs: MemAttrs) -> MemResult {
if !matches!(dst.len(), 2 | 4) || !offset.is_multiple_of(4) {
return Err(BusError::BadAccess);
}
self.shared.sync(attrs);
let busy = self.shared.bus_busy();
let (value, after) = self.read_register(offset, attrs.debug, busy);
match dst.len() {
2 => dst.copy_from_slice(&(value as u16).to_le_bytes()),
_ => dst.copy_from_slice(&value.to_le_bytes()),
}
self.finish(after);
Ok(())
}
fn write(&self, offset: u64, src: &[u8], attrs: MemAttrs) -> MemResult {
if !matches!(src.len(), 2 | 4) || !offset.is_multiple_of(4) {
return Err(BusError::BadAccess);
}
if attrs.debug {
return Err(BusError::BadAccess);
}
self.shared.sync(attrs);
let value = match src.len() {
2 => u32::from(u16::from_le_bytes([src[0], src[1]])),
_ => u32::from_le_bytes([src[0], src[1], src[2], src[3]]),
};
let after = self.write_register(offset, value);
self.finish(after);
Ok(())
}
fn constraints(&self) -> AccessConstraints {
AccessConstraints {
min: Width::U16,
max: Width::U32,
natural_alignment: true,
endian: Endian::Little,
allow_bulk: false,
..AccessConstraints::IO
}
}
}
impl Device for Stm32I2c {
fn class(&self) -> &'static DeviceClass {
&ST_I2C_CLASS
}
fn realize(&self, _ctx: &mut RealizeCtx<'_>) -> Result<()> {
Ok(())
}
fn reset(&self, _kind: ResetKind) {
{
let mut state = self.shared.state.lock();
let ticks = state.ticks;
*state = State {
ticks,
..State::default()
};
self.shared.publish(&state);
}
self.shared.wires.reset();
self.shared.update_interrupts();
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
let state = *self.shared.state.lock();
w.write_u64(state.ticks)?;
w.write_u32(state.cr1)?;
w.write_u32(state.cr2)?;
w.write_u32(state.oar1)?;
w.write_u32(state.oar2)?;
w.write_u32(state.ccr)?;
w.write_u32(state.trise)?;
w.write_u32(state.fltr)?;
w.write_u32(state.sr1)?;
w.write_u8(state.dr)?;
w.write_bool(state.tx_pending)?;
w.write_bool(state.msl)?;
w.write_bool(state.tra)?;
w.write_u8(stage_code(state.stage))?;
w.write_bool(state.dir == Direction::Read)?;
w.write_bool(state.ten.is_some())?;
w.write_u16(state.ten.unwrap_or(0))?;
w.write_bool(state.rx_done)?;
w.write_bool(state.sr1_read)?;
let (op, operand) = state.op.map_or((0, 0), Op::code);
w.write_u8(op)?;
w.write_u8(operand)?;
w.write_u32(state.halves_left)?;
w.write_u64(state.next_edge)?;
w.write_bool(state.high_half)?;
self.shared.wires.snapshot().write(w)
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
let state = State {
ticks: r.read_u64()?,
cr1: r.read_u32()?,
cr2: r.read_u32()?,
oar1: r.read_u32()?,
oar2: r.read_u32()?,
ccr: r.read_u32()?,
trise: r.read_u32()?,
fltr: r.read_u32()?,
sr1: r.read_u32()?,
dr: r.read_u8()?,
tx_pending: r.read_bool()?,
msl: r.read_bool()?,
tra: r.read_bool()?,
stage: stage_from_code(r.read_u8()?),
dir: if r.read_bool()? {
Direction::Read
} else {
Direction::Write
},
ten: {
let has = r.read_bool()?;
let value = r.read_u16()?;
has.then_some(value)
},
rx_done: r.read_bool()?,
sr1_read: r.read_bool()?,
op: {
let code = r.read_u8()?;
let operand = r.read_u8()?;
Op::from_code(code, operand)
},
halves_left: r.read_u32()?,
next_edge: r.read_u64()?,
high_half: r.read_bool()?,
};
let wires = MasterWiresState::read(r)?;
{
let mut slot = self.shared.state.lock();
*slot = state;
self.shared.publish(&slot);
}
self.shared.wires.restore(wires);
self.shared.update_interrupts();
Ok(())
}
fn region(&self, name: &str) -> Option<RegionRef> {
matches!(name, "" | "regs").then(|| Arc::clone(&self.region))
}
fn sink(&self, port: &str, sources: &[WireId]) -> Option<SinkPin> {
match port {
line::SCL_NAME => Some(SinkPin {
sink: self.shared.wires.sink(line::SCL, sources),
line: line::SCL,
}),
line::SDA_NAME => Some(SinkPin {
sink: self.shared.wires.sink(line::SDA, sources),
line: line::SDA,
}),
_ => None,
}
}
fn connect(&self, port: &str, source: WireSource) -> Result<()> {
match port {
line::SCL_NAME => self.shared.wires.connect(line::SCL, source),
line::SDA_NAME => self.shared.wires.connect(line::SDA, source),
pin::EV => *self.shared.ev.lock() = Some(source),
pin::ER => *self.shared.er.lock() = Some(source),
_ => {
return Err(Error::Config {
at: String::from(port),
message: alloc::format!(
"an STM32 I2C drives `{}` and `{}` — both open drain, and only ever low — \
plus the interrupt outputs `{}` and `{}`",
line::SCL_NAME,
line::SDA_NAME,
pin::EV,
pin::ER
),
});
}
}
Ok(())
}
fn announce(&self, _port: &str) {
self.shared.wires.announce();
self.shared.update_interrupts();
}
fn is_lazy(&self) -> bool {
true
}
fn current_tick(&self) -> u64 {
self.shared.ticks.load(Ordering::Relaxed)
}
fn advance_to(&self, tick: u64) {
Stm32I2c::advance_to(self, tick);
}
fn next_event_tick(&self) -> Option<u64> {
match self.shared.next_event.load(Ordering::Relaxed) {
NO_EVENT => None,
tick => Some(tick),
}
}
fn attach_lazy(&self, handle: LazyHandle) {
*self.shared.lazy.lock() = Some(handle);
}
}
impl Instance for Stm32I2c {}
pub static ST_I2C_CLASS: DeviceClass = DeviceClass {
name: CLASS_NAME,
version: STATE_VERSION,
summary: "STM32 I2C v1 (F1/F2/F4/L1): master mode, 7- and 10-bit addressing, \
CCR clocking, EV/ER interrupts, transactional or wired",
properties: &[
PropertySpec {
name: "link",
kind: ValueKind::Str,
required: true,
summary: "how bytes reach the slaves: `transactional` or `wired`",
},
PropertySpec {
name: "bus",
kind: ValueKind::Str,
required: false,
summary: "the named I2C bus this controller drives, for `transactional`",
},
],
construct: |props| Ok(Box::new(Stm32I2c::new(props)?)),
};
pub fn register(registry: &mut crate::core::Registry) -> Result<()> {
registry.add(&ST_I2C_CLASS)
}
pub fn bind(bindings: &mut crate::machine::Bindings) -> Result<()> {
bindings.bind(CLASS_NAME, |props| Ok(Arc::new(Stm32I2c::new(props)?)))
}
#[must_use]
pub fn schema() -> crate::machine::validate::ClassSchema {
use crate::machine::validate::{ClassSchema, PortDir, PropSchema};
ClassSchema::new(CLASS_NAME)
.prop(
PropSchema::new("link", ValueKind::Str)
.required()
.values(Link::NAMES),
)
.prop(PropSchema::new("bus", ValueKind::Str))
.port(line::SCL_NAME, PortDir::InOut)
.port(line::SDA_NAME, PortDir::InOut)
.port(pin::EV, PortDir::Out)
.port(pin::ER, PortDir::Out)
.region("")
.region("regs")
}