| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title I2cDriverHardwareStub.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief Stub hardware implementation for the I2C peripheral (Linux/test builds) | ||
| 5 | // | ||
| 6 | // This file is compiled for Linux/test builds to enable unit testing. | ||
| 7 | // For SAMD21 target builds, I2cDriverHardware.cpp is used instead. | ||
| 8 | // | ||
| 9 | // The stub records the arguments of each hardware call and exposes injectable | ||
| 10 | // interrupt/bus status so unit tests can drive the component's state machine, | ||
| 11 | // ISR error dispatch and telemetry mapping without any real MCU. | ||
| 12 | // ====================================================================== | ||
| 13 | |||
| 14 | #include "fprime-samd/Drv/I2cDriver/I2cDriverHardware.hpp" | ||
| 15 | |||
| 16 | namespace Samd21 { | ||
| 17 | namespace I2cHardware { | ||
| 18 | |||
| 19 | static StubState s_state = {}; | ||
| 20 | |||
| 21 | 175 | StubState& getStubState() { | |
| 22 | 175 | return s_state; | |
| 23 | } | ||
| 24 | |||
| 25 | 39 | void resetStubState() { | |
| 26 | // Assign a fresh value-initialized instance rather than memset: several | ||
| 27 | // members (e.g. SercomKind) are autocoded classes with vtables that | ||
| 28 | // memset would corrupt. | ||
| 29 |
3/5✓ Branch 2 taken 39 times.
✓ Branch 5 taken 39 times.
✓ Branch 9 taken 39 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
|
39 | s_state = StubState{}; |
| 30 | 39 | } | |
| 31 | |||
| 32 | 14 | void fireIsr() { | |
| 33 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (s_state.isr_callback != nullptr) { |
| 34 |
2/2✓ Branch 2 taken 14 times.
✓ Branch 5 taken 14 times.
|
14 | s_state.isr_callback(s_state.isr_data, s_state.isr_sercom); |
| 35 | } | ||
| 36 | 14 | } | |
| 37 | |||
| 38 | 39 | void I2cHal::registerIsr(SercomKind sercom, | |
| 39 | void (*callback)(Fw::PassiveComponentBase*, SercomKind), | ||
| 40 | Fw::PassiveComponentBase* data) { | ||
| 41 | 39 | s_state.isr_callback = callback; | |
| 42 | 39 | s_state.isr_data = data; | |
| 43 | 39 | s_state.isr_sercom = sercom; | |
| 44 | 39 | } | |
| 45 | |||
| 46 | 39 | void I2cHal::configure(SercomKind sercom, | |
| 47 | I2cDriver::SclLowTimeout scl_low_timeout, | ||
| 48 | I2cDriver::InactiveTimeout inactive_timeout, | ||
| 49 | I2cDriver::ClockStretchMode clock_stretch_mode, | ||
| 50 | I2cDriver::Frequency frequency, | ||
| 51 | I2cDriver::ClientSclLowTimeout client_scl_low_timeout, | ||
| 52 | I2cDriver::HostSclLowTimeout host_scl_low_timeout, | ||
| 53 | I2cDriver::SdaHold sda_hold, | ||
| 54 | I2cDriver::PinUsage pin_usage, | ||
| 55 | I2cDriver::RunInStandby run_in_standby) { | ||
| 56 | 39 | s_state.configured = true; | |
| 57 | 39 | s_state.configure_count++; | |
| 58 | 39 | s_state.sercom = sercom; | |
| 59 | 39 | s_state.scl_low_timeout = scl_low_timeout; | |
| 60 | 39 | s_state.inactive_timeout = inactive_timeout; | |
| 61 | 39 | s_state.clock_stretch_mode = clock_stretch_mode; | |
| 62 | 39 | s_state.frequency = frequency; | |
| 63 | 39 | s_state.client_scl_low_timeout = client_scl_low_timeout; | |
| 64 | 39 | s_state.host_scl_low_timeout = host_scl_low_timeout; | |
| 65 | 39 | s_state.sda_hold = sda_hold; | |
| 66 | 39 | s_state.pin_usage = pin_usage; | |
| 67 | 39 | s_state.run_in_standby = run_in_standby; | |
| 68 | 39 | } | |
| 69 | |||
| 70 | 49 | U32 I2cHal::getDataRegisterAddress(SercomKind sercom) { | |
| 71 | 49 | return s_state.data_register_address; | |
| 72 | } | ||
| 73 | |||
| 74 | 28 | I2cInterruptStatus I2cHal::readInterruptStatus(SercomKind sercom) { | |
| 75 | 28 | return s_state.interrupt_status; | |
| 76 | } | ||
| 77 | |||
| 78 | 35 | I2cBusStatus I2cHal::readBusStatus(SercomKind sercom) { | |
| 79 | 35 | return s_state.bus_status; | |
| 80 | } | ||
| 81 | |||
| 82 | 4 | I2cRawRegisters I2cHal::readRawRegisters(SercomKind sercom) { | |
| 83 | 4 | return s_state.raw_registers; | |
| 84 | } | ||
| 85 | |||
| 86 | 4 | void I2cHal::recoverBusToIdle(SercomKind sercom) { | |
| 87 | 4 | s_state.recover_bus_count++; | |
| 88 | |||
| 89 | // Model the write-1-clear recovery: after recovery no interrupt/error flags | ||
| 90 | // remain pending and the bus is IDLE, so the driver can start fresh. | ||
| 91 | 4 | s_state.interrupt_status = I2cInterruptStatus{}; | |
| 92 | 4 | s_state.bus_status.busState = 0x1; // IDLE | |
| 93 | 4 | s_state.bus_status.clockHold = false; | |
| 94 | 4 | s_state.bus_status.masterOnBus = false; | |
| 95 | 4 | s_state.bus_status.slaveOnBus = false; | |
| 96 | 4 | } | |
| 97 | |||
| 98 | 8 | void I2cHal::acknowledgeErrors(SercomKind sercom) { | |
| 99 | 8 | s_state.acknowledge_errors_count++; | |
| 100 | // Model the write-1-clear: once acked, the pending error/flags go away so a | ||
| 101 | // subsequent readInterruptStatus() does not re-report the same error. | ||
| 102 | 8 | s_state.interrupt_status.error = false; | |
| 103 | 8 | s_state.interrupt_status.busError = false; | |
| 104 | 8 | s_state.interrupt_status.arbLost = false; | |
| 105 | 8 | s_state.interrupt_status.lowTimeout = false; | |
| 106 | 8 | s_state.interrupt_status.masterExtTimeout = false; | |
| 107 | 8 | s_state.interrupt_status.slaveExtTimeout = false; | |
| 108 | 8 | s_state.interrupt_status.lengthError = false; | |
| 109 | 8 | } | |
| 110 | |||
| 111 | 7 | void I2cHal::acknowledgeMasterOnBus(SercomKind sercom) { | |
| 112 | 7 | s_state.acknowledge_mb_count++; | |
| 113 | 7 | s_state.interrupt_status.masterOnBus = false; | |
| 114 | 7 | } | |
| 115 | |||
| 116 | 8 | void I2cHal::enableMasterOnBusInterrupt(SercomKind sercom) { | |
| 117 | 8 | s_state.enable_mb_count++; | |
| 118 | 8 | } | |
| 119 | |||
| 120 | 8 | void I2cHal::disableMasterOnBusInterrupt(SercomKind sercom) { | |
| 121 | 8 | s_state.disable_mb_count++; | |
| 122 | 8 | } | |
| 123 | |||
| 124 | 18 | void I2cHal::beginRead(SercomKind sercom, U32 addr, U8 byteCount) { | |
| 125 | 18 | s_state.begin_read_count++; | |
| 126 | 18 | s_state.begin_read_addr = addr; | |
| 127 | 18 | s_state.begin_read_len = byteCount; | |
| 128 | 18 | } | |
| 129 | |||
| 130 | 22 | void I2cHal::beginWrite(SercomKind sercom, U32 addr, U8 byteCount, bool generateStopCondition) { | |
| 131 | 22 | s_state.begin_write_count++; | |
| 132 | 22 | s_state.begin_write_addr = addr; | |
| 133 | 22 | s_state.begin_write_len = byteCount; | |
| 134 | 22 | s_state.begin_write_stop = generateStopCondition; | |
| 135 | 22 | } | |
| 136 | |||
| 137 | } // namespace I2cHardware | ||
| 138 | } // namespace Samd21 | ||
| 139 |