// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The inter-integrated circuit interface of RM0433 rev 9 chapter 47, written against the `i2c1`
//! layout. One layout serves all four instances, I2C1 to I2C4, which carry the same eleven registers
//! at the same offsets. Each has two interrupt lines, the event line at position 0 and the error
//! line at position 1 of its table, RM0433 rev 9 Table 147. Every citation is RM0433 rev 9 unless it
//! names another document.
//!
//! **Controller mode only.** The interface becomes a controller on a start, 47.4.4, and nothing on a
//! board's side of this model is a second controller that could address the part, so it is always
//! the controller. The target-mode registers and flags are stored and inert, in the second table. A
//! chip hangs on the wires as a [`Peer`], the seam the barometer and the compasses attach through.
//! The controller calls it as the transfer reaches it: the start, with the address and direction,
//! at the end of the address byte, a byte written at the end of its ninth pulse, a byte read at the
//! end of its eighth, and the stop. A chip that does not answer an address is not acknowledged, and
//! the controller then does what it does for any address nobody answers.
//!
//! **What the model handles and what falls to the register file.** `CR1` is the model's for `PE`
//! and for the enables the interrupt and request lines follow. `CR2` is its whole controller side,
//! `TIMINGR` the period of everything, `TXDR` and `RXDR` the data, and `ISR` the flags hardware sets
//! and software's write of `TXE` and `TXIS`. `ICR` clears the flags it names. `OAR1`, `OAR2` and
//! `TIMEOUTR` are stored, `PECR` reads zero, and every read but `RXDR` is the register file's.
//!
//! **Time.** A byte is nine SCL pulses, eight data bits and the acknowledge bit, 47.4.4. One SCL
//! period is `tSCL = tSYNC1 + tSYNC2 + [(SCLH + 1) + (SCLL + 1)] x (PRESC + 1) x tI2CCLK`, 47.4.9,
//! with `tI2CCLK` the period of `i2c_ker_ck`. The notes under Tables 391 to 393 give `tSYNC1 +
//! tSYNC2` a minimum of four kernel clock periods, which a model with no wire slope and no analog
//! filter takes as the whole of it, and 47.4.9 adds the digital filter's `DNF x tI2CCLK` at each of
//! the two edges. The low time is no shorter than the stretch every pulse gets, "at least [(SDADEL +
//! SCLDEL + 1) x (PRESC + 1) + 1] x tI2CCLK", 47.4.5. Steps are added up in kernel clock periods from
//! one origin and rounded once, so a long run of bytes does not drift.
//!
//! A transfer starts at the instant `START` is written, and its address ends nine periods later. The
//! start and stop conditions and the bus-free time after a stop take no time of their own, since
//! the manual gives no figure for them. When software owes the controller something, SCL is stretched
//! and nothing is scheduled: `TXIS` waits for `TXDR`, a full `RXDR` for a read, and `TC` and `TCR`
//! for `CR2`.
//!
//! **The kernel clock.** `i2c_ker_ck` is [`RESET_KERNEL_HZ`] until the part's clock tree drives it
//! through [`Device::on_clock`], 64 MHz because the first three instances select `rcc_pclk1` and the
//! fourth `rcc_pclk4` "default after reset", 8.7.21 and 8.7.22, and a part that has not touched its
//! clock tree runs both from the 64 MHz `hsi_ck`, 8.7.2, 8.7.7, 8.7.8, 8.7.9 and 8.7.10. What a step
//! has left on the wire finishes at the new rate from the instant of the change, and a stopped clock
//! holds it where it stands. A step the clock or `PE` moved leaves its old event in the queue. There
//! is no cancel, so the model waits for one id and ignores the rest.
//!
//! **What the part gives it.** [`INSTANCES`] has each instance's name and its request multiplexer
//! inputs, Tables 124 and 128. A part wires [`RECEIVE_REQUEST`] and [`TRANSMIT_REQUEST`] to them,
//! gives each instance the clock its `I2C123SEL` or `I2C4SEL` selects, and calls
//! [`I2c::chips_restarted`] and [`I2c::inherit`] across a reset as the STM32F427's part does.
//!
//! **ArduPilot's bus clear.** ArduPilot starts by toggling SCL as a GPIO twenty times,
//! `I2CBus::clear_bus`, to release a chip holding SDA low, and does so again after a timeout. That
//! is the GPIO model's, and the interface sees no edge of it.
//!
//! | Register or section | Behavior | Source | Test |
//! |---|---|---|---|
//! | Reset state | `ISR` at `0x0000_0001`, `TXE` set, every other register zero | 47.9.12 Table 401 | `every_register_comes_out_of_reset_as_the_manual_gives_it` |
//! | Register types | `START`, `STOP`, `NACK`, `PECBYTE`, `TXE` and `TXIS` are type `rs`, which software sets and a written zero leaves | 47.9.2, 47.9.7 | `the_corrected_fields_are_what_the_part_declares_on_every_instance`, `a_written_zero_does_not_clear_txe_or_a_pending_start` |
//! | `PE` | Clearing it releases the lines, resets the state machines, and puts `START`, `STOP`, `NACK`, `PECBYTE` and every status bit back at reset with `TXE` set. The configuration stays. What was on the wire never completes. While it is clear `START`, `STOP`, `NACK` and `PECBYTE` read clear, `ISR` holds its reset value, and a `START` is told | 47.4.5, 47.4.6, 47.9.1, 47.9.2, 47.9.7 | `clearing_pe_resets_the_transfer_and_the_flags_and_keeps_the_configuration`, `control_bits_and_txdr_written_with_pe_clear_leave_the_reset_state`, `a_start_with_pe_clear_starts_nothing_and_is_told`, `an_event_nothing_waits_for_is_ignored` |
//! | `START`, `SADD`, `RD_WRN`, `NBYTES` | Sets `BUSY`, sends the address for nine SCL periods, and clears `START` once it has gone. `SADD[7:1]` is the 7-bit address and bits 9, 8 and 0 are don't care | 47.4.9, 47.9.2, 47.9.7 | `a_start_puts_the_address_on_the_wire_for_nine_scl_periods`, `a_seven_bit_address_comes_from_sadd_bits_seven_to_one` |
//! | Address acknowledged | In the write direction `TXIS` is set and SCL is stretched until `TXDR` is written | 47.4.9 | `an_acknowledged_write_address_sets_txis_and_stretches_scl` |
//! | Address not acknowledged | `NACKF` is set, `TXIS` is not, and the stop follows by itself with `STOPF` set and `BUSY` clear, whatever `AUTOEND` says. A run of probes of empty addresses costs nine SCL periods each and leaves the bus free | 47.4.9, 47.9.7 | `an_unanswered_address_sets_nackf_and_stopf_and_frees_the_bus_at_the_ninth_pulse`, `a_run_of_probes_of_empty_addresses_costs_nine_scl_periods_each`, `chibios_gets_its_nack_from_an_empty_address_in_one_interrupt` |
//! | `TXIS`, `TXE`, `TXDR` | `TXIS` is set after the address and after each acknowledged byte that leaves more to send. A write to `TXDR` clears it and the byte moves into the shift register at once, so `TXE` stays set. The byte takes nine periods from the write | 47.4.7, 47.4.9, 47.9.7, Figure 544 | `each_byte_written_to_txdr_takes_nine_scl_periods_and_its_acknowledge_asks_for_the_next` |
//! | A byte written early | A byte written while one is on the wire waits in `TXDR` with `TXE` clear and moves in at the acknowledge, so the two go back to back with no stretch | 47.4.7, Figure 544 | `a_byte_written_while_one_is_on_the_wire_follows_it_without_a_stretch` |
//! | `TXE` written one | Flushes `TXDR` | 47.9.7 | `writing_one_to_txe_flushes_a_byte_waiting_in_txdr` |
//! | `TXIS` written one | Does nothing: 47.9.7 lets software set it only with `NOSTRETCH` set, which 47.9.1 says to keep cleared in controller mode, and a firmware that sets `NOSTRETCH` is told | 47.9.1, 47.9.7 | `txis_written_one_does_nothing_and_nostretch_is_told` |
//! | `TXDR` written while `TXE` is clear | The write is dropped, `TXDR` keeps its byte, and the session is told: 47.9.11 says it "can be written only when TXE = 1" | 47.9.11 | `a_write_to_txdr_while_it_is_full_is_dropped_and_told` |
//! | A data byte not acknowledged | `NACKF` is set, `TXIS` is not set again, and the stop follows | 47.4.9 | `a_nacked_data_byte_sets_nackf_skips_txis_and_stops_the_bus` |
//! | `NBYTES`, `AUTOEND` | The counter ends the segment after `NBYTES` bytes. With `AUTOEND` the stop follows, `STOPF` set and `BUSY` clear. `NBYTES` of zero ends it after the address. `NBYTES` reads back as written | 47.4.7, 47.4.9, 47.9.2 | `each_byte_written_to_txdr_takes_nine_scl_periods_and_its_acknowledge_asks_for_the_next`, `a_transfer_of_zero_bytes_ends_after_the_address`, `nbytes_reads_back_as_written_through_a_transfer` |
//! | Software end, `TC` | With `AUTOEND` clear `TC` is set after the last byte and SCL is stretched until `START` or `STOP` is set, which clears it | 47.4.9, 47.9.7 | `software_end_sets_tc_and_stretches_until_stop_is_set`, `a_software_end_read_sets_tc_after_the_nack_and_keeps_the_last_byte` |
//! | A repeated start | `START` at `TC` clears it and sends the start and the address `CR2` names then, with `BUSY` kept and no stop between. `START` set before the end of a segment goes out after it, with no `TC`, or after the automatic stop once the bus is free | 47.4.9, 47.9.2 | `a_start_at_tc_makes_a_repeated_start_with_no_stop_between`, `a_start_requested_before_the_end_of_a_software_end_segment_restarts_with_no_tc`, `a_start_requested_behind_an_automatic_stop_goes_out_once_the_bus_is_free`, `a_start_waiting_behind_a_transfer_goes_out_after_a_nack_stops_it` |
//! | `RELOAD`, `TCR` | With `RELOAD` the end of `NBYTES` sets `TCR` and stretches SCL with no stop, and `AUTOEND` has no effect. A non-zero `NBYTES` written clears `TCR` and goes on. A zero leaves it. More than 255 bytes go in blocks | 47.4.7, 47.4.9, 47.9.7, Table 389 | `reload_raises_tcr_and_a_non_zero_nbytes_write_resumes_the_transfer`, `a_transfer_of_more_than_255_bytes_reloads_and_costs_a_byte_period_each` |
//! | `STOP` | The stop follows the byte on the wire, or comes at once with SCL stretched at `TXIS`, `TC` or `TCR`. It clears `STOP`, `NACK` and `PECBYTE`, sets `STOPF` and clears `BUSY`. With no transfer open it is not a request and the bit reads clear, the model's own reading for the STOP ChibiOS's NACK handler writes after the automatic stop | 47.4.9, 47.9.2, 47.9.7 | `stop_set_while_a_byte_is_on_the_wire_ends_the_bus_after_that_byte`, `stop_set_while_txis_stands_stops_the_bus_at_once`, `stop_set_while_tcr_stands_ends_the_transfer`, `the_stop_condition_clears_nack_and_pecbyte`, `a_stop_with_no_transfer_open_does_nothing_and_leaves_the_bit_clear` |
//! | `RXNE`, `RXDR` | A byte reaches `RXDR` at the eighth pulse and `RXNE` is set. The acknowledge pulse follows and the next byte starts at once. A read of `RXDR` clears `RXNE` | 47.4.7, 47.9.7, 47.9.10, Figure 543 | `a_received_byte_reaches_rxdr_at_the_eighth_pulse_and_the_last_one_is_not_acknowledged`, `a_debuggers_peek_of_rxdr_does_not_clear_rxne` |
//! | The last received byte | The byte before a stop or a repeated start is not acknowledged. Under `AUTOEND` the stop follows, otherwise `TC` is set. A byte received while a `STOP` is asked for is the last | 47.4.9 | `a_received_byte_reaches_rxdr_at_the_eighth_pulse_and_the_last_one_is_not_acknowledged`, `a_software_end_read_sets_tc_after_the_nack_and_keeps_the_last_byte`, `stop_set_during_a_receive_nacks_the_byte_on_the_wire` |
//! | A received byte while `RXNE` is set | SCL is stretched between the eighth and ninth pulse until `RXDR` is read, which returns the older byte and lets the newer in | 47.4.7, Figure 543 | `an_unread_byte_stretches_scl_before_the_acknowledge_until_rxdr_is_read` |
//! | A receive with `RELOAD` | Every byte of the block is acknowledged and `TCR` is set after the last | 47.4.7, 47.4.9 | `a_receive_with_reload_acknowledges_the_block_and_raises_tcr` |
//! | 10-bit addressing | `ADD10` sends the header with the two top address bits, then the second byte. A read with `HEAD10R` clear then restarts with the header in the read direction, and with `HEAD10R` set sends only that header. A header not acknowledged is resent every nine periods until it is, or until `ADDRCF` quits the loop, and a second byte not acknowledged stops like a 7-bit address. `START` clears after the whole sequence | 47.4.9, 47.9.2, Figures 554 and 555 | `a_ten_bit_write_sends_the_header_then_the_second_byte_and_then_data`, `a_ten_bit_read_sends_the_write_header_the_second_byte_and_a_read_header`, `head10r_sends_only_the_read_header_for_a_turnaround`, `a_ten_bit_header_that_is_not_acknowledged_is_resent_until_it_is`, `addrcf_quits_the_loop_that_resends_a_ten_bit_header`, `an_unacknowledged_second_byte_of_a_ten_bit_address_stops_too` |
//! | `TIMINGR`, `DNF` | The SCL period above, from `PRESC`, `SCLL`, `SCLH`, `SDADEL`, `SCLDEL` and `DNF`, against the kernel clock. The cases are columns of Tables 391 and 393, the stretch floor, the filter, the largest prescaler and the value ArduPilot programs | 47.4.5, 47.4.9, 47.9.1, 47.9.5, Tables 391 to 393 | `the_address_phase_lasts_nine_scl_periods_of_the_period_timingr_gives`, `a_run_of_bytes_does_not_drift` |
//! | The kernel clock | `RESET_KERNEL_HZ` until driven. A change moves what a step has left to the new rate, and a stopped clock holds it | 47.4.3, 8.7.2, 8.7.7 to 8.7.10, 8.7.21, 8.7.22 | `an_interface_never_wired_to_a_clock_runs_on_the_64_mhz_the_part_wakes_up_on`, `a_change_of_kernel_clock_applies_to_the_periods_a_step_has_left`, `a_stopped_kernel_clock_holds_a_step_until_it_returns`, `a_clock_change_while_idle_applies_to_the_next_transfer` |
//! | `TXDMAEN`, `RXDMAEN` | The transmit request follows `TXIS`, request output 1, and the receive request follows `RXNE`, request output 0 | 47.7.1, 47.7.2 | `txdmaen_drives_the_transmit_request_while_txis_stands`, `rxdmaen_drives_the_receive_request_while_rxne_stands`, `without_their_enables_the_dma_requests_stay_down` |
//! | The event interrupt | Position 0. `TC` or `TCR` under `TCIE`, `TXIS` under `TXIE`, `RXNE` under `RXIE`, `STOPF` under `STOPIE`, `ADDR` under `ADDRIE` and `NACKF` under `NACKIE`, a level that lowers with the flag | 47.6, Table 400, Figure 569 | `the_event_line_follows_each_flag_under_its_own_enable`, `a_nack_raises_the_event_line_under_nackie_and_nackcf_lowers_it`, `a_stop_raises_the_event_line_under_stopie_and_stopcf_lowers_it`, `txis_and_rxne_raise_the_event_line_under_txie_and_rxie`, `every_instance_runs_the_same_model_on_its_own_interrupt_lines` |
//! | The error interrupt | Position 1. Any of `BERR`, `OVR`, `ARLO`, `TIMEOUT`, `ALERT` and `PECERR` under `ERRIE`. Nothing here sets them, so the test raises each in the register file | 47.6, Figure 569 | `the_error_line_follows_any_error_flag_under_erie_at_position_one` |
//! | `ICR` | Each bit written one clears its own flag and no other. `ADDRCF` also clears `START`, and quits the loop that resends a 10-bit header. The register reads zero and its reserved bits do nothing | 47.4.9, 47.9.8 | `icr_clears_each_flag_written_one_and_leaves_the_others`, `icr_bits_that_name_no_flag_clear_nothing`, `addrcf_withdraws_a_start_waiting_behind_a_transfer`, `addrcf_quits_the_loop_that_resends_a_ten_bit_header` |
//! | Target-mode flags | `ADDR`, `DIR`, `ADDCODE` and the errors that need a second controller are never set | 47.9.7 | `a_transfer_never_sets_the_target_mode_flags` |
//! | Registers stored | `OAR1`, `OAR2` and `TIMEOUTR` keep what is written, and the read-only `PECR` and `RXDR` ignore a write | 47.9.3, 47.9.4, 47.9.6, 47.9.9, 47.9.10 | `target_and_smbus_registers_store_what_is_written_and_read_only_ones_do_not` |
//! | Firmware mistakes | Told once each. Dropped: `TXDR` written while full, and `DNF`, `ANFOFF` or `NOSTRETCH` changed with `PE` set, which 47.9.1 and 47.9.11 say can be written only otherwise. Landed: `TIMINGR` changed with `PE` set, the addressing fields changed while `START` is set, and `START` with `PE` clear. `NOSTRETCH` set is told too | 47.9.1, 47.9.2, 47.9.5, 47.9.11 | `timingr_changed_with_pe_set_is_told_and_a_rewrite_of_the_same_value_is_not`, `the_filter_and_stretch_bits_changed_with_pe_set_are_dropped_and_told`, `addressing_fields_changed_while_start_is_set_are_told`, `a_start_with_pe_clear_starts_nothing_and_is_told`, `a_write_to_txdr_while_it_is_full_is_dropped_and_told` |
//! | The chips on the wires | The first chip in attachment order that acknowledges the address is the one addressed. A silenced chip is not acknowledged, and a silence asked for inside its transfer waits for the stop. A chip's own events come back to it, a part restart reaches every chip, and a fresh interface takes the chips of the old one | 47.4.4 | `the_first_chip_that_acknowledges_the_address_is_the_one_addressed`, `a_silent_chip_is_not_acknowledged_and_answers_again_once_let_back`, `a_silence_asked_inside_a_transfer_waits_for_the_stop`, `events_a_chip_scheduled_come_back_to_it_at_their_instant`, `a_part_restart_reaches_every_chip_on_the_bus`, `a_fresh_interface_inherits_the_chips_of_the_old_one`, `a_board_gets_a_chip_back_as_the_type_it_hung` |
//! | A driver's whole sequence | ChibiOS's `i2c_lld_master_transmit_timeout` as ArduPilot issues it for the barometer's chip ID, register accesses and interrupt handler ported function for function, in interrupt mode, which ArduPilot's H7 build uses, and in ChibiOS's DMA mode, at the board's kernel clock and timing: the bytes, the instant each reached the chip, and the interrupts taken, within the driver's limit | 47.4.9, 47.7 | `chibios_reads_the_barometers_chip_id_with_these_bytes_at_these_instants`, `chibios_with_dma_reads_the_chip_id_through_the_two_requests` |
//! | The four instances | The instance table with its request multiplexer inputs, the lines of each, and the layout every instance carries | 47.3 Table 384, Table 147 | `the_instance_table_names_the_four_instances_of_the_layout`, `every_instance_runs_the_same_model_on_its_own_interrupt_lines` |
//! | Snapshot, reset, determinism | The state serializes. A reset puts the transfer back and keeps the chips. Two runs of one script are one run | The runtime's snapshot and reset | `the_model_state_round_trips_through_a_snapshot`, `a_reset_puts_the_transfer_back_and_keeps_the_chips`, `two_runs_of_one_script_are_the_same_run` |
//! | The reset bit | A write that leaves the interface's bit of `RCC_APB1LRSTR` or `RCC_APB4RSTR` set puts it back the same way, with both lines and each request the multiplexer heard rise lowered at once | 8.4.3, 8.7.33, 8.7.36 | `a_reset_line_lowers_the_request_the_multiplexer_heard_at_once` |
//! | ES0392 rev 15 2.19.1 | Table 3 marks it `A` for revisions Y and W and absent on V, so it is not applicable here and the model follows 47.4.9: a header not acknowledged is resent every nine periods until it is, or `ADDRCF` is written. No test reproduces the erratum's stuck `START` | ES0392 rev 15 2.19.1, Table 3 | `a_ten_bit_header_that_is_not_acknowledged_is_resent_until_it_is` |
//! | ES0392 rev 15 2.19.7 | Documentation only: `START` is cleared by setting `ADDRCF`, not at an address match. The model clears a waiting `START` that way | ES0392 rev 15 2.19.7 | `addrcf_withdraws_a_start_waiting_behind_a_transfer` |
//!
//! **What the model leaves out, and why.**
//!
//! | Behavior | Source | Reason |
//! |---|---|---|
//! | Target mode: `OAR1`, `OAR2`, `GCEN`, `SBC`, `NOSTRETCH` as a target, `WUPEN`, the `NACK` bit's use, `ADDR`, `DIR`, `ADDCODE`, `OVR`, the byte-control mode | 47.4.8, 47.9.1 to 47.9.4 | Nothing on a board's side addresses the part, the same gap the STM32F427's interface leaves. The registers are stored and the bits are inert. `NACK` and `PECBYTE` clear at a stop as the pages say |
//! | Multi-controller operation: `ARLO`, `BERR`, clock synchronization with a second controller, a controller holding SCL low | 47.4.9, 47.4.17 | This model is the bus's only controller and drives every edge itself, so none of it can arise |
//! | The System Management Bus layer: `PECEN`, `PECBYTE`, `PECR`, `PECERR`, `TIMEOUTR`, `TIMEOUT`, the host, device default and alert addresses, `ALERT` | 47.4.11 to 47.4.15, 47.9.6 | No driver of this firmware's interface turns it on. ArduPilot sets `SMBHEN` for a System Management Bus battery monitor and never `PECEN`. The registers are stored. A transfer with `PECBYTE` set is not given its PEC byte |
//! | Wake-up from Stop mode and `i2c_wkup` | 47.4.16, 47.5, Table 400 | The part has no low-power modes |
//! | The debug freeze of the bus timeout | 47.8 | The timeout is not modeled |
//! | The duration of the start and stop conditions and the bus-free time after a stop, `tHD:STA`, `tSU:STA`, `tSU:STO` and `tBUF` | 47.4.9, Table 390 | The manual says only that `SCLH` and `SCLL` "also" set them and gives no figure. A start goes out at the write, a stop is at the end of the last pulse, and a start behind a stop follows it at the same instant |
//! | The analog filter delay `tAF`, the SCL and SDA slopes, and `tSYNC1 + tSYNC2` above its minimum | 47.4.5, 47.4.9, DS12110 rev 11 Table 95 | The datasheet gives `tAF` as the width of a spike the filter suppresses, 50 ns at least and 260 ns at most, and the slopes belong to a board. The model has no analog edge, so the sync delay is the minimum the notes under Tables 391 to 393 give |
//! | Noise filtering by `DNF` and `ANFOFF` | 47.4.5 | There is no noise on a wire to filter. The delay `DNF` adds is modeled |
//! | The data hold and data setup times beyond the stretch | 47.4.5, Table 388 | Waveform margins on the wire, not register behavior. The minimum stretch they give is the floor under the low time |
//! | A chip stretching SCL | 47.4.9 | [`Peer`]'s calls answer in the call that makes them, so a chip cannot hold SCL for a stretch of virtual time. The controller's own stretches for software are modeled |
//! | Write latency and the three `i2c_pclk` cycles `PE` must stay clear | 47.4.6, 47.9.1 | There is no APB clock in the model, and every write takes effect at the access |
//! | Being held at reset while `I2CxRST` stands | 8.4.3, 8.7.33, 8.7.36 | The interface is reset at each write that leaves the bit set and answers as usual between two of them. Held in reset it would answer its reset values and take no write, but ChibiOS sets and clears the bit in two writes, so nothing reaches it while the bit stands |
//! | Two chips acknowledging the same address | 47.4.4 | An acknowledge is open drain, so two chips pulling it low are one acknowledge. The first in attachment order is the one addressed |
//! | Transaction counts and one-shot drops on an I2C chip | The chip seam | [`Peer`] gives the controller no way to learn which chip an address picks before it starts one, so a place's `begin` is never called. The interface's `attach` carries the ceiling |
//! | A sub-word access | 47.9 | "The registers are accessed by words (32-bit)." The effect of a narrower one is not stated, so the bus lands it in its lane |
//! | ES0392 rev 15 2.19.2 | ES0392 rev 15 | A documentation erratum: `PE` must be clear before Stop mode with `WUPEN` clear. The part has no Stop mode |
//! | ES0392 rev 15 2.19.3 | ES0392 rev 15 | Table 3 marks it `P` on every revision, so it applies to V: a data setup time shorter than one kernel clock period samples the previous SDA. Its workaround is a faster kernel clock. It is an analog margin on the wire, not a register behavior |
//! | ES0392 rev 15 2.19.4 | ES0392 rev 15 | Table 3 marks it `A` on every revision, so it applies to V: `BERR` can be set spuriously in controller mode and "any such transfer continues normally". The sheet names no condition, so nothing sets it here. Its workaround, clearing `BERR`, is `BERRCF`, which the model takes |
//! | ES0392 rev 15 2.19.5 | ES0392 rev 15 | Table 3 marks it `P` for revisions Y and W and absent on V, so it is not applicable here. No test reproduces what this revision does not do |
//! | ES0392 rev 15 2.19.6 | ES0392 rev 15 | Table 3 marks it `P` on every revision. It needs the part to be a target and a controller on one bus, which is target mode |
//! | ES0392 rev 15 2.19.8 | ES0392 rev 15 | Table 3 marks it `N` on every revision. It is target mode, `OVR` with `NOSTRETCH` |
//! | ES0392 rev 15 2.19.9 | ES0392 rev 15 | Table 3 marks it `A` on every revision, so it applies to V, pending. A controller stalls when the second byte of two successive requests is written less than two kernel clock cycles after the first and the ratio of the APB clock to the kernel clock is between 1.5 and 3. The model has no APB clock and writes happen at whole instants, so it has neither term. A driver that follows the workaround, writing the first byte before the start or keeping the ratio outside the range, is not affected |
//! | ES0392 rev 15 2.19.10 | ES0392 rev 15 | Table 3 marks it `A` on every revision. It is the bus timeout in target mode |
//!
//! **Where this model departs from the vendor's file.** Sections 47.9.2 and 47.9.7 type `START`,
//! `STOP`, `NACK`, `PECBYTE`, `TXE` and `TXIS` as `rs`, which software can set and a written zero
//! leaves, and ST's file types them plain read-write. [`START`], [`STOP`], [`NACK`], [`PECBYTE`],
//! [`TXE`] and [`TXIS`] restate them, and the part corrects each on every instance. The file splits
//! `SADD[9:0]` into ten one-bit fields, and the model reads it whole through fields of its own.
//!
//! **Determinism.** The model reads no clock but the virtual one and keeps no randomness. A step's
//! instant is a count of kernel clock periods from one origin. `two_runs_of_one_script_are_the_same_run`
//! drives two fresh interfaces through the barometer's chip ID read, a probe nobody answers, a byte
//! cut off by a clock change and a disable, and checks the interrupt lines, the requests, what was
//! told, every register, the chips' records of what they heard and the model's whole state agree
//! between the two.
use crate::chip::i2c::Peer;
use crate::chip::{Place, Timeline};
use crate::device::{Device, DeviceContext, Level, RegisterFile};
use crate::snapshot::store;
use crate::soc::stm32h743::generated::i2c1::{Register, cr1, cr2, icr, isr, timingr, txdr};
use crate::soc::{Access, FieldSpec, WriteModify};
use crate::time::{EventId, NANOS_PER_SECOND, VirtualNanos};
// The event line, position zero of the instance's table: `I2C1_EV` at line 31 of RM0433 rev 9
// Table 147, and the instance's own pair for the others.
const EVENT: usize = 0;
const ERROR: usize = 1;
/// The request `RXDMAEN` drives off `RXNE`, this instance's request output zero, RM0433 rev 9
/// section 47.7.2.
pub const RECEIVE_REQUEST: usize = 0;
/// The request `TXDMAEN` drives off `TXIS`, request output one, section 47.7.1.
pub const TRANSMIT_REQUEST: usize = 1;
/// The rate `i2c_ker_ck` has until the part's clock tree drives it otherwise: `rcc_pclk1` for the
/// first three instances and `rcc_pclk4` for the fourth, both the 64 MHz `hsi_ck` of a part that
/// still runs on the oscillator it wakes up on. RM0433 rev 9 section 8.7.21 and 8.7.22 select the
/// kernel clock from `rcc_pclk1` and `rcc_pclk4` "default after reset", and 8.7.9 and 8.7.10 divide
/// neither. 8.7.8 divides `sys_ck` by nothing, 8.7.7 selects `hsi_ck` as `sys_ck` "default after
/// reset", and 8.7.2 gives `hsi_ck` as 64 MHz at `HSIDIV` 00 "default after reset".
pub const RESET_KERNEL_HZ: u64 = 64_000_000;
// SCL pulses in one byte: eight data bits and the acknowledge bit, section 47.4.4.
const BYTE_PERIODS: u64 = 9;
// SCL pulses before a received byte reaches `RXDR`, section 47.4.7.
const RECEIVED_PERIODS: u64 = 8;
const ACKNOWLEDGE_PERIODS: u64 = 1;
// `tSYNC1 + tSYNC2` as a count of `i2c_ker_ck` periods: "minimum value is 4 x tI2CCLK", the note
// under Tables 391 to 393. A model has no wire slope or analog filter to add to it.
const SYNC_TICKS: u64 = 4;
// What a 10-bit address's first byte carries before its two top address bits, `11110`, as the
// 7-bit address a [`Peer`] is started with, section 47.4.9.
const TEN_BIT_HEADER: u8 = 0x78;
// The addressing fields of `CR2`, which section 47.9.2 says not to change while `START` stands.
const ADDRESSING: u64 =
SADD.mask() | cr2::RD_WRN.mask() | cr2::ADD10.mask() | cr2::HEAD10R.mask() | cr2::NBYTES.mask();
// The fields of `CR1` that section 47.9.1 allows to change only with `PE` clear.
const CR1_WHILE_DISABLED: u64 = cr1::DNF.mask() | cr1::ANFOFF.mask() | cr1::NOSTRETCH.mask();
// The fields of `TIMINGR`, section 47.9.5.
const TIMING: u64 = timingr::PRESC.mask()
| timingr::SCLDEL.mask()
| timingr::SDADEL.mask()
| timingr::SCLH.mask()
| timingr::SCLL.mask();
// The flags the error line follows, with `ERRIE`, section 47.6.
const ERROR_FLAGS: u64 = isr::BERR.mask()
| isr::OVR.mask()
| isr::ARLO.mask()
| isr::TIMEOUT.mask()
| isr::ALERT.mask()
| isr::PECERR.mask();
// The bits of `CR2` hardware clears at a stop and with `PE`, section 47.9.2.
const CR2_CLEARED_BY_STOP: u64 = cr2::STOP.mask() | cr2::NACK.mask() | cr2::PECBYTE.mask();
// Each enable bit of the event line and the flags it lets through, section 47.6 and Figure 569.
const EVENT_SOURCES: [(&FieldSpec, u64); 6] = [
(cr1::TCIE, isr::TC.mask() | isr::TCR.mask()),
(cr1::TXIE, isr::TXIS.mask()),
(cr1::RXIE, isr::RXNE.mask()),
(cr1::STOPIE, isr::STOPF.mask()),
(cr1::ADDRIE, isr::ADDR.mask()),
(cr1::NACKIE, isr::NACKF.mask()),
];
const TIMING_WHILE_ENABLED: &str =
"RM0433 rev 9 section 47.9.5: TIMINGR must be configured with PE = 0";
const FILTERS_WHILE_ENABLED: &str = "RM0433 rev 9 section 47.9.1: DNF, ANFOFF and NOSTRETCH can be programmed only with PE = 0, so the write is dropped";
const NOSTRETCH_IN_CONTROLLER: &str =
"RM0433 rev 9 section 47.9.1: NOSTRETCH must be kept cleared in controller mode";
const ADDRESSING_WHILE_STARTING: &str = "RM0433 rev 9 section 47.9.2: SADD, RD_WRN, ADD10, HEAD10R and NBYTES must not change while START is set";
const START_WHILE_DISABLED: &str =
"RM0433 rev 9 section 47.9.2: hardware clears START while PE = 0, so no start is generated";
const TXDR_WHILE_FULL: &str =
"RM0433 rev 9 section 47.9.11: TXDATA can be written only when TXE = 1";
// `SADD[9:0]`, section 47.9.2.
const SADD: FieldSpec = FieldSpec::new("SADD", 0, 10, Access::ReadWrite);
const SADD_ADDRESS: FieldSpec = FieldSpec::new("SADD[7:1]", 1, 7, Access::ReadWrite);
const SADD_TOP: FieldSpec = FieldSpec::new("SADD[9:8]", 8, 2, Access::ReadWrite);
const SADD_LOW: FieldSpec = FieldSpec::new("SADD[7:0]", 0, 8, Access::ReadWrite);
/// `START`, RM0433 rev 9 section 47.9.2: type `rs`, set by software and cleared by hardware.
pub static START: FieldSpec = cr2::START.with_write(WriteModify::OneToSet);
/// `STOP`, section 47.9.2, the same rule as [`START`].
pub static STOP: FieldSpec = cr2::STOP.with_write(WriteModify::OneToSet);
/// `NACK`, section 47.9.2, the same rule as [`START`].
pub static NACK: FieldSpec = cr2::NACK.with_write(WriteModify::OneToSet);
/// `PECBYTE`, section 47.9.2, the same rule as [`START`].
pub static PECBYTE: FieldSpec = cr2::PECBYTE.with_write(WriteModify::OneToSet);
/// `TXE`, section 47.9.7: type `rs`, software can set it to flush `TXDR`.
pub static TXE: FieldSpec = isr::TXE.with_write(WriteModify::OneToSet);
/// `TXIS`, section 47.9.7: type `rs`, the same rule as [`TXE`].
pub static TXIS: FieldSpec = isr::TXIS.with_write(WriteModify::OneToSet);
/// An instance of the interface and what a part wires it with.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Instance {
/// The instance as the vendor's file names it.
pub name: &'static str,
/// The request multiplexer its two requests arrive at: I2C4 is on the second, the other three
/// on the first, RM0433 rev 9 Tables 124 and 128.
pub multiplexer: &'static str,
/// The multiplexer input [`RECEIVE_REQUEST`] drives.
pub receive_input: u8,
/// The multiplexer input [`TRANSMIT_REQUEST`] drives.
pub transmit_input: u8,
}
/// The four instances, one layout. Each has two interrupt lines, the event line at position 0 and
/// the error line at position 1 of the table the part carries, and runs on the kernel clock
/// RM0433 rev 9 sections 8.7.21 and 8.7.22 select: `I2C123SEL` for the first three and `I2C4SEL`
/// for the fourth.
pub static INSTANCES: [Instance; 4] = [
Instance {
name: "I2C1",
multiplexer: "DMAMUX1",
receive_input: 33,
transmit_input: 34,
},
Instance {
name: "I2C2",
multiplexer: "DMAMUX1",
receive_input: 35,
transmit_input: 36,
},
Instance {
name: "I2C3",
multiplexer: "DMAMUX1",
receive_input: 73,
transmit_input: 74,
},
Instance {
name: "I2C4",
multiplexer: "DMAMUX2",
receive_input: 13,
transmit_input: 14,
},
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Direction {
Write,
Read,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Start {
Clear,
Pending,
Sending,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Second {
top: u8,
byte: u8,
then_read: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Sent {
Seven(u8),
Header {
top: u8,
read: bool,
second: Option<Second>,
},
Second(Second),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Step {
Address(Sent),
Resend(Sent),
Written(u8),
Received,
Acknowledged,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Wait {
event: Option<EventId>,
step: Step,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum State {
Idle,
OnWire(Wait),
AwaitingData,
Stretching,
Reloading,
Complete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Wire {
origin: VirtualNanos,
ticks: u64,
ended: Option<VirtualNanos>,
}
/// One inter-integrated circuit interface of an STM32H743, RM0433 rev 9 chapter 47, in controller
/// mode.
#[derive(Debug)]
pub struct I2c {
kernel_hz: u64,
peers: Vec<Place<dyn Peer>>,
active: Option<usize>,
state: State,
wire: Wire,
direction: Direction,
left: u32,
txdr_full: bool,
held: u8,
txis: bool,
shifted: Option<u8>,
start: Start,
stop_requested: bool,
last_cr1: u64,
last_cr2: u64,
last_timingr: u64,
interrupt: [Level; 2],
requests: [Level; 2],
}
store!(I2c {
values: [
kernel_hz,
active,
state,
wire,
direction,
left,
txdr_full,
held,
txis,
shifted,
start,
stop_requested,
last_cr1,
last_cr2,
last_timingr,
interrupt,
requests,
],
parts: [peers],
built: [],
});
impl Default for I2c {
fn default() -> Self {
Self::with_kernel_clock(RESET_KERNEL_HZ)
}
}
fn flag(registers: &mut RegisterFile, field: &FieldSpec, on: bool) {
registers.set(
Register::Isr,
field.set(registers.get(Register::Isr), u64::from(on)),
);
}
fn clear_cr2(registers: &mut RegisterFile, mask: u64) {
registers.set(Register::Cr2, registers.get(Register::Cr2) & !mask);
}
impl I2c {
/// The interface of an instance whose `i2c_ker_ck` runs at `hz` until the part's clock tree
/// drives it otherwise.
#[must_use]
pub fn with_kernel_clock(hz: u64) -> Self {
Self {
kernel_hz: hz,
peers: Vec::new(),
active: None,
state: State::Idle,
wire: Wire {
origin: VirtualNanos(0),
ticks: 0,
ended: None,
},
direction: Direction::Write,
left: 0,
txdr_full: false,
held: 0,
txis: false,
shifted: None,
start: Start::Clear,
stop_requested: false,
last_cr1: 0,
last_cr2: 0,
last_timingr: 0,
interrupt: [Level::Low; 2],
requests: [Level::Low; 2],
}
}
/// Hangs a chip on this bus's wires, behind whatever address it answers to.
pub fn attach(&mut self, peer: impl Peer + 'static) {
let peer: Box<dyn Peer> = Box::new(peer);
// ceiling: a trigger never counts or faults a chip here; upgrade when a board names an I2C chip to one
self.peers.push(Place::new(peer));
}
/// Takes the chips off `from`'s wires and hangs them on these, silent if they were. A part reset
/// ends the open transfer, so a silence that was waiting for it has had it.
pub fn inherit(&mut self, from: &mut Self) {
self.peers = std::mem::take(&mut from.peers);
self.peers.iter_mut().for_each(Place::release);
}
/// Stops the nth chip on these wires answering, or lets it answer again: a board condition,
/// [`Place::silence`]. A chip in the open transfer keeps answering until it ends, so a transfer
/// is never cut in half. Answers whether a chip hangs there.
#[must_use]
pub fn silence(&mut self, nth: usize, silent: bool) -> bool {
let Some(place) = self.peers.get_mut(nth) else {
return false;
};
if self.active == Some(nth) {
place.silence_at_release(silent);
} else {
place.silence(silent);
}
true
}
/// The first chip on this bus, to ask what it saw, or `None` where nothing is hung.
#[must_use]
pub fn peer(&self) -> Option<&dyn Peer> {
self.peers.first().map(Place::chip)
}
/// The nth chip on this bus as the type it was hung as, or `None` where nothing hangs there or
/// another chip does.
pub fn chip_mut<P: Peer>(&mut self, nth: usize) -> Option<&mut P> {
self.peers.get_mut(nth)?.chip_as()
}
/// Tells every chip on this bus that the part restarted, at the instant `context` gives.
pub fn chips_restarted(&mut self, context: &mut DeviceContext<'_>) {
for place in &mut self.peers {
place.chip_mut().restarted(&mut Timeline::new(context));
}
}
fn active_peer(&mut self) -> Option<&mut dyn Peer> {
self.peers.get_mut(self.active?)?.answering()
}
fn release(&mut self, context: &mut DeviceContext<'_>) {
if let Some(peer) = self.active_peer() {
peer.stop(&mut Timeline::new(context));
}
if let Some(place) = self
.active
.take()
.and_then(|index| self.peers.get_mut(index))
{
place.release();
}
}
// Searches every chip for one that acknowledges `address`, section 47.4.4: the controller
// learns nothing about a chip that does not, and a silent one never does.
fn select(&mut self, address: u8, read: bool, context: &mut DeviceContext<'_>) -> bool {
for (index, place) in self.peers.iter_mut().enumerate() {
if place
.answering()
.is_some_and(|peer| peer.start(address, read, &mut Timeline::new(context)))
{
self.active = Some(index);
return true;
}
}
false
}
// One SCL period in `i2c_ker_ck` periods, section 47.4.9.
//
// `tSCL = tSYNC1 + tSYNC2 + [(SCLH + 1) + (SCLL + 1)] x (PRESC + 1) x tI2CCLK`, with the sync delay at the 4 periods Tables 391 to 393 give as its minimum, plus each digital filter delay `DNF x tI2CCLK`. The low time is no shorter than the stretch every pulse gets, `[(SDADEL + SCLDEL + 1) x (PRESC + 1) + 1] x tI2CCLK`, section 47.4.5.
fn period_ticks(registers: &RegisterFile) -> u64 {
let timing = registers.get(Register::Timingr);
let prescaler = timingr::PRESC.get(timing) + 1;
let stretch =
(timingr::SDADEL.get(timing) + timingr::SCLDEL.get(timing) + 1) * prescaler + 1;
let low = ((timingr::SCLL.get(timing) + 1) * prescaler).max(stretch);
let high = (timingr::SCLH.get(timing) + 1) * prescaler;
let filter = cr1::DNF.get(registers.get(Register::Cr1));
SYNC_TICKS + 2 * filter + low + high
}
fn nanos(&self, ticks: u64) -> u64 {
let nanos = u128::from(ticks) * u128::from(NANOS_PER_SECOND) / u128::from(self.kernel_hz);
u64::try_from(nanos).unwrap_or(u64::MAX)
}
fn elapsed_ticks(&self, now: VirtualNanos) -> u64 {
let elapsed = u128::from(now.0.saturating_sub(self.wire.origin.0))
* u128::from(self.kernel_hz)
/ u128::from(NANOS_PER_SECOND);
u64::try_from(elapsed).unwrap_or(u64::MAX)
}
fn arm(&self, context: &mut DeviceContext<'_>) -> Option<EventId> {
(self.kernel_hz != 0).then(|| {
context.schedule(VirtualNanos(
self.wire.origin.0 + self.nanos(self.wire.ticks),
))
})
}
fn run(
&mut self,
step: Step,
periods: u64,
registers: &RegisterFile,
context: &mut DeviceContext<'_>,
) {
if self.wire.ended != Some(context.now()) {
self.wire = Wire {
origin: context.now(),
ticks: 0,
ended: None,
};
}
self.wire.ticks += periods * Self::period_ticks(registers);
let event = self.arm(context);
self.state = State::OnWire(Wait { event, step });
}
fn forget_transfer(&mut self) {
self.state = State::Idle;
self.direction = Direction::Write;
self.left = 0;
self.txdr_full = false;
self.held = 0;
self.txis = false;
self.shifted = None;
self.start = Start::Clear;
self.stop_requested = false;
}
fn sync_transmit_flags(&self, registers: &mut RegisterFile) {
flag(registers, isr::TXE, !self.txdr_full);
flag(registers, isr::TXIS, self.txis);
}
// The request lines and the two interrupt lines, from the flags and their enables, section
// 47.6 and sections 47.7.1 and 47.7.2.
fn refresh(&mut self, registers: &RegisterFile, context: &mut DeviceContext<'_>) {
let control = registers.get(Register::Cr1);
let status = registers.get(Register::Isr);
let requests = [
(
RECEIVE_REQUEST,
cr1::RXDMAEN.is_set(control) && isr::RXNE.is_set(status),
),
(
TRANSMIT_REQUEST,
cr1::TXDMAEN.is_set(control) && isr::TXIS.is_set(status),
),
];
for (output, wanted) in requests {
let level = Level::from(wanted);
if level != self.requests[output] {
self.requests[output] = level;
context.request(output, level);
}
}
let event = EVENT_SOURCES
.iter()
.any(|(enable, flags)| enable.is_set(control) && status & flags != 0);
let error = cr1::ERRIE.is_set(control) && status & ERROR_FLAGS != 0;
for (position, wanted) in [(EVENT, event), (ERROR, error)] {
let level = Level::from(wanted);
if level != self.interrupt[position] {
self.interrupt[position] = level;
context.interrupt(position, level);
}
}
}
// `START` carried out: the address sequence `CR2` describes goes on the wire, `BUSY` is set and
// `TC` is cleared, section 47.4.9 and sections 47.9.2 and 47.9.7.
fn launch(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let control = registers.get(Register::Cr2);
let read = cr2::RD_WRN.is_set(control);
self.direction = if read {
Direction::Read
} else {
Direction::Write
};
self.left = u32::try_from(cr2::NBYTES.get(control)).expect("eight bits");
self.start = Start::Sending;
flag(registers, isr::BUSY, true);
flag(registers, isr::TC, false);
let first = if cr2::ADD10.is_set(control) {
let top = u8::try_from(SADD_TOP.get(control)).expect("two bits");
let byte = u8::try_from(SADD_LOW.get(control)).expect("eight bits");
if read && cr2::HEAD10R.is_set(control) {
Sent::Header {
top,
read: true,
second: None,
}
} else {
Sent::Header {
top,
read: false,
second: Some(Second {
top,
byte,
then_read: read,
}),
}
}
} else {
Sent::Seven(u8::try_from(SADD_ADDRESS.get(control)).expect("seven bits"))
};
self.run(Step::Address(first), BYTE_PERIODS, registers, context);
}
// `START` set with `PE` set. It goes at once on a free bus and out of a stretched `TC`, and
// waits behind a segment on the wire, section 47.9.2.
fn request_start(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.start = Start::Pending;
if matches!(self.state, State::Idle | State::Complete) {
self.launch(registers, context);
}
}
// `STOP` set with `PE` set: the bus stops after the byte on the wire, or at once where none is,
// section 47.9.2. With no transfer open there is nothing to stop and the bit stays clear. That
// reading is the model's own: it is there for the STOP that ChibiOS's NACK handler writes after
// the automatic stop.
fn request_stop(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
match self.state {
State::Idle => clear_cr2(registers, cr2::STOP.mask()),
State::AwaitingData | State::Reloading | State::Complete => {
self.stop_requested = true;
self.stop(registers, context);
}
State::OnWire(_) | State::Stretching => self.stop_requested = true,
}
}
// The STOP condition: the chip released, `BUSY` clear, `STOPF` set, and a `START` that waited
// for the bus gone out, sections 47.4.9 and 47.9.7.
fn stop(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.release(context);
self.state = State::Idle;
self.stop_requested = false;
self.txis = false;
clear_cr2(registers, CR2_CLEARED_BY_STOP);
flag(registers, isr::BUSY, false);
flag(registers, isr::STOPF, true);
flag(registers, isr::TC, false);
flag(registers, isr::TCR, false);
self.sync_transmit_flags(registers);
if self.start == Start::Pending {
self.launch(registers, context);
}
}
fn clear_start(&mut self, registers: &mut RegisterFile) {
self.start = Start::Clear;
clear_cr2(registers, cr2::START.mask());
}
// A NACK, of the address or of a byte: `NACKF` is set and the STOP follows by itself, section
// 47.4.9. A `START` that was sending its address is done with. One waiting for the bus is not.
fn nacked(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.start == Start::Sending {
self.clear_start(registers);
}
flag(registers, isr::NACKF, true);
self.stop(registers, context);
}
fn address_done(
&mut self,
sent: Sent,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let read = self.direction == Direction::Read;
let acked = match sent {
Sent::Seven(address) => self.select(address, read, context),
Sent::Header { top, read, .. } => self.select(TEN_BIT_HEADER | top, read, context),
Sent::Second(second) => self
.active_peer()
.is_some_and(|peer| peer.write(second.byte, &mut Timeline::new(context))),
};
if !acked {
if let Sent::Header { .. } = sent {
// 47.4.9: the controller keeps resending a 10-bit header until it is acknowledged.
self.run(Step::Resend(sent), BYTE_PERIODS, registers, context);
} else {
self.nacked(registers, context);
}
return;
}
let next = match sent {
Sent::Header {
second: Some(second),
..
} => Some(Sent::Second(second)),
Sent::Second(Second {
top,
then_read: true,
..
}) => Some(Sent::Header {
top,
read: true,
second: None,
}),
_ => None,
};
if let Some(next) = next {
self.run(Step::Address(next), BYTE_PERIODS, registers, context);
return;
}
self.clear_start(registers);
if self.stop_requested {
self.stop(registers, context);
} else {
self.continue_transfer(registers, context);
}
}
fn continue_transfer(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.left == 0 {
self.segment_end(registers, context);
} else if self.stop_requested {
self.stop(registers, context);
} else if self.direction == Direction::Write {
self.offer_byte(registers, context);
} else {
self.run(Step::Received, RECEIVED_PERIODS, registers, context);
}
}
// A byte is wanted from software. One already in `TXDR` goes on the wire and leaves `TXDR` empty,
// and an empty `TXDR` raises `TXIS` and stretches SCL until it is written, section 47.4.9 and
// Figure 544.
fn offer_byte(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
if self.txdr_full {
self.send(registers, context);
self.txis = self.left > 0;
} else {
self.txis = true;
self.state = State::AwaitingData;
}
self.sync_transmit_flags(registers);
}
fn send(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.txdr_full = false;
self.left -= 1;
self.run(Step::Written(self.held), BYTE_PERIODS, registers, context);
}
// The counter has run out, section 47.4.9: a `STOP` software asked for, then `TCR` under
// `RELOAD`, then the automatic `STOP`, then a `START` software asked for, then `TC`.
fn segment_end(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let control = registers.get(Register::Cr2);
if self.stop_requested || (!cr2::RELOAD.is_set(control) && cr2::AUTOEND.is_set(control)) {
self.stop(registers, context);
} else if cr2::RELOAD.is_set(control) {
flag(registers, isr::TCR, true);
self.state = State::Reloading;
} else if self.start == Start::Pending {
self.launch(registers, context);
} else {
flag(registers, isr::TC, true);
self.state = State::Complete;
}
}
// The segment's next block of `NBYTES`, written while `TCR` stood, section 47.9.7.
fn reload(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let control = registers.get(Register::Cr2);
self.left = u32::try_from(cr2::NBYTES.get(control)).expect("eight bits");
flag(registers, isr::TCR, false);
self.state = State::Idle;
self.continue_transfer(registers, context);
}
fn written(&mut self, byte: u8, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let acked = self
.active_peer()
.is_some_and(|peer| peer.write(byte, &mut Timeline::new(context)));
if acked {
self.continue_transfer(registers, context);
} else {
self.nacked(registers, context);
}
}
// The eighth pulse of a received byte: it reaches `RXDR` if that is empty, and stretches SCL
// before the acknowledge pulse until software reads it if not, section 47.4.7.
fn received(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
let reload = cr2::RELOAD.is_set(registers.get(Register::Cr2));
let last = self.stop_requested || (self.left == 1 && !reload);
// A released SDA reads high.
let byte = self
.active_peer()
.map_or(0xFF, |peer| peer.read(last, &mut Timeline::new(context)));
self.left -= 1;
if isr::RXNE.is_set(registers.get(Register::Isr)) {
self.shifted = Some(byte);
self.state = State::Stretching;
} else {
registers.set(Register::Rxdr, u64::from(byte));
flag(registers, isr::RXNE, true);
self.run(Step::Acknowledged, ACKNOWLEDGE_PERIODS, registers, context);
}
}
fn event(&mut self, step: Step, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
match step {
Step::Address(sent) | Step::Resend(sent) => {
self.address_done(sent, registers, context);
}
Step::Written(byte) => self.written(byte, registers, context),
Step::Received => self.received(registers, context),
Step::Acknowledged => self.continue_transfer(registers, context),
}
}
// `PE` cleared, section 47.4.6: the lines released, the state machines reset, and the
// communication control bits and status bits back at their reset values, the configuration
// kept.
fn disable(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
self.release(context);
self.forget_transfer();
self.wire.ended = None;
clear_cr2(registers, cr2::START.mask() | CR2_CLEARED_BY_STOP);
registers.set(Register::Isr, isr::TXE.mask());
}
fn write_cr1(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let enabled = cr1::PE.is_set(self.last_cr1);
let mut kept = value;
if enabled && cr1::PE.is_set(value) && (value ^ self.last_cr1) & CR1_WHILE_DISABLED != 0 {
// 47.9.1: these can be programmed only with PE = 0, so the write is dropped.
context.tell("CR1", FILTERS_WHILE_ENABLED);
kept = (value & !CR1_WHILE_DISABLED) | (self.last_cr1 & CR1_WHILE_DISABLED);
registers.restore(Register::Cr1, CR1_WHILE_DISABLED, self.last_cr1);
}
if cr1::NOSTRETCH.is_set(kept) && !cr1::NOSTRETCH.is_set(self.last_cr1) {
context.tell("CR1", NOSTRETCH_IN_CONTROLLER);
}
if enabled && !cr1::PE.is_set(value) {
self.disable(registers, context);
}
self.last_cr1 = kept;
}
fn write_cr2(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let last = self.last_cr2;
self.last_cr2 = value;
if !cr1::PE.is_set(registers.get(Register::Cr1)) {
if cr2::START.is_set(value) {
context.tell("CR2", START_WHILE_DISABLED);
}
clear_cr2(registers, cr2::START.mask() | CR2_CLEARED_BY_STOP);
return;
}
if self.start != Start::Clear && (value ^ last) & ADDRESSING != 0 {
context.tell("CR2", ADDRESSING_WHILE_STARTING);
}
if self.state == State::Reloading && cr2::NBYTES.get(value) != 0 {
self.reload(registers, context);
}
if cr2::START.is_set(value) && self.start == Start::Clear {
self.request_start(registers, context);
}
if cr2::STOP.is_set(value) && !self.stop_requested {
self.request_stop(registers, context);
}
}
fn write_timingr(
&mut self,
value: u64,
registers: &RegisterFile,
context: &mut DeviceContext<'_>,
) {
let enabled = cr1::PE.is_set(registers.get(Register::Cr1));
if enabled && (value ^ self.last_timingr) & TIMING != 0 {
context.tell("TIMINGR", TIMING_WHILE_ENABLED);
}
self.last_timingr = value;
}
fn write_isr(&mut self, value: u64, registers: &mut RegisterFile) {
if !cr1::PE.is_set(registers.get(Register::Cr1)) {
registers.set(Register::Isr, isr::TXE.mask());
return;
}
if isr::TXE.is_set(value) {
self.txdr_full = false;
}
self.sync_transmit_flags(registers);
}
fn write_icr(&mut self, value: u64, registers: &mut RegisterFile) {
for (clear, flagged) in [
(icr::ADDRCF, isr::ADDR),
(icr::NACKCF, isr::NACKF),
(icr::STOPCF, isr::STOPF),
(icr::BERRCF, isr::BERR),
(icr::ARLOCF, isr::ARLO),
(icr::OVRCF, isr::OVR),
(icr::PECCF, isr::PECERR),
(icr::TIMOUTCF, isr::TIMEOUT),
(icr::ALERTCF, isr::ALERT),
] {
if clear.is_set(value) {
flag(registers, flagged, false);
}
}
if icr::ADDRCF.is_set(value) {
// 47.4.9: ADDRCF quits the loop that resends a 10-bit header, and 47.9.8 clears START.
if matches!(
self.state,
State::OnWire(Wait {
step: Step::Resend(_),
..
})
) {
self.state = State::Idle;
}
self.clear_start(registers);
}
}
fn write_txdr(
&mut self,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if !cr1::PE.is_set(registers.get(Register::Cr1)) {
registers.set(Register::Isr, isr::TXE.mask());
return;
}
if self.txdr_full {
// 47.9.11: TXDATA can be written only when TXE = 1, so the write is dropped.
context.tell("TXDR", TXDR_WHILE_FULL);
registers.set(Register::Txdr, u64::from(self.held));
return;
}
self.held = u8::try_from(txdr::TXDATA.get(value)).expect("eight bits");
self.txdr_full = true;
self.txis = false;
if self.state == State::AwaitingData {
self.send(registers, context);
}
self.sync_transmit_flags(registers);
}
}
impl Device for I2c {
type Register = Register;
// The kernel clock has changed. A step on the wire keeps the periods it had left, so those
// finish at the new rate from this instant, and a stopped clock holds it where it stands until
// the clock returns, section 47.4.3.
fn on_clock(
&mut self,
hz: u64,
_registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let State::OnWire(wait) = self.state else {
self.kernel_hz = hz;
self.wire.ended = None;
return;
};
let remaining = self
.wire
.ticks
.saturating_sub(self.elapsed_ticks(context.now()));
self.wire = Wire {
origin: context.now(),
ticks: remaining,
ended: None,
};
self.kernel_hz = hz;
let event = self.arm(context);
self.state = State::OnWire(Wait { event, ..wait });
}
fn read(
&mut self,
register: Register,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
match register {
// Section 47.9.10: reading `RXDR` clears `RXNE`, and releases a stretch that waited for it.
Register::Rxdr => {
let value = registers.get(Register::Rxdr);
flag(registers, isr::RXNE, false);
if self.state == State::Stretching {
let byte = self.shifted.take().expect("a stretch holds its byte");
registers.set(Register::Rxdr, u64::from(byte));
flag(registers, isr::RXNE, true);
self.run(Step::Acknowledged, ACKNOWLEDGE_PERIODS, registers, context);
}
self.refresh(registers, context);
Some(value)
}
// Sections 47.9.1 to 47.9.9 and 47.9.11: the register file answers.
Register::Cr1
| Register::Cr2
| Register::Oar1
| Register::Oar2
| Register::Timingr
| Register::Timeoutr
| Register::Isr
| Register::Icr
| Register::Pecr
| Register::Txdr => None,
}
}
fn write(
&mut self,
register: Register,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
match register {
Register::Cr1 => self.write_cr1(value, registers, context),
Register::Cr2 => self.write_cr2(value, registers, context),
Register::Timingr => self.write_timingr(value, registers, context),
Register::Isr => self.write_isr(value, registers),
Register::Icr => self.write_icr(value, registers),
Register::Txdr => self.write_txdr(value, registers, context),
// Target mode, SMBus timeouts and the read-only registers: stored, or ignored by the file.
Register::Oar1
| Register::Oar2
| Register::Timeoutr
| Register::Pecr
| Register::Rxdr => {}
}
self.refresh(registers, context);
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if let State::OnWire(wait) = self.state
&& wait.event == Some(event)
{
self.wire.ended = Some(context.now());
self.state = State::Idle;
self.event(wait.step, registers, context);
self.refresh(registers, context);
}
for place in &mut self.peers {
place
.chip_mut()
.on_event(event, &mut Timeline::new(context));
}
}
fn reset(&mut self) {
self.forget_transfer();
self.wire.ended = None;
self.last_cr1 = 0;
self.last_cr2 = 0;
self.last_timingr = 0;
self.interrupt = [Level::Low; 2];
self.requests = [Level::Low; 2];
self.peers.iter_mut().for_each(Place::release);
self.active = None;
}
/// The interface's reset line, section 8.4.3: the interface goes back as a reset puts it, and
/// each request the multiplexer heard rise falls at once.
fn reset_line(&mut self, _registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
context.lower_requests(&self.requests);
self.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::device::{InterruptChange, RequestChange, ResetCause, Told};
use crate::mcu::{Mcu, Operate, Single};
use crate::snapshot;
use crate::soc::stm32h743::builder;
use crate::soc::stm32h743::generated::i2c1::{oar1, oar2, rxdr, timeoutr};
use crate::soc::stm32h743::generated::{I2C1, I2C2, I2C3, I2C4};
use crate::soc::{Layout, Peripheral, Width};
const START_AT: VirtualNanos = VirtualNanos(10_000);
const BASE_HZ: u64 = 50_000_000;
const BASE_TICK_NANOS: u64 = 20;
const BASE_SCL_TICKS: u64 = 40;
const SCL_NANOS: u64 = BASE_SCL_TICKS * BASE_TICK_NANOS;
const BYTE_NANOS: u64 = 9 * SCL_NANOS;
const EIGHTH_NANOS: u64 = 8 * SCL_NANOS;
const LATENCY: u64 = 1_000;
const ADDRESS: u8 = 0x50;
const NOBODY_HOME: u8 = 0x3C;
// A 10-bit address, section 47.4.9: past the 7-bit range, with 1 as its top two bits and 0x45 as its
// second byte.
const TEN_BIT_ADDRESS: u16 = 0x145;
const ARDUPILOT_100_KHZ: u64 = 0x0070_7CBB;
const ARDUPILOT_KERNEL_HZ: u64 = 32_000_000;
const ARDUPILOT_SCL_TICKS: u64 = 4 + 188 + 125;
fn timing(prescaler: u64, scll: u64, sclh: u64, sdadel: u64, scldel: u64) -> u64 {
[
(timingr::PRESC, prescaler),
(timingr::SCLL, scll),
(timingr::SCLH, sclh),
(timingr::SDADEL, sdadel),
(timingr::SCLDEL, scldel),
]
.iter()
.fold(0, |value, (field, setting)| field.set(value, *setting))
}
fn base_timing() -> u64 {
timing(0, 0x13, 0x0F, 2, 4)
}
fn sadd_of(address: u8) -> u64 {
SADD_ADDRESS.set(0, u64::from(address))
}
fn request(address: u8, read: bool, nbytes: u64, extra: u64) -> u64 {
let value = SADD.set(0, sadd_of(address));
cr2::NBYTES.set(cr2::RD_WRN.set(value, u64::from(read)), nbytes) | extra
}
fn ten_bit_request(read: bool, nbytes: u64, extra: u64) -> u64 {
let value = SADD.set(0, u64::from(TEN_BIT_ADDRESS)) | cr2::ADD10.mask();
cr2::NBYTES.set(cr2::RD_WRN.set(value, u64::from(read)), nbytes) | extra
}
fn at(offset: u64) -> VirtualNanos {
VirtualNanos(START_AT.0 + offset)
}
fn wire(origin: VirtualNanos, periods: u64, ticks: u64, hz: u64) -> VirtualNanos {
let nanos = u128::from(periods * ticks) * 1_000_000_000 / u128::from(hz);
VirtualNanos(origin.0 + u64::try_from(nanos).expect("a short span"))
}
fn pattern(register: usize) -> u8 {
u8::try_from(register).expect("a register index") ^ 0xA5
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum Heard {
Start { address: u8, read: bool },
Write(u8),
Read { last: bool },
Stop,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Chip {
address: u8,
memory: Vec<u8>,
pointer: usize,
pointer_next: bool,
accepts: usize,
heard: Vec<(VirtualNanos, Heard)>,
}
store!(Chip);
impl Chip {
fn at(address: u8) -> Self {
Self {
address,
memory: (0..256).map(pattern).collect(),
pointer: 0,
pointer_next: false,
accepts: usize::MAX,
heard: Vec::new(),
}
}
fn accepting(address: u8, bytes: usize) -> Self {
Self {
accepts: bytes,
..Self::at(address)
}
}
fn bytes_written(&self) -> Vec<u8> {
self.heard
.iter()
.filter_map(|(_, heard)| match heard {
Heard::Write(byte) => Some(*byte),
_ => None,
})
.collect()
}
}
impl Peer for Chip {
fn start(&mut self, address: u8, read: bool, context: &mut Timeline<'_, '_>) -> bool {
if address != self.address {
return false;
}
self.heard
.push((context.now(), Heard::Start { address, read }));
self.pointer_next = !read;
true
}
fn write(&mut self, byte: u8, context: &mut Timeline<'_, '_>) -> bool {
let taken = self.bytes_written().len();
self.heard.push((context.now(), Heard::Write(byte)));
if taken >= self.accepts {
return false;
}
if self.pointer_next {
self.pointer_next = false;
self.pointer = usize::from(byte);
} else {
self.memory[self.pointer] = byte;
self.pointer = (self.pointer + 1) % self.memory.len();
}
true
}
fn read(&mut self, last: bool, context: &mut Timeline<'_, '_>) -> u8 {
self.heard.push((context.now(), Heard::Read { last }));
let byte = self.memory[self.pointer];
self.pointer = (self.pointer + 1) % self.memory.len();
byte
}
fn stop(&mut self, context: &mut Timeline<'_, '_>) {
self.heard.push((context.now(), Heard::Stop));
}
}
// A chip with a 10-bit address, which acknowledges the header carrying its top two bits and then
// its own second byte, section 47.4.9.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct TenBitChip {
address: u16,
expecting_second: bool,
heard: Vec<(VirtualNanos, Heard)>,
}
store!(TenBitChip);
impl TenBitChip {
fn at(address: u16) -> Self {
Self {
address,
expecting_second: false,
heard: Vec::new(),
}
}
fn top(&self) -> u8 {
u8::try_from((self.address >> 8) & 0b11).expect("two bits")
}
}
impl Peer for TenBitChip {
fn start(&mut self, address: u8, read: bool, context: &mut Timeline<'_, '_>) -> bool {
if address != TEN_BIT_HEADER | self.top() {
return false;
}
self.heard
.push((context.now(), Heard::Start { address, read }));
self.expecting_second = !read;
true
}
fn write(&mut self, byte: u8, context: &mut Timeline<'_, '_>) -> bool {
self.heard.push((context.now(), Heard::Write(byte)));
if self.expecting_second {
self.expecting_second = false;
return u16::from(byte) == self.address & 0xFF;
}
true
}
fn read(&mut self, last: bool, context: &mut Timeline<'_, '_>) -> u8 {
self.heard.push((context.now(), Heard::Read { last }));
0x6C
}
fn stop(&mut self, context: &mut Timeline<'_, '_>) {
self.heard.push((context.now(), Heard::Stop));
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Alarm {
address: u8,
scheduled: Option<EventId>,
due: Option<VirtualNanos>,
heard: Vec<EventId>,
restarted: u32,
}
store!(Alarm);
impl Peer for Alarm {
fn start(&mut self, address: u8, _read: bool, context: &mut Timeline<'_, '_>) -> bool {
if address != self.address {
return false;
}
let due = VirtualNanos(context.now().0 + 250);
self.scheduled = Some(context.schedule(due));
self.due = Some(due);
true
}
fn write(&mut self, _byte: u8, _context: &mut Timeline<'_, '_>) -> bool {
true
}
fn read(&mut self, _last: bool, _context: &mut Timeline<'_, '_>) -> u8 {
0
}
fn stop(&mut self, _context: &mut Timeline<'_, '_>) {}
fn on_event(&mut self, event: EventId, _context: &mut Timeline<'_, '_>) {
self.heard.push(event);
}
fn restarted(&mut self, _context: &mut Timeline<'_, '_>) {
self.restarted += 1;
}
}
struct Rig {
soc: Single<I2c>,
peripheral: Peripheral,
now: VirtualNanos,
}
impl Rig {
fn on(peripheral: Peripheral, kernel_hz: u64) -> Self {
Self {
soc: Single::new(
builder(),
peripheral.name,
I2c::with_kernel_clock(kernel_hz),
)
.expect("the bus builds"),
peripheral,
now: START_AT,
}
}
fn new() -> Self {
Self::on(I2C1, BASE_HZ)
}
fn enabled() -> Self {
let mut rig = Self::new();
rig.soc.model.attach(Chip::at(ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig
}
fn location(&self, register: Register) -> u64 {
self.peripheral.base + u64::from(register.spec().offset)
}
fn read(&mut self, register: Register) -> u64 {
let address = self.location(register);
self.soc
.read(self.now, address, Width::W32)
.expect("the register answers")
}
fn write(&mut self, register: Register, value: u64) {
let address = self.location(register);
self.soc
.write(self.now, address, Width::W32, value)
.expect("the register takes the write");
}
fn or_into(&mut self, register: Register, bits: u64) {
let value = self.read(register);
self.write(register, value | bits);
}
fn and_out(&mut self, register: Register, bits: u64) {
let value = self.read(register);
self.write(register, value & !bits);
}
fn go_to(&mut self, instant: VirtualNanos) {
self.now = instant;
self.soc.advance_to(instant);
}
fn after(&mut self, nanos: u64) {
self.go_to(VirtualNanos(self.now.0 + nanos));
}
fn to(&mut self, offset: u64) {
self.go_to(at(offset));
}
fn status(&mut self, field: &FieldSpec) -> bool {
field.is_set(self.read(Register::Isr))
}
fn control(&mut self, field: &FieldSpec) -> bool {
field.is_set(self.read(Register::Cr2))
}
fn chip(&mut self, nth: usize) -> &mut Chip {
self.soc
.model
.chip_mut::<Chip>(nth)
.expect("a chip is hung there")
}
fn heard(&mut self) -> Vec<(VirtualNanos, Heard)> {
self.chip(0).heard.clone()
}
fn lines(&mut self) -> Vec<(u16, Level)> {
self.soc
.drain_interrupts()
.into_iter()
.map(
|InterruptChange {
interrupt, level, ..
}| (interrupt, level),
)
.collect()
}
fn requests(&mut self) -> Vec<(usize, Level)> {
self.soc
.drain_requests()
.into_iter()
.map(|RequestChange { request, level, .. }| (request, level))
.collect()
}
fn told(&mut self) -> Vec<Told> {
self.soc.drain_told()
}
fn registers(&mut self) -> &mut RegisterFile {
Single::registers(&mut self.soc)
}
fn ten_bit_heard(&mut self) -> Vec<(VirtualNanos, Heard)> {
self.soc
.model
.chip_mut::<TenBitChip>(0)
.expect("a 10-bit chip is hung there")
.heard
.clone()
}
fn alarm(&mut self) -> &mut Alarm {
self.soc
.model
.chip_mut::<Alarm>(0)
.expect("an alarm is hung there")
}
fn raise_flag(&mut self, field: &FieldSpec) {
let value = self.registers().get(Register::Isr);
self.registers().set(Register::Isr, value | field.mask());
}
fn event_line(&self) -> u16 {
self.peripheral.interrupts[EVENT]
}
fn error_line(&self) -> u16 {
self.peripheral.interrupts[ERROR]
}
fn refresh(&mut self) {
self.write(Register::Oar1, 0);
}
fn clock(&mut self, hz: u64) {
self.soc.drive(self.now, |model, registers, context| {
model.on_clock(hz, registers, context);
});
}
fn start(&mut self, address: u8, read: bool, nbytes: u64, extra: u64) {
self.write(
Register::Cr2,
request(address, read, nbytes, extra) | cr2::START.mask(),
);
}
fn feed(&mut self, bytes: &[u8]) {
let mut left = bytes.iter();
loop {
if self.status(isr::TXIS) {
let &byte = left.next().expect("a byte for every TXIS");
self.write(Register::Txdr, u64::from(byte));
} else if let Some(next) = self.soc.next_event() {
self.go_to(next);
} else {
break;
}
}
}
fn write_bytes(&mut self, address: u8, bytes: &[u8], extra: u64) {
let count = u64::try_from(bytes.len()).expect("a short list");
self.start(address, false, count, extra);
self.feed(bytes);
}
}
// Reset values, RM0433 rev 9 section 47.9.12 Table 401.
#[test]
fn every_register_comes_out_of_reset_as_the_manual_gives_it() {
let mut rig = Rig::new();
for register in Register::ALL {
let expected = if register == Register::Isr {
isr::TXE.mask()
} else {
0
};
let value = rig.registers().get(register);
assert_eq!(value, expected, "{register:?}");
}
}
// The corrections the part declares are the rules sections 47.9.2 and 47.9.7 give, on every
// instance.
#[test]
fn the_corrected_fields_are_what_the_part_declares_on_every_instance() {
for instance in &INSTANCES {
let mut rig = Rig::on(
*crate::soc::stm32h743::generated::PERIPHERALS
.iter()
.find(|peripheral| peripheral.name == instance.name)
.expect("an instance of the file"),
BASE_HZ,
);
for (register, name) in [
(Register::Cr2, "START"),
(Register::Cr2, "STOP"),
(Register::Cr2, "NACK"),
(Register::Cr2, "PECBYTE"),
(Register::Isr, "TXE"),
(Register::Isr, "TXIS"),
] {
let fields = rig.registers().fields(register.index());
let field = fields
.iter()
.find(|field| field.name == name)
.expect("a field of the register");
assert_eq!(
field.write,
WriteModify::OneToSet,
"{} {register:?} {name}",
instance.name
);
}
}
}
// The registers this model leaves to the register file keep what they are written, and the ones
// section 47.9 types read-only ignore a write.
#[test]
fn target_and_smbus_registers_store_what_is_written_and_read_only_ones_do_not() {
let mut rig = Rig::new();
rig.write(Register::Oar1, oar1::OA1EN.mask() | oar1::OA1.set(0, 0xA4));
rig.write(Register::Oar2, oar2::OA2EN.mask() | oar2::OA2MSK.set(0, 3));
rig.write(
Register::Timeoutr,
timeoutr::TIMOUTEN.mask() | timeoutr::TIMEOUTA.set(0, 0x61),
);
assert_eq!(
rig.read(Register::Oar1),
oar1::OA1EN.mask() | oar1::OA1.set(0, 0xA4)
);
assert_eq!(
rig.read(Register::Oar2),
oar2::OA2EN.mask() | oar2::OA2MSK.set(0, 3)
);
assert_eq!(
rig.read(Register::Timeoutr),
timeoutr::TIMOUTEN.mask() | timeoutr::TIMEOUTA.set(0, 0x61)
);
rig.write(Register::Pecr, 0xFF);
rig.write(Register::Rxdr, 0xFF);
assert_eq!(rig.read(Register::Pecr), 0);
assert_eq!(rig.read(Register::Rxdr), 0);
}
// Section 47.9.7: `TXE` and `TXIS` are type `rs`, so a written zero leaves them.
#[test]
fn a_written_zero_does_not_clear_txe_or_a_pending_start() {
let mut rig = Rig::enabled();
rig.write(Register::Txdr, 0x42);
assert!(!rig.status(isr::TXE), "TXDR holds the byte");
rig.write(Register::Isr, 0);
assert!(
!rig.status(isr::TXE),
"a written zero does not set it either"
);
rig.start(ADDRESS, false, 1, 0);
rig.write(Register::Cr2, request(ADDRESS, false, 1, 0));
assert!(
rig.control(cr2::START),
"a written zero does not clear START"
);
}
// Sections 47.4.4 and 47.4.9: the address phase of a start is nine SCL periods, `BUSY` is set at once and
// `START` stays set until the address has gone.
#[test]
fn a_start_puts_the_address_on_the_wire_for_nine_scl_periods() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, 0);
assert!(rig.status(isr::BUSY));
assert!(rig.control(cr2::START));
assert!(!rig.status(isr::TXIS));
rig.to(BYTE_NANOS - 1);
assert!(rig.control(cr2::START), "still going out");
assert!(rig.heard().is_empty());
rig.to(BYTE_NANOS);
assert!(
!rig.control(cr2::START),
"hardware clears it once the address is sent"
);
assert!(rig.status(isr::BUSY));
assert_eq!(
rig.heard(),
vec![(
at(BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: false
}
)]
);
}
// Section 47.9.2: `SADD[7:1]` is the address, and bits 9, 8 and 0 are don't care.
#[test]
fn a_seven_bit_address_comes_from_sadd_bits_seven_to_one() {
let mut rig = Rig::enabled();
let noise = cr2::SADD0.mask() | cr2::SADD8.mask() | cr2::SADD9.mask();
rig.start(ADDRESS, false, 1, noise);
rig.to(BYTE_NANOS);
assert_eq!(
rig.heard(),
vec![(
at(BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: false
}
)]
);
}
// Section 47.4.9: after the address is acknowledged in the write direction `TXIS` is set, and
// SCL is stretched until `TXDR` is written: no event is pending.
#[test]
fn an_acknowledged_write_address_sets_txis_and_stretches_scl() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 2, 0);
rig.to(BYTE_NANOS);
assert!(rig.status(isr::TXIS));
assert!(rig.status(isr::TXE));
assert_eq!(rig.soc.next_event(), None, "the wire waits for software");
rig.to(1_000_000);
assert!(rig.status(isr::TXIS), "and goes on waiting");
assert!(rig.status(isr::BUSY));
assert_eq!(
rig.heard().len(),
1,
"nothing but the address reached the chip"
);
}
// Section 47.4.9: a NACK sets `NACKF`, `TXIS` is not set, and the STOP follows by itself, with
// `STOPF` set and `BUSY` clear, whatever `AUTOEND` says.
#[test]
fn an_unanswered_address_sets_nackf_and_stopf_and_frees_the_bus_at_the_ninth_pulse() {
for autoend in [0, cr2::AUTOEND.mask()] {
let mut rig = Rig::enabled();
rig.start(NOBODY_HOME, false, 1, autoend);
rig.to(BYTE_NANOS - 1);
assert!(!rig.status(isr::NACKF));
assert!(rig.status(isr::BUSY));
rig.to(BYTE_NANOS);
assert!(rig.status(isr::NACKF), "autoend {autoend:#x}");
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS));
assert!(!rig.status(isr::TC));
assert!(!rig.control(cr2::START), "cleared whatever the answer");
assert!(
rig.heard().is_empty(),
"the chip at another address heard nothing"
);
}
}
#[test]
fn a_run_of_probes_of_empty_addresses_costs_nine_scl_periods_each() {
let mut rig = Rig::enabled();
let probes = [0x0C, 0x0D, 0x1C, 0x1E, 0x2C, 0x3C, 0x68, 0x77];
for (nth, address) in probes.into_iter().enumerate() {
let began = at(u64::try_from(nth).expect("a short list") * BYTE_NANOS);
assert_eq!(rig.now, began);
assert!(!rig.status(isr::BUSY), "the bus is free for probe {nth}");
rig.start(address, false, 1, 0);
rig.after(BYTE_NANOS);
assert!(rig.status(isr::NACKF), "{address:#04x}");
assert!(!rig.status(isr::BUSY));
rig.write(Register::Icr, icr::NACKCF.mask() | icr::STOPCF.mask());
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(
!rig.control(cr2::STOP),
"there is no transfer for a STOP to end"
);
assert!(!rig.status(isr::NACKF));
assert!(!rig.status(isr::STOPF));
}
assert!(rig.heard().is_empty());
}
// Section 47.4.9: `TXIS` after each address or acknowledged byte, a write to `TXDR` clears it, and
// a byte takes nine SCL periods from the write. `AUTOEND` ends it with the STOP.
#[test]
fn each_byte_written_to_txdr_takes_nine_scl_periods_and_its_acknowledge_asks_for_the_next() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert!(rig.status(isr::TXIS));
let mut written_at = Vec::new();
for (nth, byte) in [0x11, 0x22, 0x33].into_iter().enumerate() {
rig.after(LATENCY);
written_at.push(rig.now);
assert!(rig.status(isr::TXIS), "byte {nth}");
rig.write(Register::Txdr, byte);
assert!(!rig.status(isr::TXIS), "a write to TXDR clears it");
assert!(
rig.status(isr::TXE),
"the byte moved into the shift register"
);
rig.after(BYTE_NANOS - 1);
assert!(!rig.status(isr::STOPF));
rig.after(1);
}
assert!(rig.status(isr::STOPF), "AUTOEND sent the STOP");
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS), "no TXIS after the last byte");
let ends: Vec<VirtualNanos> = written_at
.iter()
.map(|written| VirtualNanos(written.0 + BYTE_NANOS))
.collect();
assert_eq!(
rig.heard(),
vec![
(
at(BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: false
}
),
(ends[0], Heard::Write(0x11)),
(ends[1], Heard::Write(0x22)),
(ends[2], Heard::Write(0x33)),
(ends[2], Heard::Stop),
]
);
}
// Figure 544: `TXDR` can take the next byte while one is on the wire, and moves into the
// shift register at the acknowledge, with no stretch between the two.
#[test]
fn a_byte_written_while_one_is_on_the_wire_follows_it_without_a_stretch() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(Register::Txdr, 0x22);
assert!(!rig.status(isr::TXE), "the second waits in TXDR");
assert!(!rig.status(isr::TXIS));
rig.to(2 * BYTE_NANOS);
assert!(
rig.status(isr::TXE),
"moved into the shift register at the acknowledge"
);
assert!(
rig.status(isr::TXIS),
"TXDR is empty and one more byte is wanted"
);
rig.write(Register::Txdr, 0x33);
rig.to(3 * BYTE_NANOS);
assert!(!rig.status(isr::TXIS));
rig.to(4 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
let heard = rig.heard();
let writes: Vec<_> = heard
.iter()
.filter(|(_, heard)| matches!(heard, Heard::Write(_)))
.cloned()
.collect();
assert_eq!(
writes,
vec![
(at(2 * BYTE_NANOS), Heard::Write(0x11)),
(at(3 * BYTE_NANOS), Heard::Write(0x22)),
(at(4 * BYTE_NANOS), Heard::Write(0x33)),
],
"back to back, a byte period apart"
);
}
// Section 47.9.7: software can set `TXE` to flush `TXDR`. The byte is gone and the next
// acknowledge asks again.
#[test]
fn writing_one_to_txe_flushes_a_byte_waiting_in_txdr() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(Register::Txdr, 0x22);
assert!(!rig.status(isr::TXE));
rig.write(Register::Isr, isr::TXE.mask());
assert!(rig.status(isr::TXE), "flushed");
rig.to(2 * BYTE_NANOS);
assert!(
rig.status(isr::TXIS),
"the next acknowledge asks for a byte"
);
assert_eq!(rig.soc.next_event(), None, "and SCL is stretched for it");
assert_eq!(
rig.chip(0).bytes_written(),
vec![0x11],
"0x22 never reached the chip"
);
}
// Section 47.9.11: `TXDATA` can be written only when `TXE` is set. The write is dropped, `TXDR`
// keeps its byte, and the session is told.
#[test]
fn a_write_to_txdr_while_it_is_full_is_dropped_and_told() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(Register::Txdr, 0x22);
assert!(rig.told().is_empty());
rig.after(100);
rig.write(Register::Txdr, 0x33);
assert_eq!(
rig.told(),
vec![Told {
instance: "I2C1",
register: "TXDR",
reason: TXDR_WHILE_FULL,
at: rig.now,
}]
);
assert_eq!(rig.read(Register::Txdr), 0x22);
rig.to(3 * BYTE_NANOS);
assert_eq!(rig.chip(0).bytes_written(), vec![0x11, 0x22]);
}
// Section 47.4.9: a NACK on a data byte sets `NACKF`, `TXIS` is not set again, and the STOP follows.
#[test]
fn a_nacked_data_byte_sets_nackf_skips_txis_and_stops_the_bus() {
let mut rig = Rig::new();
rig.soc.model.attach(Chip::accepting(ADDRESS, 1));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 3, 0);
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::TXIS));
rig.write(Register::Txdr, 0x22);
rig.to(3 * BYTE_NANOS - 1);
assert!(!rig.status(isr::NACKF));
rig.to(3 * BYTE_NANOS);
assert!(rig.status(isr::NACKF));
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS), "no TXIS after a NACK");
let heard = rig.heard();
assert_eq!(
heard.last(),
Some(&(at(3 * BYTE_NANOS), Heard::Stop)),
"the chip heard the stop"
);
assert_eq!(rig.soc.next_event(), None);
}
#[test]
fn a_transfer_of_zero_bytes_ends_after_the_address() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 0, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS));
assert_eq!(rig.heard().last(), Some(&(at(BYTE_NANOS), Heard::Stop)));
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 0, 0);
rig.to(BYTE_NANOS);
assert!(rig.status(isr::TC));
assert!(rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS));
}
// Section 47.4.9: with `AUTOEND` clear the controller sets `TC` and stretches SCL after the last
// byte, and `STOP` clears `TC` and ends the transfer.
#[test]
fn software_end_sets_tc_and_stretches_until_stop_is_set() {
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[0x11], 0);
assert!(rig.status(isr::TC));
assert!(rig.status(isr::BUSY));
assert!(!rig.status(isr::STOPF));
assert_eq!(
rig.soc.next_event(),
None,
"SCL is stretched while TC stands"
);
rig.to(1_000_000);
assert!(rig.status(isr::TC), "and stays so");
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(!rig.status(isr::TC), "setting STOP clears TC");
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(
!rig.control(cr2::STOP),
"hardware clears STOP at the stop condition"
);
assert_eq!(rig.heard().last(), Some(&(rig.now, Heard::Stop)));
}
// Section 47.4.9: setting `START` at `TC` is the repeated start. It clears `TC`, keeps `BUSY`,
// sends no STOP, and addresses the target `CR2` names then.
#[test]
fn a_start_at_tc_makes_a_repeated_start_with_no_stop_between() {
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[0x0D], 0);
rig.after(LATENCY);
let restart = rig.now;
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
assert!(!rig.status(isr::TC), "setting START clears TC");
assert!(rig.status(isr::BUSY));
assert!(!rig.status(isr::STOPF));
rig.go_to(VirtualNanos(restart.0 + BYTE_NANOS));
assert_eq!(
rig.heard(),
vec![
(
at(BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: false
}
),
(at(2 * BYTE_NANOS), Heard::Write(0x0D)),
(
VirtualNanos(restart.0 + BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: true
}
),
]
);
}
// Section 47.4.9: `RELOAD` raises `TCR` at the end of `NBYTES` with no stop, `AUTOEND` has no
// effect beside it, and writing a non-zero `NBYTES` clears `TCR` and goes on.
#[test]
fn reload_raises_tcr_and_a_non_zero_nbytes_write_resumes_the_transfer() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::TCIE.mask());
rig.write_bytes(
ADDRESS,
&[0x11, 0x22],
cr2::RELOAD.mask() | cr2::AUTOEND.mask(),
);
assert!(rig.status(isr::TCR));
assert!(!rig.status(isr::TC));
assert!(
!rig.status(isr::STOPF),
"AUTOEND has no effect with RELOAD set"
);
assert!(rig.status(isr::BUSY));
assert_eq!(rig.soc.next_event(), None);
let rising = rig.event_line();
assert_eq!(
rig.lines(),
vec![(rising, Level::High)],
"TCIE raises the event line"
);
rig.write(
Register::Cr2,
request(ADDRESS, false, 0, cr2::AUTOEND.mask()),
);
assert!(rig.status(isr::TCR), "a zero NBYTES leaves it");
rig.write(
Register::Cr2,
request(ADDRESS, false, 1, cr2::AUTOEND.mask()),
);
assert!(!rig.status(isr::TCR));
assert!(
rig.status(isr::TXIS),
"the next block asks for its first byte"
);
assert_eq!(rig.lines(), vec![(rising, Level::Low)]);
rig.write(Register::Txdr, 0x33);
rig.after(BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert_eq!(rig.chip(0).bytes_written(), vec![0x11, 0x22, 0x33]);
}
#[test]
fn a_transfer_of_more_than_255_bytes_reloads_and_costs_a_byte_period_each() {
let mut rig = Rig::enabled();
let bytes: Vec<u8> = (0..300_u16)
.map(|n| u8::try_from(n % 251).expect("below 251"))
.collect();
rig.start(ADDRESS, false, 255, cr2::RELOAD.mask());
rig.feed(&bytes[..255]);
assert!(rig.status(isr::TCR));
rig.write(
Register::Cr2,
request(ADDRESS, false, 45, cr2::AUTOEND.mask()),
);
rig.feed(&bytes[255..]);
assert!(rig.status(isr::STOPF));
assert_eq!(rig.chip(0).bytes_written(), bytes);
assert_eq!(rig.now, at(301 * BYTE_NANOS));
assert_eq!(rig.heard().last(), Some(&(rig.now, Heard::Stop)));
}
// Section 47.9.2: `STOP` is "STOP generation after current byte transfer".
#[test]
fn stop_set_while_a_byte_is_on_the_wire_ends_the_bus_after_that_byte() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, 0);
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.after(3_000);
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(rig.control(cr2::STOP), "it stands until the stop condition");
assert!(rig.status(isr::BUSY));
rig.to(2 * BYTE_NANOS - 1);
assert!(!rig.status(isr::STOPF));
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS), "no byte is asked for after a STOP");
assert!(!rig.control(cr2::STOP));
assert_eq!(rig.chip(0).bytes_written(), vec![0x11]);
assert_eq!(rig.heard().last(), Some(&(at(2 * BYTE_NANOS), Heard::Stop)));
assert_eq!(rig.soc.next_event(), None);
}
#[test]
fn stop_set_while_txis_stands_stops_the_bus_at_once() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, 0);
rig.to(BYTE_NANOS);
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.status(isr::TXIS));
assert_eq!(rig.heard().last(), Some(&(at(BYTE_NANOS), Heard::Stop)));
}
// Section 47.9.2: `START` set during a transfer under `AUTOEND` generates the start "once the
// bus is free", which is the instant of the automatic stop.
#[test]
fn a_start_requested_behind_an_automatic_stop_goes_out_once_the_bus_is_free() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.after(2_000);
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
assert!(
!rig.status(isr::STOPF),
"the first transfer is still on the wire"
);
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::STOPF), "it stopped");
assert!(rig.status(isr::BUSY), "and the new transfer has begun");
assert!(rig.control(cr2::START), "its address is going out");
rig.to(3 * BYTE_NANOS);
assert!(!rig.control(cr2::START));
assert_eq!(
rig.heard()[1..],
[
(at(2 * BYTE_NANOS), Heard::Write(0x11)),
(at(2 * BYTE_NANOS), Heard::Stop),
(
at(3 * BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: true
}
),
]
);
}
// Section 47.9.2: `START` set before the end of a software-end segment is the repeated start
// "after the end of the NBYTES transfer", so `TC` is never raised.
#[test]
fn a_start_requested_before_the_end_of_a_software_end_segment_restarts_with_no_tc() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, 0);
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.after(2_000);
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, 0) | cr2::START.mask(),
);
rig.to(2 * BYTE_NANOS);
assert!(!rig.status(isr::TC));
assert!(rig.status(isr::BUSY));
assert!(!rig.status(isr::STOPF));
rig.to(3 * BYTE_NANOS);
assert_eq!(
rig.heard().last(),
Some(&(
at(3 * BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: true
}
))
);
}
// Section 47.9.8: `ADDRCF` also clears `START`, which withdraws a start waiting behind a
// transfer. ES0392 rev 15 2.19.7 says that is where `START` is cleared, and not at an address
// match.
#[test]
fn addrcf_withdraws_a_start_waiting_behind_a_transfer() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
assert!(rig.control(cr2::START));
rig.write(Register::Icr, icr::ADDRCF.mask());
assert!(!rig.control(cr2::START));
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY), "nothing started after the stop");
}
// Section 47.4.7 and Figure 543: a received byte reaches `RXDR` after the eighth pulse, the
// acknowledge pulse follows, and the next byte comes at once. The last byte is not acknowledged.
#[test]
fn a_received_byte_reaches_rxdr_at_the_eighth_pulse_and_the_last_one_is_not_acknowledged() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 2, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert!(!rig.status(isr::RXNE));
rig.to(BYTE_NANOS + EIGHTH_NANOS - 1);
assert!(!rig.status(isr::RXNE));
rig.to(BYTE_NANOS + EIGHTH_NANOS);
assert!(rig.status(isr::RXNE));
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(0)));
assert!(!rig.status(isr::RXNE), "reading RXDR clears it");
rig.to(2 * BYTE_NANOS + EIGHTH_NANOS);
assert!(rig.status(isr::RXNE));
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(1)));
rig.to(3 * BYTE_NANOS - 1);
assert!(!rig.status(isr::STOPF));
rig.to(3 * BYTE_NANOS);
assert!(rig.status(isr::STOPF), "AUTOEND stopped after the NACK");
assert!(!rig.status(isr::BUSY));
assert_eq!(
rig.heard(),
vec![
(
at(BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: true
}
),
(at(BYTE_NANOS + EIGHTH_NANOS), Heard::Read { last: false }),
(
at(2 * BYTE_NANOS + EIGHTH_NANOS),
Heard::Read { last: true }
),
(at(3 * BYTE_NANOS), Heard::Stop),
]
);
}
// Section 47.4.9: in software end mode the last byte is still not acknowledged, `TC` is set, and `STOP`
// ends it, with the byte still there to read.
#[test]
fn a_software_end_read_sets_tc_after_the_nack_and_keeps_the_last_byte() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 1, 0);
rig.to(2 * BYTE_NANOS - 1);
assert!(!rig.status(isr::TC));
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::TC));
assert!(rig.status(isr::BUSY));
assert!(rig.status(isr::RXNE), "unread");
assert_eq!(rig.soc.next_event(), None);
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(!rig.status(isr::TC));
assert!(rig.status(isr::STOPF));
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(0)));
}
// Section 47.4.7: a byte that completes while `RXDR` is still full stretches SCL before the
// acknowledge pulse until `RXDR` is read. The read returns the older byte and lets the newer
// one in.
#[test]
fn an_unread_byte_stretches_scl_before_the_acknowledge_until_rxdr_is_read() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS + EIGHTH_NANOS);
assert!(rig.status(isr::RXNE));
rig.to(2 * BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(
rig.soc.next_event(),
None,
"SCL is stretched before the acknowledge"
);
assert_eq!(
rig.heard().len(),
3,
"the second byte is in the shift register"
);
rig.to(5 * BYTE_NANOS);
assert_eq!(rig.soc.next_event(), None);
assert_eq!(
rig.read(Register::Rxdr),
u64::from(pattern(0)),
"the older byte"
);
assert!(rig.status(isr::RXNE), "the newer byte moved in");
assert_eq!(
rig.soc.next_event(),
Some(VirtualNanos(rig.now.0 + SCL_NANOS)),
"the acknowledge pulse follows one period later"
);
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(1)));
rig.after(SCL_NANOS + EIGHTH_NANOS);
assert!(rig.status(isr::RXNE), "the third byte");
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(2)));
rig.after(SCL_NANOS);
assert!(rig.status(isr::STOPF));
}
#[test]
fn stop_set_during_a_receive_nacks_the_byte_on_the_wire() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 3, 0);
rig.to(BYTE_NANOS + 1_000);
rig.or_into(Register::Cr2, cr2::STOP.mask());
rig.to(BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(
rig.heard().last().map(|(_, heard)| heard),
Some(&Heard::Read { last: true })
);
assert!(!rig.status(isr::STOPF));
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(rig.status(isr::RXNE));
assert_eq!(rig.heard().last(), Some(&(at(2 * BYTE_NANOS), Heard::Stop)));
}
#[test]
fn a_receive_with_reload_acknowledges_the_block_and_raises_tcr() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 2, cr2::RELOAD.mask());
rig.to(BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(0)));
rig.to(2 * BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(1)));
rig.to(3 * BYTE_NANOS);
assert!(rig.status(isr::TCR));
assert!(!rig.status(isr::STOPF));
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, cr2::AUTOEND.mask()),
);
assert!(!rig.status(isr::TCR));
rig.to(4 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
let reads: Vec<bool> = rig
.heard()
.into_iter()
.filter_map(|(_, heard)| match heard {
Heard::Read { last } => Some(last),
_ => None,
})
.collect();
assert_eq!(reads, vec![false, false, true]);
}
const CHIBIOS_INT_MASK: u64 = isr::TCR.mask()
| isr::TC.mask()
| isr::STOPF.mask()
| isr::NACKF.mask()
| isr::ADDR.mask()
| isr::RXNE.mask()
| isr::TXIS.mask();
const CHIBIOS_ISR_LIMIT: usize = 6;
const BAROMETER: u8 = 0x76;
// The register ArduPilot reads first, `DPS280_REG_PID`, and what the DPS310's datasheet gives
// it, V1.2 section 8.10.
const CHIP_ID_REGISTER: u8 = 0x0D;
const CHIP_ID: u8 = 0x10;
fn barometer() -> Chip {
let mut chip = Chip::at(BAROMETER);
chip.memory[usize::from(CHIP_ID_REGISTER)] = CHIP_ID;
chip
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Mode {
Interrupts,
Dma,
}
#[derive(Debug)]
struct Chibios {
mode: Mode,
tx: Vec<u8>,
sent: usize,
wanted: usize,
received: Vec<u8>,
receiving: bool,
failed: bool,
done: bool,
interrupts: usize,
rises: Vec<VirtualNanos>,
}
impl Chibios {
fn new(mode: Mode) -> Self {
Self {
mode,
tx: Vec::new(),
sent: 0,
wanted: 0,
received: Vec::new(),
receiving: false,
failed: false,
done: false,
interrupts: 0,
rises: Vec::new(),
}
}
fn start(&self, rig: &mut Rig) {
rig.and_out(Register::Cr1, cr1::PE.mask());
let dma = if self.mode == Mode::Dma {
cr1::TXDMAEN.mask() | cr1::RXDMAEN.mask()
} else {
0
};
rig.write(Register::Cr1, dma | cr1::ERRIE.mask() | cr1::NACKIE.mask());
rig.write(Register::Timingr, ARDUPILOT_100_KHZ);
rig.or_into(Register::Cr1, cr1::PE.mask());
}
fn setup(&self, rig: &mut Rig, read: bool) {
let left = if read {
self.wanted - self.received.len()
} else {
self.tx.len() - self.sent
};
let (count, reload) = if left > 255 {
(255, cr2::RELOAD.mask())
} else {
(u64::try_from(left).expect("at most 255"), 0)
};
let direction = if read { cr2::RD_WRN.mask() } else { 0 };
let kept = rig.read(Register::Cr2) & !(cr2::NBYTES.mask() | cr2::RELOAD.mask());
rig.write(
Register::Cr2,
kept | direction | cr2::NBYTES.set(0, count) | reload,
);
}
fn master_transmit(&mut self, rig: &mut Rig, address: u8, tx: &[u8], wanted: usize) {
self.tx = tx.to_vec();
self.wanted = wanted;
assert!(
!rig.status(isr::BUSY),
"ChibiOS waits for BUSY to clear first"
);
rig.write(Register::Cr2, sadd_of(address));
self.setup(rig, false);
let enables = if self.mode == Mode::Dma {
cr1::TCIE.mask()
} else {
cr1::TCIE.mask() | cr1::TXIE.mask()
};
rig.or_into(Register::Cr1, enables);
rig.or_into(Register::Cr2, cr2::START.mask());
}
fn serve(&mut self, rig: &mut Rig, status: u64) {
self.interrupts += 1;
let control = rig.read(Register::Cr1);
rig.write(Register::Icr, status & CHIBIOS_INT_MASK);
if isr::NACKF.is_set(status) {
self.failed = true;
rig.or_into(Register::Cr2, cr2::STOP.mask());
rig.and_out(
Register::Cr1,
cr1::TCIE.mask() | cr1::TXIE.mask() | cr1::RXIE.mask(),
);
self.done = true;
return;
}
if self.mode == Mode::Interrupts {
if !self.receiving {
if cr1::TXIE.is_set(control) && isr::TXIS.is_set(status) {
rig.write(Register::Txdr, u64::from(self.tx[self.sent]));
self.sent += 1;
if self.sent == self.tx.len() {
rig.and_out(Register::Cr1, cr1::TXIE.mask());
}
}
} else if cr1::RXIE.is_set(control) && isr::RXNE.is_set(status) {
let byte = rig.read(Register::Rxdr);
self.received
.push(u8::try_from(rxdr::RXDATA.get(byte)).expect("eight bits"));
if self.received.len() == self.wanted {
rig.and_out(Register::Cr1, cr1::RXIE.mask());
}
}
}
if isr::TCR.is_set(status) {
self.setup(rig, self.receiving);
return;
}
if isr::TC.is_set(status) {
if !self.receiving && self.wanted > 0 {
self.setup(rig, true);
if self.mode == Mode::Interrupts {
rig.or_into(Register::Cr1, cr1::RXIE.mask());
}
rig.or_into(Register::Cr2, cr2::START.mask());
self.receiving = true;
return;
}
rig.or_into(Register::Cr2, cr2::STOP.mask());
rig.and_out(Register::Cr1, cr1::TCIE.mask());
self.done = true;
}
}
fn run(&mut self, rig: &mut Rig) {
let mut line = false;
let mut requests = [false; 2];
loop {
for (position, level) in rig.lines() {
if position == rig.event_line() {
if level == Level::High && !line {
self.rises.push(rig.now);
}
line = level == Level::High;
}
}
for (request, level) in rig.requests() {
requests[request] = level == Level::High;
}
if self.mode == Mode::Dma {
if requests[TRANSMIT_REQUEST] && self.sent < self.tx.len() {
rig.write(Register::Txdr, u64::from(self.tx[self.sent]));
self.sent += 1;
continue;
}
if requests[RECEIVE_REQUEST] && self.received.len() < self.wanted {
let byte = rig.read(Register::Rxdr);
self.received
.push(u8::try_from(rxdr::RXDATA.get(byte)).expect("eight bits"));
continue;
}
}
if line {
assert!(
!self.done,
"a stray interrupt after the transfer sends ChibiOS to reset the peripheral"
);
rig.after(LATENCY);
let status = rig.read(Register::Isr);
self.serve(rig, status);
continue;
}
if self.done {
return;
}
let next = rig
.soc
.next_event()
.expect("the transfer waits on an event");
rig.go_to(next);
}
}
}
fn scl(origin: VirtualNanos, periods: u64) -> VirtualNanos {
wire(origin, periods, ARDUPILOT_SCL_TICKS, ARDUPILOT_KERNEL_HZ)
}
fn later(instant: VirtualNanos, nanos: u64) -> VirtualNanos {
VirtualNanos(instant.0 + nanos)
}
#[test]
fn chibios_reads_the_barometers_chip_id_with_these_bytes_at_these_instants() {
let mut rig = Rig::on(I2C2, ARDUPILOT_KERNEL_HZ);
rig.soc.model.attach(barometer());
let mut driver = Chibios::new(Mode::Interrupts);
driver.start(&mut rig);
let began = rig.now;
driver.master_transmit(&mut rig, BAROMETER, &[CHIP_ID_REGISTER], 1);
driver.run(&mut rig);
assert!(!driver.failed);
assert_eq!(driver.received, vec![CHIP_ID]);
let addressed = scl(began, 9);
let wrote_from = later(addressed, LATENCY);
let wrote = scl(wrote_from, 9);
let read_from = later(wrote, LATENCY);
let acknowledged = scl(read_from, 18);
let stopped = later(acknowledged, LATENCY);
assert_eq!(
rig.heard(),
vec![
(
addressed,
Heard::Start {
address: BAROMETER,
read: false
}
),
(wrote, Heard::Write(CHIP_ID_REGISTER)),
(
scl(read_from, 9),
Heard::Start {
address: BAROMETER,
read: true
}
),
(scl(read_from, 17), Heard::Read { last: true }),
(stopped, Heard::Stop),
]
);
assert_eq!(
driver.rises,
vec![addressed, wrote, scl(read_from, 17), acknowledged],
"TXIS, then TC, then RXNE, then TC again"
);
assert_eq!(rig.now, stopped);
assert!(driver.interrupts <= (1 + 1) * CHIBIOS_ISR_LIMIT);
assert_eq!(driver.interrupts, 4);
assert!(!rig.status(isr::BUSY), "the STOP freed the bus");
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::TC));
assert!(
rig.lines().is_empty(),
"the line was already down when the handler left"
);
}
// ChibiOS's DMA path, which ArduPilot's H7 build leaves off (`STM32_I2C_USE_DMA` FALSE): the
// controller asks for the byte out and the byte in through its requests.
#[test]
fn chibios_with_dma_reads_the_chip_id_through_the_two_requests() {
let mut rig = Rig::on(I2C2, ARDUPILOT_KERNEL_HZ);
rig.soc.model.attach(barometer());
let mut driver = Chibios::new(Mode::Dma);
driver.start(&mut rig);
let began = rig.now;
driver.master_transmit(&mut rig, BAROMETER, &[CHIP_ID_REGISTER], 1);
driver.run(&mut rig);
assert!(!driver.failed);
assert_eq!(driver.received, vec![CHIP_ID]);
let wrote = scl(began, 18);
let read_from = later(wrote, LATENCY);
assert_eq!(
rig.heard(),
vec![
(
scl(began, 9),
Heard::Start {
address: BAROMETER,
read: false
}
),
(wrote, Heard::Write(CHIP_ID_REGISTER)),
(
scl(read_from, 9),
Heard::Start {
address: BAROMETER,
read: true
}
),
(scl(read_from, 17), Heard::Read { last: true }),
(later(scl(read_from, 18), LATENCY), Heard::Stop),
],
"the byte went out at the instant TXIS asked, with no handler between"
);
assert_eq!(
driver.interrupts, 2,
"only the two transfer-complete interrupts"
);
}
#[test]
fn chibios_gets_its_nack_from_an_empty_address_in_one_interrupt() {
let mut rig = Rig::on(I2C1, ARDUPILOT_KERNEL_HZ);
for (nth, address) in [0x1E, 0x0D, 0x2C, 0x3C].into_iter().enumerate() {
let mut driver = Chibios::new(Mode::Interrupts);
driver.start(&mut rig);
let began = rig.now;
driver.master_transmit(&mut rig, address, &[0x0A], 1);
driver.run(&mut rig);
assert!(driver.failed, "probe {nth}");
assert!(driver.received.is_empty());
assert_eq!(driver.interrupts, 1);
assert_eq!(
driver.rises,
vec![scl(began, 9)],
"the NACK, nine periods on"
);
assert_eq!(rig.now, later(scl(began, 9), LATENCY));
assert_eq!(
rig.read(Register::Isr),
isr::TXE.mask(),
"the handler cleared NACKF and STOPF, and the bus is free"
);
assert!(!rig.control(cr2::STOP));
}
}
// Section 47.4.9: the 10-bit write sequence is the header, with the two top address bits, then
// the second address byte, and then data.
#[test]
fn a_ten_bit_write_sends_the_header_then_the_second_byte_and_then_data() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
ten_bit_request(false, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
rig.to(BYTE_NANOS);
assert!(rig.control(cr2::START), "the address sequence is not over");
assert!(!rig.status(isr::TXIS));
rig.to(2 * BYTE_NANOS);
assert!(!rig.control(cr2::START));
assert!(rig.status(isr::TXIS));
rig.feed(&[0x99]);
assert_eq!(
rig.ten_bit_heard(),
vec![
(
at(BYTE_NANOS),
Heard::Start {
address: TEN_BIT_HEADER | 1,
read: false
}
),
(at(2 * BYTE_NANOS), Heard::Write(0x45)),
(at(3 * BYTE_NANOS), Heard::Write(0x99)),
(at(3 * BYTE_NANOS), Heard::Stop),
]
);
}
// Section 47.4.9 and Figure 554: with `HEAD10R` clear, a read sends the whole sequence, the
// write header, the second byte, and a repeated start with the header in the read direction.
#[test]
fn a_ten_bit_read_sends_the_write_header_the_second_byte_and_a_read_header() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
ten_bit_request(true, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
rig.to(2 * BYTE_NANOS);
assert!(rig.control(cr2::START), "the read header is still to go");
rig.to(3 * BYTE_NANOS);
assert!(!rig.control(cr2::START));
rig.to(4 * BYTE_NANOS);
assert!(rig.status(isr::STOPF));
assert_eq!(
rig.ten_bit_heard(),
vec![
(
at(BYTE_NANOS),
Heard::Start {
address: TEN_BIT_HEADER | 1,
read: false
}
),
(at(2 * BYTE_NANOS), Heard::Write(0x45)),
(
at(3 * BYTE_NANOS),
Heard::Start {
address: TEN_BIT_HEADER | 1,
read: true
}
),
(
at(3 * BYTE_NANOS + EIGHTH_NANOS),
Heard::Read { last: true }
),
(at(4 * BYTE_NANOS), Heard::Stop),
]
);
}
// Section 47.4.9 and Figure 555: after a write, a repeated start with `HEAD10R` set sends only the
// header in the read direction, with no second address byte.
#[test]
fn head10r_sends_only_the_read_header_for_a_turnaround() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
ten_bit_request(false, 1, 0) | cr2::START.mask(),
);
rig.to(2 * BYTE_NANOS);
rig.feed(&[0x99]);
assert!(rig.status(isr::TC));
let turned = rig.now;
rig.write(
Register::Cr2,
ten_bit_request(true, 1, cr2::AUTOEND.mask() | cr2::HEAD10R.mask()) | cr2::START.mask(),
);
rig.after(BYTE_NANOS);
assert!(
!rig.control(cr2::START),
"a header alone is the whole sequence"
);
let heard = rig.ten_bit_heard();
assert_eq!(
heard[heard.len() - 1],
(
later(turned, BYTE_NANOS),
Heard::Start {
address: TEN_BIT_HEADER | 1,
read: true
}
)
);
assert_eq!(
heard
.iter()
.filter(|(_, heard)| *heard == Heard::Write(0x45))
.count(),
1,
"the second byte went once, with the write"
);
}
// Section 47.4.9: a 10-bit header that is not acknowledged is sent again, nine periods on, until
// it is. ES0392 rev 15 2.19.1 describes a stuck `START` and Table 3 marks it for revisions Y and W
// alone, so this part, revision V, follows the manual.
#[test]
fn a_ten_bit_header_that_is_not_acknowledged_is_resent_until_it_is() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
assert!(rig.soc.model.silence(0, true));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
ten_bit_request(false, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
rig.to(2 * BYTE_NANOS + 1);
assert!(!rig.status(isr::NACKF), "no NACK ends a header");
assert!(rig.status(isr::BUSY));
assert!(rig.control(cr2::START));
assert!(rig.soc.model.silence(0, false));
rig.to(3 * BYTE_NANOS);
assert_eq!(
rig.ten_bit_heard()[0],
(
at(3 * BYTE_NANOS),
Heard::Start {
address: TEN_BIT_HEADER | 1,
read: false
}
)
);
rig.to(4 * BYTE_NANOS);
assert!(
rig.status(isr::TXIS),
"the second byte went and the transfer goes on"
);
}
// Section 47.4.9: setting `ADDRCF` makes the controller quit the loop that resends the header,
// and 47.9.8 clears `START` with it.
#[test]
fn addrcf_quits_the_loop_that_resends_a_ten_bit_header() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
assert!(rig.soc.model.silence(0, true));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
ten_bit_request(false, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
rig.to(BYTE_NANOS + 1_000);
rig.write(Register::Icr, icr::ADDRCF.mask());
assert!(!rig.control(cr2::START));
assert!(rig.soc.model.silence(0, false));
rig.to(10 * BYTE_NANOS);
assert!(
rig.ten_bit_heard().is_empty(),
"nothing is sent after the quit"
);
assert!(!rig.status(isr::NACKF));
}
#[test]
fn an_unacknowledged_second_byte_of_a_ten_bit_address_stops_too() {
let mut rig = Rig::new();
rig.soc.model.attach(TenBitChip::at(TEN_BIT_ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write(
Register::Cr2,
(ten_bit_request(false, 1, cr2::AUTOEND.mask()) & !SADD.mask())
| SADD.set(0, 0x146)
| cr2::START.mask(),
);
rig.to(BYTE_NANOS);
assert!(!rig.status(isr::NACKF), "the header was acknowledged");
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::NACKF));
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert!(!rig.control(cr2::START));
assert!(!rig.status(isr::TXIS));
}
struct TimingCase {
prescaler: u64,
scll: u64,
sclh: u64,
sdadel: u64,
scldel: u64,
filter: u64,
kernel_hz: u64,
// What section 47.4.9 gives, worked by hand.
scl_ticks: u64,
why: &'static str,
}
// Section 47.4.9: `tSCL = tSYNC1 + tSYNC2 + [(SCLH + 1) + (SCLL + 1)] x (PRESC + 1) x tI2CCLK`,
// with the sync delay at the 4 periods Tables 391 to 393 give as its minimum and each digital
// filter delay `DNF x tI2CCLK`, and the low time no shorter than the stretch section 47.4.5
// gives every pulse. The first four cases are columns of Tables 391 and 393.
#[test]
fn the_address_phase_lasts_nine_scl_periods_of_the_period_timingr_gives() {
let cases = [
TimingCase {
why: "Table 391, 10 kHz at 8 MHz: 4 + 200 x 2 + 196 x 2.",
prescaler: 1,
scll: 0xC7,
sclh: 0xC3,
sdadel: 2,
scldel: 4,
filter: 0,
kernel_hz: 8_000_000,
scl_ticks: 796,
},
TimingCase {
why: "Table 391, 100 kHz: 4 + 20 x 2 + 16 x 2.",
prescaler: 1,
scll: 0x13,
sclh: 0x0F,
sdadel: 2,
scldel: 4,
filter: 0,
kernel_hz: 8_000_000,
scl_ticks: 76,
},
TimingCase {
why: "Table 391, 400 kHz: 4 + 10 + 4. The stretch is (1 + 3 + 1) + 1 = 6, under the 10.",
prescaler: 0,
scll: 0x09,
sclh: 0x03,
sdadel: 1,
scldel: 3,
filter: 0,
kernel_hz: 8_000_000,
scl_ticks: 18,
},
TimingCase {
why: "Table 393, Fm+ at 48 MHz: 4 + 4 x 6 + 2 x 6.",
prescaler: 5,
scll: 0x03,
sclh: 0x01,
sdadel: 0,
scldel: 1,
filter: 0,
kernel_hz: 48_000_000,
scl_ticks: 40,
},
TimingCase {
why: "The stretch (15 + 15 + 1) + 1 = 32 is longer than the 1 of the low time: 4 + 32 + 2.",
prescaler: 0,
scll: 0x00,
sclh: 0x01,
sdadel: 15,
scldel: 15,
filter: 0,
kernel_hz: 100_000_000,
scl_ticks: 38,
},
TimingCase {
why: "The 100 kHz case with `DNF` 3: 3 periods on each of the two edges, 76 + 6.",
prescaler: 1,
scll: 0x13,
sclh: 0x0F,
sdadel: 2,
scldel: 4,
filter: 3,
kernel_hz: 8_000_000,
scl_ticks: 82,
},
TimingCase {
why: "The largest prescaler: 4 + 2 x 16 + 2 x 16. The stretch is 17.",
prescaler: 15,
scll: 0x01,
sclh: 0x01,
sdadel: 0,
scldel: 0,
filter: 0,
kernel_hz: 16_000_000,
scl_ticks: 68,
},
TimingCase {
why: "ArduPilot's own, 0x00707CBB at 32 MHz: 4 + 188 + 125.",
prescaler: 0,
scll: 0xBB,
sclh: 0x7C,
sdadel: 0,
scldel: 7,
filter: 0,
kernel_hz: 32_000_000,
scl_ticks: 317,
},
];
for case in cases {
let mut rig = Rig::on(I2C1, case.kernel_hz);
rig.soc.model.attach(Chip::at(ADDRESS));
rig.write(
Register::Timingr,
timing(
case.prescaler,
case.scll,
case.sclh,
case.sdadel,
case.scldel,
),
);
rig.write(Register::Cr1, cr1::PE.mask() | cr1::DNF.set(0, case.filter));
rig.start(ADDRESS, false, 1, 0);
assert_eq!(
rig.soc.next_event(),
Some(wire(START_AT, 9, case.scl_ticks, case.kernel_hz)),
"{}: TIMINGR {:#010x} DNF {} at {} Hz",
case.why,
rig.read(Register::Timingr),
case.filter,
case.kernel_hz
);
}
}
// The model's own default for an interface its part never wired to a clock is the 64 MHz the
// part wakes up on, RM0433 rev 9 section 8.7.
#[test]
fn an_interface_never_wired_to_a_clock_runs_on_the_64_mhz_the_part_wakes_up_on() {
assert_eq!(RESET_KERNEL_HZ, 64_000_000);
let mut rig = Rig {
soc: Single::new(builder(), "I2C1", I2c::default()).expect("the bus builds"),
peripheral: I2C1,
now: START_AT,
};
rig.soc.model.attach(Chip::at(ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 1, 0);
assert_eq!(
rig.soc.next_event(),
Some(wire(START_AT, 9, BASE_SCL_TICKS, 64_000_000))
);
}
#[test]
fn a_run_of_bytes_does_not_drift() {
let mut rig = Rig::on(I2C1, ARDUPILOT_KERNEL_HZ);
rig.soc.model.attach(Chip::at(ADDRESS));
rig.write(Register::Timingr, ARDUPILOT_100_KHZ);
rig.write(Register::Cr1, cr1::PE.mask());
let bytes = vec![0x5A; 100];
rig.write_bytes(ADDRESS, &bytes, cr2::AUTOEND.mask());
assert_eq!(rig.chip(0).bytes_written().len(), 100);
assert_eq!(
rig.now,
wire(START_AT, 9 * 101, ARDUPILOT_SCL_TICKS, ARDUPILOT_KERNEL_HZ),
"one rounding for the whole transfer"
);
let summed: u64 = 101 * (9 * ARDUPILOT_SCL_TICKS * 1_000_000_000 / ARDUPILOT_KERNEL_HZ);
assert_ne!(
rig.now.0 - START_AT.0,
summed,
"and not the sum of 101 rounded bytes"
);
}
#[test]
fn a_change_of_kernel_clock_applies_to_the_periods_a_step_has_left() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS / 2);
rig.clock(2 * BASE_HZ);
let addressed = BYTE_NANOS / 2 + BYTE_NANOS / 4;
rig.to(addressed - 1);
assert!(!rig.status(isr::TXIS));
rig.to(addressed);
assert!(rig.status(isr::TXIS));
rig.write(Register::Txdr, 0x11);
rig.to(addressed + BYTE_NANOS / 2 - 1);
assert!(rig.chip(0).bytes_written().is_empty());
rig.to(addressed + BYTE_NANOS / 2);
assert_eq!(
rig.chip(0).bytes_written(),
vec![0x11],
"the next byte runs at the new rate"
);
rig.to(BYTE_NANOS + 1);
assert_eq!(
rig.chip(0).heard.len(),
3,
"the event of the old rate, at the end of the original nine periods, changed nothing"
);
}
// Section 47.4.3: with the kernel clock stopped nothing moves, and the step carries on from
// where it stood once the clock returns.
#[test]
fn a_stopped_kernel_clock_holds_a_step_until_it_returns() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS / 2);
rig.clock(0);
rig.to(10 * BYTE_NANOS);
assert!(rig.control(cr2::START), "the address is still going out");
assert!(rig.heard().is_empty());
let returned = rig.now;
rig.clock(BASE_HZ);
rig.go_to(VirtualNanos(returned.0 + BYTE_NANOS / 2 - 1));
assert!(rig.heard().is_empty());
rig.go_to(VirtualNanos(returned.0 + BYTE_NANOS / 2));
assert!(rig.status(isr::TXIS));
assert_eq!(rig.heard().len(), 1);
}
// Section 47.7.1: `TXDMAEN` drives the transmit request for as long as `TXIS` stands.
#[test]
fn txdmaen_drives_the_transmit_request_while_txis_stands() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::TXDMAEN.mask());
rig.start(ADDRESS, false, 2, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert_eq!(rig.requests(), vec![(TRANSMIT_REQUEST, Level::High)]);
rig.write(Register::Txdr, 0x11);
assert_eq!(rig.requests(), vec![(TRANSMIT_REQUEST, Level::Low)]);
rig.to(2 * BYTE_NANOS);
assert_eq!(
rig.requests(),
vec![(TRANSMIT_REQUEST, Level::High)],
"the next byte"
);
rig.write(Register::Txdr, 0x22);
assert_eq!(rig.requests(), vec![(TRANSMIT_REQUEST, Level::Low)]);
rig.to(3 * BYTE_NANOS);
assert!(
rig.requests().is_empty(),
"nothing is asked for after the last byte"
);
}
// Section 47.7.2: `RXDMAEN` drives the receive request for as long as `RXNE` stands.
#[test]
fn rxdmaen_drives_the_receive_request_while_rxne_stands() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::RXDMAEN.mask());
rig.start(ADDRESS, true, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(rig.requests(), vec![(RECEIVE_REQUEST, Level::High)]);
rig.read(Register::Rxdr);
assert_eq!(rig.requests(), vec![(RECEIVE_REQUEST, Level::Low)]);
}
// Section 8.4.3: the interface's reset line lowers the transmit request the multiplexer heard rise
// at the reset itself, so the refresh after has nothing left to lower.
#[test]
fn a_reset_line_lowers_the_request_the_multiplexer_heard_at_once() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::TXDMAEN.mask());
rig.start(ADDRESS, false, 2, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert_eq!(rig.requests(), vec![(TRANSMIT_REQUEST, Level::High)]);
let slot = rig
.soc
.bus()
.slot_of(rig.peripheral.name)
.expect("an instance");
rig.soc.bus_mut().reset_slot(slot);
rig.soc.drive(rig.now, |i2c, registers, context| {
i2c.reset_line(registers, context);
});
assert_eq!(rig.requests(), vec![(TRANSMIT_REQUEST, Level::Low)]);
rig.write(Register::Cr1, cr1::TXDMAEN.mask());
assert!(rig.requests().is_empty(), "nothing is left to lower");
}
#[test]
fn without_their_enables_the_dma_requests_stay_down() {
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[0x11, 0x22], cr2::AUTOEND.mask());
rig.start(ADDRESS, true, 1, cr2::AUTOEND.mask());
rig.after(2 * BYTE_NANOS);
assert!(rig.requests().is_empty());
}
// Section 47.6 and Figure 569: the event line is the OR of `TCR` or `TC` under `TCIE`, and
// `TXIS`, `RXNE`, `STOPF`, `ADDR` and `NACKF` each under its own enable, and nothing else raises
// it. A flag with its enable clear raises nothing.
#[test]
fn the_event_line_follows_each_flag_under_its_own_enable() {
let sources = [
(cr1::TCIE, isr::TC),
(cr1::TCIE, isr::TCR),
(cr1::TXIE, isr::TXIS),
(cr1::RXIE, isr::RXNE),
(cr1::STOPIE, isr::STOPF),
(cr1::ADDRIE, isr::ADDR),
(cr1::NACKIE, isr::NACKF),
];
for (enable, flagged) in sources {
let mut rig = Rig::enabled();
rig.raise_flag(flagged);
rig.write(Register::Cr1, cr1::PE.mask());
assert!(rig.lines().is_empty(), "{} alone", flagged.name);
rig.write(Register::Cr1, cr1::PE.mask() | enable.mask());
assert_eq!(
rig.lines(),
vec![(rig.event_line(), Level::High)],
"{} under {}",
flagged.name,
enable.name
);
rig.write(Register::Cr1, cr1::PE.mask());
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::Low)]);
}
}
// Section 47.6: the error line is any of the six error flags under `ERRIE`, and it is the
// instance's second line, so a layout shared by four instances raises each one's own.
#[test]
fn the_error_line_follows_any_error_flag_under_erie_at_position_one() {
for flagged in [
isr::BERR,
isr::OVR,
isr::ARLO,
isr::TIMEOUT,
isr::ALERT,
isr::PECERR,
] {
let mut rig = Rig::enabled();
assert_ne!(rig.error_line(), rig.event_line());
rig.raise_flag(flagged);
rig.write(Register::Cr1, cr1::PE.mask());
assert!(rig.lines().is_empty(), "{} with ERRIE clear", flagged.name);
rig.write(Register::Cr1, cr1::PE.mask() | cr1::ERRIE.mask());
assert_eq!(
rig.lines(),
vec![(rig.error_line(), Level::High)],
"{}",
flagged.name
);
rig.write(Register::Cr1, cr1::PE.mask());
assert_eq!(rig.lines(), vec![(rig.error_line(), Level::Low)]);
}
}
// Section 47.9.8: each bit of `ICR` written one clears its own flag in `ISR` and no other, and
// the register reads zero.
#[test]
fn icr_clears_each_flag_written_one_and_leaves_the_others() {
let pairs = [
(icr::ADDRCF, isr::ADDR),
(icr::NACKCF, isr::NACKF),
(icr::STOPCF, isr::STOPF),
(icr::BERRCF, isr::BERR),
(icr::ARLOCF, isr::ARLO),
(icr::OVRCF, isr::OVR),
(icr::PECCF, isr::PECERR),
(icr::TIMOUTCF, isr::TIMEOUT),
(icr::ALERTCF, isr::ALERT),
];
let all: u64 = pairs.iter().map(|(_, flagged)| flagged.mask()).sum();
for (clear, flagged) in pairs {
let mut rig = Rig::enabled();
for (_, other) in pairs {
rig.raise_flag(other);
}
rig.write(Register::Icr, clear.mask());
assert_eq!(
rig.read(Register::Isr),
isr::TXE.mask() | (all & !flagged.mask()),
"{} clears {} alone",
clear.name,
flagged.name
);
assert_eq!(rig.read(Register::Icr), 0);
}
}
#[test]
fn icr_bits_that_name_no_flag_clear_nothing() {
let mut rig = Rig::enabled();
rig.write(Register::Txdr, 0x42);
rig.write(
Register::Icr,
u64::from(u32::MAX) & !CHIBIOS_INT_MASK | isr::TXE.mask(),
);
assert!(!rig.status(isr::TXE), "TXDR still holds its byte");
rig.write(
Register::Icr,
CHIBIOS_INT_MASK & !(icr::ADDRCF.mask() | icr::NACKCF.mask() | icr::STOPCF.mask()),
);
assert!(!rig.status(isr::TXE));
}
// Section 47.6: a NACK raises the event line under `NACKIE` and `NACKCF` lowers it.
#[test]
fn a_nack_raises_the_event_line_under_nackie_and_nackcf_lowers_it() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::NACKIE.mask());
rig.start(NOBODY_HOME, false, 1, 0);
rig.to(BYTE_NANOS - 1);
assert!(rig.lines().is_empty());
rig.to(BYTE_NANOS);
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::High)]);
rig.write(Register::Icr, icr::NACKCF.mask());
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::Low)]);
}
// Section 47.6: the stop condition raises the event line under `STOPIE`.
#[test]
fn a_stop_raises_the_event_line_under_stopie_and_stopcf_lowers_it() {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | cr1::STOPIE.mask());
rig.write_bytes(ADDRESS, &[0x11], cr2::AUTOEND.mask());
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::High)]);
rig.write(Register::Icr, icr::STOPCF.mask());
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::Low)]);
}
// Section 47.6: `TXIS` and `RXNE` raise the event line under `TXIE` and `RXIE`, and the data
// access that clears each lowers it.
#[test]
fn txis_and_rxne_raise_the_event_line_under_txie_and_rxie() {
let mut rig = Rig::enabled();
rig.write(
Register::Cr1,
cr1::PE.mask() | cr1::TXIE.mask() | cr1::RXIE.mask(),
);
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::High)]);
rig.write(Register::Txdr, 0x11);
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::Low)]);
rig.to(2 * BYTE_NANOS);
rig.start(ADDRESS, true, 1, cr2::AUTOEND.mask());
rig.after(BYTE_NANOS + EIGHTH_NANOS);
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::High)]);
rig.read(Register::Rxdr);
assert_eq!(rig.lines(), vec![(rig.event_line(), Level::Low)]);
}
// Section 47.9.1: `NOSTRETCH` must be kept cleared in controller mode, so setting it is told, and
// a written `TXIS` does nothing, since 47.9.7 allows it only with `NOSTRETCH` set.
#[test]
fn txis_written_one_does_nothing_and_nostretch_is_told() {
let mut rig = Rig::enabled();
rig.write(Register::Isr, isr::TXIS.mask());
assert!(!rig.status(isr::TXIS));
rig.write(Register::Cr1, 0);
rig.write(Register::Cr1, cr1::NOSTRETCH.mask());
assert_eq!(rig.told().len(), 1);
rig.write(Register::Cr1, cr1::PE.mask() | cr1::NOSTRETCH.mask());
rig.write(Register::Isr, isr::TXIS.mask());
assert!(!rig.status(isr::TXIS));
}
// Section 47.9.5: `TIMINGR` must be configured with `PE` clear. A change with `PE` set lands and the
// session is told. Writing the same value is not a change.
#[test]
fn timingr_changed_with_pe_set_is_told_and_a_rewrite_of_the_same_value_is_not() {
let mut rig = Rig::enabled();
rig.write(Register::Timingr, base_timing());
assert!(rig.told().is_empty());
rig.write(Register::Timingr, ARDUPILOT_100_KHZ);
assert_eq!(
rig.told(),
vec![Told {
instance: "I2C1",
register: "TIMINGR",
reason: TIMING_WHILE_ENABLED,
at: rig.now,
}]
);
assert_eq!(
rig.read(Register::Timingr),
ARDUPILOT_100_KHZ,
"the write landed"
);
}
// Section 47.9.1: `DNF`, `ANFOFF` and `NOSTRETCH` can be programmed only with `PE` clear, so a
// change with `PE` set is dropped and told, and the other bits of `CR1` change freely.
#[test]
fn the_filter_and_stretch_bits_changed_with_pe_set_are_dropped_and_told() {
let mut rig = Rig::enabled();
rig.write(
Register::Cr1,
cr1::PE.mask() | cr1::TXIE.mask() | cr1::ERRIE.mask(),
);
assert!(rig.told().is_empty(), "interrupt enables change freely");
for field in [cr1::DNF, cr1::ANFOFF, cr1::NOSTRETCH] {
let mut rig = Rig::enabled();
rig.write(Register::Cr1, cr1::PE.mask() | field.mask());
assert!(!rig.told().is_empty(), "{}", field.name);
assert_eq!(
rig.read(Register::Cr1),
cr1::PE.mask(),
"{} was dropped",
field.name
);
}
let mut rig = Rig::new();
rig.write(Register::Cr1, cr1::DNF.set(0, 4));
assert!(
rig.told().is_empty(),
"with PE clear they are free to change"
);
assert_eq!(cr1::DNF.get(rig.read(Register::Cr1)), 4);
}
// Section 47.9.2: the addressing fields must not change while `START` is set.
#[test]
fn addressing_fields_changed_while_start_is_set_are_told() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, 0);
rig.write(Register::Cr2, request(ADDRESS, false, 1, 0));
assert!(rig.told().is_empty(), "the same fields");
rig.write(Register::Cr2, request(ADDRESS, true, 4, 0));
assert_eq!(
rig.told(),
vec![Told {
instance: "I2C1",
register: "CR2",
reason: ADDRESSING_WHILE_STARTING,
at: rig.now,
}]
);
rig.to(BYTE_NANOS);
rig.write(Register::Cr2, request(ADDRESS, false, 2, 0));
assert!(rig.told().is_empty(), "START has cleared");
}
// Section 47.9.2: hardware clears `START` while `PE` is clear, so a start asked for then goes
// nowhere and the session is told.
#[test]
fn a_start_with_pe_clear_starts_nothing_and_is_told() {
let mut rig = Rig::new();
rig.soc.model.attach(Chip::at(ADDRESS));
rig.start(ADDRESS, false, 1, 0);
assert_eq!(
rig.told(),
vec![Told {
instance: "I2C1",
register: "CR2",
reason: START_WHILE_DISABLED,
at: rig.now,
}]
);
assert!(!rig.control(cr2::START));
assert!(!rig.status(isr::BUSY));
assert_eq!(rig.soc.next_event(), None);
}
// Section 47.4.6: clearing `PE` releases the lines, resets the state machines, and puts the
// communication control bits and the status bits back at reset, and the configuration stays.
// What was on the wire never completes.
#[test]
fn clearing_pe_resets_the_transfer_and_the_flags_and_keeps_the_configuration() {
let mut rig = Rig::enabled();
rig.write(
Register::Cr1,
cr1::PE.mask() | cr1::TXIE.mask() | cr1::TCIE.mask(),
);
rig.start(NOBODY_HOME, false, 1, 0);
rig.after(BYTE_NANOS);
assert!(rig.status(isr::NACKF) && rig.status(isr::STOPF));
let flags = rig.read(Register::Isr);
assert_ne!(flags, isr::TXE.mask());
rig.start(ADDRESS, false, 3, 0);
rig.after(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(Register::Txdr, 0x22);
rig.or_into(
Register::Cr2,
cr2::STOP.mask() | cr2::NACK.mask() | cr2::PECBYTE.mask(),
);
assert!(rig.status(isr::BUSY));
rig.write(Register::Cr1, cr1::TXIE.mask() | cr1::TCIE.mask());
assert_eq!(
rig.read(Register::Isr),
isr::TXE.mask(),
"every status bit at reset, TXE set"
);
let cleared = cr2::START.mask() | cr2::STOP.mask() | cr2::NACK.mask() | cr2::PECBYTE.mask();
let cr2_now = rig.read(Register::Cr2);
assert_eq!(cr2_now & cleared, 0);
assert_eq!(
SADD.get(cr2_now),
sadd_of(ADDRESS),
"the addressing is configuration"
);
assert_eq!(cr2::NBYTES.get(cr2_now), 3);
assert_eq!(rig.read(Register::Timingr), base_timing());
assert_eq!(rig.read(Register::Cr1), cr1::TXIE.mask() | cr1::TCIE.mask());
assert_eq!(
rig.heard().last().map(|(_, heard)| heard),
Some(&Heard::Stop)
);
let heard = rig.heard().len();
rig.after(1_000_000);
assert_eq!(
rig.heard().len(),
heard,
"the byte on the wire never finished"
);
assert_eq!(rig.read(Register::Isr), isr::TXE.mask());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 1, 0);
rig.after(BYTE_NANOS);
assert!(
rig.status(isr::TXIS),
"a new transfer begins from the beginning"
);
}
// Section 47.4.6 and the bit descriptions: with `PE` clear `NACK`, `STOP` and `PECBYTE` are
// cleared by hardware, and a byte written to `TXDR` leaves `TXE` set.
#[test]
fn control_bits_and_txdr_written_with_pe_clear_leave_the_reset_state() {
let mut rig = Rig::new();
rig.write(
Register::Cr2,
cr2::NACK.mask() | cr2::STOP.mask() | cr2::PECBYTE.mask() | cr2::NBYTES.set(0, 5),
);
let value = rig.read(Register::Cr2);
assert_eq!(value, cr2::NBYTES.set(0, 5));
rig.write(Register::Txdr, 0x11);
assert_eq!(rig.read(Register::Isr), isr::TXE.mask());
rig.write(Register::Isr, isr::TXIS.mask());
assert_eq!(rig.read(Register::Isr), isr::TXE.mask());
}
#[test]
fn the_first_chip_that_acknowledges_the_address_is_the_one_addressed() {
let mut rig = Rig::new();
rig.soc.model.attach(Chip::at(NOBODY_HOME));
rig.soc.model.attach(Chip::at(ADDRESS));
rig.soc.model.attach(Chip::at(ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.write_bytes(ADDRESS, &[0x11], cr2::AUTOEND.mask());
assert!(rig.chip(0).heard.is_empty());
assert_eq!(rig.chip(1).bytes_written(), vec![0x11]);
assert!(rig.chip(2).heard.is_empty());
}
#[test]
fn a_silent_chip_is_not_acknowledged_and_answers_again_once_let_back() {
let mut rig = Rig::enabled();
assert!(!rig.soc.model.silence(1, true), "nothing hangs there");
assert!(rig.soc.model.silence(0, true));
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert!(rig.status(isr::NACKF));
rig.write(Register::Icr, icr::NACKCF.mask() | icr::STOPCF.mask());
assert!(rig.soc.model.silence(0, false));
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.after(BYTE_NANOS);
assert!(rig.status(isr::TXIS));
}
#[test]
fn a_silence_asked_inside_a_transfer_waits_for_the_stop() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 2, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
assert!(rig.soc.model.silence(0, true));
rig.feed(&[0x11, 0x22]);
assert_eq!(
rig.chip(0).bytes_written(),
vec![0x11, 0x22],
"the transfer finished"
);
rig.write(Register::Icr, icr::STOPCF.mask());
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.after(BYTE_NANOS);
assert!(rig.status(isr::NACKF), "and then the chip is silent");
}
fn alarm(address: u8) -> Alarm {
Alarm {
address,
scheduled: None,
due: None,
heard: Vec::new(),
restarted: 0,
}
}
#[test]
fn events_a_chip_scheduled_come_back_to_it_at_their_instant() {
let mut rig = Rig::new();
rig.soc.model.attach(alarm(ADDRESS));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 1, 0);
rig.to(BYTE_NANOS);
let scheduled = rig.alarm().scheduled.expect("started");
assert_eq!(rig.alarm().due, Some(at(BYTE_NANOS + 250)));
assert_eq!(
rig.alarm().heard.len(),
1,
"the controller's own event reached it too"
);
rig.to(BYTE_NANOS + 249);
assert_eq!(rig.alarm().heard.len(), 1);
rig.to(BYTE_NANOS + 250);
assert_eq!(rig.alarm().heard.last(), Some(&scheduled));
assert!(rig.status(isr::TXIS), "and the interface is where it was");
}
#[test]
fn an_event_nothing_waits_for_is_ignored() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, 0);
rig.after(BYTE_NANOS / 2);
rig.write(Register::Cr1, 0);
rig.to(10 * BYTE_NANOS);
assert_eq!(rig.read(Register::Isr), isr::TXE.mask());
assert!(rig.heard().is_empty(), "the address never reached the chip");
}
#[test]
fn a_part_restart_reaches_every_chip_on_the_bus() {
let mut rig = Rig::new();
rig.soc.model.attach(alarm(ADDRESS));
rig.soc.model.attach(alarm(NOBODY_HOME));
rig.soc.drive(rig.now, |model, _registers, context| {
model.chips_restarted(context);
});
assert_eq!(rig.alarm().restarted, 1);
assert_eq!(
rig.soc.model.chip_mut::<Alarm>(1).expect("hung").restarted,
1
);
}
#[test]
fn a_board_gets_a_chip_back_as_the_type_it_hung() {
let mut rig = Rig::enabled();
assert!(rig.soc.model.chip_mut::<Chip>(0).is_some());
assert!(rig.soc.model.chip_mut::<TenBitChip>(0).is_none());
assert!(rig.soc.model.chip_mut::<Chip>(1).is_none());
assert!(rig.soc.model.peer().is_some());
assert!(I2c::default().peer().is_none());
}
#[test]
fn a_fresh_interface_inherits_the_chips_of_the_old_one() {
let mut old = I2c::default();
old.attach(Chip::at(ADDRESS));
assert!(old.silence(0, true));
let mut fresh = I2c::default();
fresh.inherit(&mut old);
assert!(old.peer().is_none());
assert!(fresh.chip_mut::<Chip>(0).is_some());
}
#[test]
fn the_model_state_round_trips_through_a_snapshot() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.write(Register::Txdr, 0x22);
let bytes = snapshot::stored(&rig.soc.model).expect("the model stores itself");
let mut restored = I2c::with_kernel_clock(BASE_HZ);
restored.attach(Chip::at(ADDRESS));
snapshot::load(&mut restored, snapshot::keep(bytes)).expect("and reads itself back");
assert_eq!(format!("{restored:?}"), format!("{:?}", rig.soc.model));
}
#[test]
fn a_reset_puts_the_transfer_back_and_keeps_the_chips() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 3, 0);
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.after(1_000);
Mcu::reset(&mut rig.soc, rig.now, ResetCause::Core);
assert_eq!(
rig.soc.next_event(),
None,
"what was on the wire went with the reset"
);
assert_eq!(rig.read(Register::Isr), isr::TXE.mask());
assert!(rig.soc.model.chip_mut::<Chip>(0).is_some());
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 1, 0);
rig.after(BYTE_NANOS);
assert!(rig.status(isr::TXIS));
}
// The model is the same on every instance of the layout, and each raises its own event line at
// position 0 and its own error line at position 1, which are the pairs of RM0433 rev 9 Table 147.
#[test]
fn every_instance_runs_the_same_model_on_its_own_interrupt_lines() {
for (peripheral, lines) in [
(I2C1, [31, 32]),
(I2C2, [33, 34]),
(I2C3, [72, 73]),
(I2C4, [95, 96]),
] {
assert_eq!(peripheral.interrupts, lines, "{}", peripheral.name);
let mut rig = Rig::on(peripheral, BASE_HZ);
rig.write(Register::Timingr, base_timing());
rig.write(
Register::Cr1,
cr1::PE.mask() | cr1::NACKIE.mask() | cr1::ERRIE.mask(),
);
rig.start(NOBODY_HOME, false, 1, 0);
rig.to(BYTE_NANOS);
assert_eq!(
rig.lines(),
vec![(lines[0], Level::High)],
"{}",
peripheral.name
);
rig.raise_flag(isr::BERR);
rig.refresh();
assert_eq!(
rig.lines(),
vec![(lines[1], Level::High)],
"{}",
peripheral.name
);
}
}
#[test]
fn the_instance_table_names_the_four_instances_of_the_layout() {
// Tables 124 and 128 for the request inputs.
let wired = [
("I2C1", "DMAMUX1", 33, 34),
("I2C2", "DMAMUX1", 35, 36),
("I2C3", "DMAMUX1", 73, 74),
("I2C4", "DMAMUX2", 13, 14),
];
assert_eq!(INSTANCES.len(), wired.len());
for (instance, (name, multiplexer, receive, transmit)) in INSTANCES.iter().zip(wired) {
assert_eq!(
(
instance.name,
instance.multiplexer,
instance.receive_input,
instance.transmit_input
),
(name, multiplexer, receive, transmit)
);
}
for instance in &INSTANCES {
let peripheral = crate::soc::stm32h743::generated::PERIPHERALS
.iter()
.find(|peripheral| peripheral.name == instance.name)
.expect("an instance of the file");
assert!(
std::ptr::eq(peripheral.registers, <Register as Layout>::REGISTERS),
"{} carries the layout this model is written against",
instance.name
);
}
}
fn scripted() -> (Rig, Chibios) {
let mut rig = Rig::on(I2C2, ARDUPILOT_KERNEL_HZ);
rig.soc.model.attach(barometer());
let mut driver = Chibios::new(Mode::Interrupts);
driver.start(&mut rig);
driver.master_transmit(&mut rig, BAROMETER, &[CHIP_ID_REGISTER], 1);
driver.run(&mut rig);
let mut probe = Chibios::new(Mode::Interrupts);
probe.start(&mut rig);
probe.master_transmit(&mut rig, 0x1E, &[0x0A], 1);
probe.run(&mut rig);
rig.start(BAROMETER, false, 2, 0);
rig.after(5_000);
rig.clock(ARDUPILOT_KERNEL_HZ / 2);
rig.after(100_000);
rig.write(Register::Cr1, 0);
rig.after(1_000_000);
(rig, driver)
}
#[test]
fn two_runs_of_one_script_are_the_same_run() {
let (mut first, driver) = scripted();
let (mut second, again) = scripted();
assert_eq!(driver.rises, again.rises);
assert_eq!(driver.received, again.received);
assert_eq!(first.told(), second.told());
assert_eq!(first.lines(), second.lines());
assert_eq!(first.requests(), second.requests());
for register in Register::ALL {
assert_eq!(
first.registers().get(register),
second.registers().get(register),
"{register:?} disagrees between two runs of the same script"
);
}
assert_eq!(first.heard(), second.heard());
assert_eq!(
format!("{:?}", first.soc.model),
format!("{:?}", second.soc.model)
);
}
// Section 47.9.2: a `START` waiting behind a transfer is still asked for when a NACK stops that
// transfer, and goes out once the bus is free.
#[test]
fn a_start_waiting_behind_a_transfer_goes_out_after_a_nack_stops_it() {
let mut rig = Rig::new();
rig.soc.model.attach(Chip::accepting(ADDRESS, 0));
rig.write(Register::Timingr, base_timing());
rig.write(Register::Cr1, cr1::PE.mask());
rig.start(ADDRESS, false, 2, 0);
rig.to(BYTE_NANOS);
rig.write(Register::Txdr, 0x11);
rig.after(2_000);
rig.write(
Register::Cr2,
request(ADDRESS, true, 1, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
rig.to(2 * BYTE_NANOS);
assert!(rig.status(isr::NACKF));
assert!(rig.status(isr::STOPF));
assert!(rig.status(isr::BUSY), "the waiting start took the bus");
assert!(rig.control(cr2::START));
rig.to(3 * BYTE_NANOS);
assert_eq!(
rig.heard().last(),
Some(&(
at(3 * BYTE_NANOS),
Heard::Start {
address: ADDRESS,
read: true
}
))
);
}
// Section 47.9.2: `STOP` is for the controller's transfer. With none open, a `STOP` is not a
// request and the bit reads clear, so a driver that writes it to a bus the NACK already stopped
// leaves nothing standing for the next transfer.
#[test]
fn a_stop_with_no_transfer_open_does_nothing_and_leaves_the_bit_clear() {
let mut rig = Rig::enabled();
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(!rig.control(cr2::STOP));
assert!(!rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
assert_eq!(rig.soc.next_event(), None);
rig.write_bytes(ADDRESS, &[0x11], cr2::AUTOEND.mask());
assert_eq!(
rig.chip(0).bytes_written(),
vec![0x11],
"the next transfer is whole"
);
}
#[test]
fn stop_set_while_tcr_stands_ends_the_transfer() {
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[0x11], cr2::RELOAD.mask());
assert!(rig.status(isr::TCR));
rig.or_into(Register::Cr2, cr2::STOP.mask());
assert!(!rig.status(isr::TCR));
assert!(rig.status(isr::STOPF));
assert!(!rig.status(isr::BUSY));
}
// Section 47.9.2: hardware clears `NACK` and `PECBYTE` at a stop condition.
#[test]
fn the_stop_condition_clears_nack_and_pecbyte() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.or_into(Register::Cr2, cr2::NACK.mask() | cr2::PECBYTE.mask());
assert!(rig.control(cr2::NACK) && rig.control(cr2::PECBYTE));
rig.feed(&[0x11]);
assert!(rig.status(isr::STOPF));
assert!(!rig.control(cr2::NACK) && !rig.control(cr2::PECBYTE));
}
#[test]
fn a_debuggers_peek_of_rxdr_does_not_clear_rxne() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, true, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS + EIGHTH_NANOS);
let address = rig.location(Register::Rxdr);
let peeked = rig
.soc
.peek(rig.now, address, Width::W32)
.expect("the register answers");
assert_eq!(peeked, u64::from(pattern(0)));
assert!(rig.status(isr::RXNE));
assert_eq!(rig.read(Register::Rxdr), u64::from(pattern(0)));
assert!(!rig.status(isr::RXNE));
}
#[test]
fn nbytes_reads_back_as_written_through_a_transfer() {
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[1, 2, 3], cr2::AUTOEND.mask());
assert_eq!(cr2::NBYTES.get(rig.read(Register::Cr2)), 3);
}
#[test]
fn a_transfer_never_sets_the_target_mode_flags() {
let never = isr::ADDR.mask()
| isr::DIR.mask()
| isr::ADDCODE.mask()
| isr::BERR.mask()
| isr::ARLO.mask()
| isr::OVR.mask()
| isr::PECERR.mask()
| isr::TIMEOUT.mask()
| isr::ALERT.mask();
let mut rig = Rig::enabled();
rig.write_bytes(ADDRESS, &[0x0D], 0);
rig.write(
Register::Cr2,
request(ADDRESS, true, 2, cr2::AUTOEND.mask()) | cr2::START.mask(),
);
for _ in 0..40 {
rig.after(SCL_NANOS);
assert_eq!(rig.read(Register::Isr) & never, 0);
if rig.status(isr::RXNE) {
rig.read(Register::Rxdr);
}
}
assert!(rig.status(isr::STOPF));
}
#[test]
fn a_clock_change_while_idle_applies_to_the_next_transfer() {
let mut rig = Rig::enabled();
rig.clock(2 * BASE_HZ);
rig.start(ADDRESS, false, 1, 0);
assert_eq!(rig.soc.next_event(), Some(at(BYTE_NANOS / 2)));
}
#[test]
fn a_clock_change_at_the_instant_a_step_ends_applies_to_the_next_step() {
let mut rig = Rig::enabled();
rig.start(ADDRESS, false, 1, cr2::AUTOEND.mask());
rig.to(BYTE_NANOS);
rig.clock(2 * BASE_HZ);
rig.write(Register::Txdr, 0x11);
rig.to(BYTE_NANOS + BYTE_NANOS / 2 - 1);
assert!(rig.chip(0).bytes_written().is_empty());
rig.to(BYTE_NANOS + BYTE_NANOS / 2);
assert_eq!(rig.chip(0).bytes_written(), vec![0x11]);
}
}