CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-i2c.hh
1#pragma once
2#include <cdefs.h>
3#include <debug.hh>
4#include <stdint.h>
5#include <utils.hh>
6
7/**
8 * Driver for the OpenTitan's I2C block.
9 *
10 * Documentation source can be found at:
11 * https://github.com/lowRISC/opentitan/tree/4fe1b8dd1a09af9dbc242434481ae031955dfd85/hw/ip/i2c
12 */
14{
15 /// Interrupt State Register
17 /// Interrupt Enable Register
19 /// Interrupt Test Register
20 uint32_t interruptTest;
21 /// Alert Test Register (Unused in Sonata)
22 uint32_t alertTest;
23 /// I2C Control Register
24 uint32_t control;
25 /// I2C Live Status Register for Host and Target modes
26 uint32_t status;
27 /// I2C Read Data
28 uint32_t readData;
29 /// I2C Host Format Data
30 uint32_t formatData;
31 /// I2C FIFO control register
32 uint32_t fifoCtrl;
33 /// Host mode FIFO configuration
35 /// Target mode FIFO configuration
37 /// Host mode FIFO status register
39 /// Target mode FIFO status register
41 /// I2C Override Control Register
42 uint32_t override;
43 /// Oversampled Receive values
44 uint32_t values;
45 /**
46 * Detailed I2C Timings (directly corresponding to table 10 in the I2C
47 * Specification).
48 */
49 uint32_t timing[5];
50 /// I2C clock stretching timeout control.
52 /// I2C target address and mask pairs
53 uint32_t targetId;
54 /// I2C target acquired data
55 uint32_t acquiredData;
56 /// I2C target transmit data
57 uint32_t transmitData;
58 /**
59 * I2C host clock generation timeout value (in units of input clock
60 * frequency).
61 */
63 /// I2C target internal stretching timeout control.
65 /**
66 * Number of times the I2C target has NACK'ed a new transaction since the
67 * last read of this register.
68 */
70 /**
71 * Controls for mid-transfer (N)ACK phase handling.
72 */
74 /// The data byte pending to be written to the Acquire (ACQ) FIFO.
76 /**
77 * Timeout in Host-Mode for an unhandled NACK before hardware automatically
78 * ends the transaction.
79 */
81 /// Latched events that explain why the controller halted.
83 /**
84 * Latched events that can cause the target module to stretch the clock at
85 * the beginning of a read transfer.
86 */
87 uint32_t targetEvents;
88
89 /**
90 * The interrupts of the OpenTitan's I2C block.
91 *
92 * Documentation source can be found at:
93 * https://github.com/lowRISC/opentitan/blob/4fe1b8dd1a09af9dbc242434481ae031955dfd85/hw/ip/i2c/doc/interfaces.md
94 */
95 enum class Interrupt
96 {
97 /**
98 * A host mode interrupt. This is asserted whilst the Format FIFO level
99 * is below the low threshold. This is a level status interrupt.
100 */
102 /**
103 * A host mode interrupt. This is asserted whilst the Receive FIFO level
104 * is above the high threshold. This is a level status interrupt.
105 */
107 /**
108 * A target mode interrupt. This is asserted whilst the Aquired FIFO
109 * level is above the high threshold. This is a level status interrupt.
110 */
112 /**
113 * A host mode interrupt. This is raised if the Receive FIFO has
114 * overflowed.
115 */
117 /**
118 * A host mode interrupt. This is raised if the controller FSM is
119 * halted, such as on an unexpected NACK or lost arbitration. Check the
120 * `controllerEvents` register for the reason. The interrupt will only
121 * be released when the bits in `controllerEvents` are cleared.
122 */
124 /**
125 * A host mode interrupt. This is raised if the SCL line drops early
126 * (not supported without clock synchronization).
127 */
129 /**
130 * A host mode interrupt. This is raised if the SDA line goes low when
131 * host is trying to assert high.
132 */
134 /**
135 * A host mode interrupt. This is raised if target stretches the clock
136 * beyond the allowed timeout period.
137 */
139 /**
140 * A host mode interrupt. This is raised if the target does not assert a
141 * constant value of SDA during transmission.
142 */
144 /**
145 * A host and target mode interrupt. In host mode, raised if the host
146 * issues a repeated START or terminates the transaction by issuing
147 * STOP. In target mode, raised if the external host issues a STOP or
148 * repeated START.
149 */
151 /**
152 * A target mode interrupt. This is raised if the target is stretching
153 * clocks for a read command. This is a level status interrupt.
154 */
156 /**
157 * A target mode interrupt. This is asserted whilst the Transmit FIFO
158 * level is below the low threshold. This is a level status interrupt.
159 */
161 /**
162 * A target mode interrupt. This is raised if the target is stretching
163 * clocks due to full Aquired FIFO or zero count in
164 * targetAckControl.NBYTES (if enabled). This is a level status
165 * interrupt.
166 */
168 /**
169 * A target mode interrupt. This is raised if STOP is received without a
170 * preceding NACK during an external host read.
171 */
173 /**
174 * A target mode interrupt. This is raised if the host stops sending the
175 * clock during an ongoing transaction.
176 */
178 };
179
180 static constexpr uint32_t interrupt_bit(const Interrupt Interrupt)
181 {
182 return 1 << static_cast<uint32_t>(Interrupt);
183 };
184
185 /// Control Register Fields
186 enum [[clang::flag_enum]] : uint32_t
187 {
188 /// Enable Host I2C functionality
190 /// Enable Target I2C functionality
192 /// Enable I2C line loopback test If line loopback is enabled, the
193 /// internal design sees ACQ and RX data as "1"
195 /// Enable NACKing the address on a stretch timeout. This is a target
196 /// mode feature. If enabled, a stretch timeout will cause the device to
197 /// NACK the address byte. If disabled, it will ACK instead.
199 /// Enable ACK Control Mode, which works with the `targetAckControl`
200 /// register to allow software to control upper-layer (N)ACKing.
202 /// Enable the bus monitor in multi-controller mode.
204 /// If set, causes a read transfer addressed to the this target to set
205 /// the corresponding bit in the `targetEvents` register. While the
206 /// `transmitPending` field is 1, subsequent read transactions will
207 /// stretch the clock, even if there is data in the Transmit FIFO.
209 };
210
211 /// Status Register Fields
212 enum [[clang::flag_enum]] : uint32_t
213 {
214 /// Host mode Format FIFO is full
216 /// Host mode Receive FIFO is full
218 /// Host mode Format FIFO is empty
220 /// Host functionality is idle. No Host transaction is in progress
222 /// Target functionality is idle. No Target transaction is in progress
224 /// Host mode Receive FIFO is empty
226 /// Target mode Transmit FIFO is full
228 /// Target mode Acquired FIFO is full
230 /// Target mode Transmit FIFO is empty
232 /// Target mode Acquired FIFO is empty
234 /// Target mode stretching at (N)ACK phase due to zero count
235 /// in the `targetAckControl` register.
237 };
238
239 /// FormatData Register Fields
240 enum [[clang::flag_enum]] : uint32_t
241 {
242 /// Issue a START condition before transmitting BYTE.
244 /// Issue a STOP condition after this operation
246 /// Read BYTE bytes from I2C. (256 if BYTE==0)
248 /**
249 * Do not NACK the last byte read, let the read
250 * operation continue
251 */
253 /// Do not signal an exception if the current byte is not ACK’d
255 };
256
257 /// FifoControl Register Fields
258 enum [[clang::flag_enum]] : uint32_t
259 {
260 /// Receive fifo reset. Write 1 to the register resets it. Read returns
261 /// 0
263 /// Format fifo reset. Write 1 to the register resets it. Read returns 0
265 /// Acquired FIFO reset. Write 1 to the register resets it. Read returns
266 /// 0
268 /// Transmit FIFO reset. Write 1 to the register resets it. Read returns
269 /// 0
271 };
272
273 /// ControllerEvents Register Fields
274 enum [[clang::flag_enum]] : uint32_t
275 {
276 /// Controller FSM is halted due to receiving an unexpected NACK.
278 /**
279 * Controller FSM is halted due to a Host-Mode active transaction being
280 * ended by the `hostNackHandlerTimeout` mechanism.
281 */
283 /**
284 * Controller FSM is halted due to a Host-Mode active transaction being
285 * terminated because of a bus timeout activated by `timeoutControl`.
286 */
288 /**
289 * Controller FSM is halted due to a Host-Mode active transaction being
290 * terminated because of lost arbitration.
291 */
293 };
294
295 // Referred to as 'RX FIFO' in the documentation
296 static constexpr uint32_t ReceiveFifoDepth = 8;
297
298 /// Flag set when we're debugging this driver.
299 static constexpr bool DebugOpenTitanI2c = false;
300
301 /// Helper for conditional debug logs and assertions.
302 using Debug = ConditionalDebug<DebugOpenTitanI2c, "OpenTitan I2C">;
303
304 /**
305 * Performs a 32-bit integer unsigned division, rounding up. The bottom
306 * 16 bits of the result are then returned.
307 *
308 * As usual, a divisor of 0 is still Undefined Behavior.
309 */
310 static uint16_t round_up_divide(uint32_t a, uint32_t b)
311 {
312 if (a == 0)
313 {
314 return 0;
315 }
316 const uint32_t Res = ((a - 1) / b) + 1;
317 Debug::Assert(Res <= UINT16_MAX,
318 "Division result too large to fit in uint16_t.");
319 return static_cast<uint16_t>(Res);
320 }
321
322 /**
323 * Reset the controller Events so that the `ControllerHalt` interrupt will
324 * be released, allowing the I2C driver to continue after the controller
325 * FSM has halted due to e.g. a NACK, configured timeout, or loss of
326 * arbitration.
327 *
328 * Returns the (masked) result of reading the register before clearing
329 * the controller events, so that you can use the `controllerEvent`
330 * register fields to determine why the Controller FSM was halted.
331 */
332 uint32_t reset_controller_events() volatile
333 {
334 constexpr uint32_t FieldMask =
337 uint32_t events = controllerEvents & FieldMask;
338 controllerEvents = FieldMask;
339 return events;
340 }
341
342 /// Reset all of the fifos.
348
349 /// Configure the I2C block to be in host mode.
350 void host_mode_set() volatile
351 {
353 }
354
355 /**
356 * Set the I2C timing parameters appropriately for the given bit rate.
357 * Distilled from:
358 * https://github.com/lowRISC/opentitan/blob/9ddf276c64e2974ed8e528e8b2feb00b977861de/hw/ip/i2c/doc/programmers_guide.md
359 */
360 void speed_set(const uint32_t SpeedKhz) volatile
361 {
362 // We must round up the system clock frequency to lengthen intervals.
363 const uint16_t SystemClockKhz = round_up_divide(CPU_TIMER_HZ, 1000);
364 // We want to underestimate the clock period, to lengthen the timings.
365 const uint16_t ClockPeriod = (1000 * 1000) / SystemClockKhz;
366
367 // Decide which bus mode this represents
368 uint32_t mode = (SpeedKhz > 100u) + (SpeedKhz > 400u);
369
370 // Minimum fall time when V_DD is 3.3V
371 constexpr uint16_t MinimumFallTime = 20 * 3 / 5;
372 // Specification minimum timings (Table 10) in nanoseconds for each bus
373 // mode.
374 constexpr uint16_t MinimumTimeValues[5][2][3] = {
375 {
376 {4700u, 1300u, 150u}, // Low Period
377 {4000u, 600u, 260u}, // High Period
378 },
379 {
380 // Fall time of SDA and SCL signals
381 {MinimumFallTime, MinimumFallTime, MinimumFallTime},
382 // Rise time of SDA and SCL signals
383 {120, 120, 120},
384 },
385 {
386 {4700u, 600u, 260u}, // Hold time for a repeated start condition
387 {4000u, 600u, 260u}, // Set-up time for a repeated start condition
388 },
389 {
390 {4000u, 1u, 1u}, // Data hold time
391 {500u, 100u, 50u}, // Data set-up time
392 },
393 {
394 // Bus free time between a STOP and START condition
395 {4700u, 1300u, 500u},
396 // Set-up time for a STOP condition
397 {4000u, 600u, 260u},
398 },
399 };
400 for (uint32_t i = 0; i < 5; ++i)
401 {
402 timing[i] =
403 (round_up_divide(MinimumTimeValues[i][0][mode], ClockPeriod)
404 << 16) |
405 round_up_divide(MinimumTimeValues[i][1][mode], ClockPeriod);
406 }
407 }
408
409 void blocking_write_byte(const uint32_t Fmt) volatile
410 {
411 while (0 != (StatusFormatFull & status))
412 {
413 }
414 formatData = Fmt;
415 }
416
417 /// Returns true when the format fifo is empty
418 [[nodiscard]] bool format_is_empty() volatile
419 {
420 return 0 != (StatusFormatEmpty & status);
421 }
422
423 [[nodiscard]] bool blocking_write(const uint8_t Addr7,
424 const uint8_t data[],
425 const uint32_t NumBytes,
426 const bool SkipStop) volatile
427 {
428 if (NumBytes == 0)
429 {
430 return true;
431 }
432 blocking_write_byte(FormatDataStart | (Addr7 << 1) | 0u);
433 for (uint32_t i = 0; i < NumBytes - 1; ++i)
434 {
435 blocking_write_byte(data[i]);
436 }
437 blocking_write_byte((SkipStop ? 0u : FormatDataStop) |
438 data[NumBytes - 1]);
439 while (!format_is_empty())
440 {
442 {
444 return false;
445 }
446 }
447 return true;
448 }
449
450 [[nodiscard]] bool blocking_read(const uint8_t Addr7,
451 uint8_t buf[],
452 const uint32_t NumBytes) volatile
453 {
454 for (uint32_t idx = 0; idx < NumBytes; idx += ReceiveFifoDepth)
455 {
456 blocking_write_byte(FormatDataStart | (Addr7 << 1) | 1u);
457 while (!format_is_empty())
458 {
459 }
461 {
463 return false;
464 }
465 uint32_t bytesRemaining = NumBytes - idx;
466 bool lastChunk = ReceiveFifoDepth >= bytesRemaining;
467 uint8_t chunkSize = lastChunk ? static_cast<uint8_t>(bytesRemaining)
468 : ReceiveFifoDepth;
469
470 blocking_write_byte((lastChunk ? FormatDataStop : 0) |
471 FormatDataReadBytes | chunkSize);
472 while (!format_is_empty())
473 {
474 }
475
476 for (uint32_t chunkIdx = 0; chunkIdx < chunkSize; ++chunkIdx)
477 {
478 buf[idx + chunkIdx] = readData;
479 }
480 }
481 return true;
482 }
483
484 /// Returns true if the given interrupt is asserted.
485 [[nodiscard]] bool interrupt_is_asserted(Interrupt interrupt) volatile
486 {
487 return 0 != (interruptState & interrupt_bit(interrupt));
488 }
489
490 /// Clears the given interrupt.
491 void interrupt_clear(Interrupt interrupt) volatile
492 {
493 interruptState = interrupt_bit(interrupt);
494 }
495
496 /// Enables the given interrupt.
497 void interrupt_enable(Interrupt interrupt) volatile
498 {
499 interruptEnable = interruptEnable | interrupt_bit(interrupt);
500 }
501
502 /// Disables the given interrupt.
503 void interrupt_disable(Interrupt interrupt) volatile
504 {
505 interruptEnable = interruptEnable & ~interrupt_bit(interrupt);
506 }
507
508 /**
509 * Sets the thresholds for the format and receive fifos.
510 */
511 void host_thresholds_set(uint16_t formatThreshold,
512 uint16_t receiveThreshold) volatile
513 {
515 (formatThreshold & 0xfff) << 16 | (receiveThreshold & 0xfff);
516 }
517};
Utility class to delete copy and move contructors.
Definition utils.hh:53
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
Driver for the OpenTitan's I2C block.
Interrupt
The interrupts of the OpenTitan's I2C block.
@ AcquiredFull
A target mode interrupt.
@ SclInterference
A host mode interrupt.
@ StretchTimeout
A host mode interrupt.
@ ControllerHalt
A host mode interrupt.
@ UnexpectedStop
A target mode interrupt.
@ ReceiveOverflow
A host mode interrupt.
@ ReceiveThreshold
A host mode interrupt.
@ SdaUnstable
A host mode interrupt.
@ HostTimeout
A target mode interrupt.
@ AcquiredThreshold
A target mode interrupt.
@ TransmitStretch
A target mode interrupt.
@ TransmitThreshold
A target mode interrupt.
@ CommandComplete
A host and target mode interrupt.
@ SdaInterference
A host mode interrupt.
@ FormatThreshold
A host mode interrupt.
uint32_t status
I2C Live Status Register for Host and Target modes.
uint32_t readData
I2C Read Data.
@ ControlMultiControllerMonitorEnable
Enable the bus monitor in multi-controller mode.
@ ControlAckControlEnable
Enable ACK Control Mode, which works with the targetAckControl register to allow software to control ...
@ ControlTransmitStretchEnable
If set, causes a read transfer addressed to the this target to set the corresponding bit in the targe...
@ ControlNackAddressAfterTimeout
Enable NACKing the address on a stretch timeout.
@ FormatDataNakOk
Do not signal an exception if the current byte is not ACK’d.
@ FormatDataStop
Issue a STOP condition after this operation.
@ FormatDataStart
Issue a START condition before transmitting BYTE.
@ FormatDataReadCount
Do not NACK the last byte read, let the read operation continue.
@ FormatDataReadBytes
Read BYTE bytes from I2C. (256 if BYTE==0).
uint32_t interruptState
Interrupt State Register.
uint32_t values
Oversampled Receive values.
uint32_t targetNackCount
Number of times the I2C target has NACK'ed a new transaction since the last read of this register.
void host_thresholds_set(uint16_t formatThreshold, uint16_t receiveThreshold) volatile
Sets the thresholds for the format and receive fifos.
uint32_t controllerEvents
Latched events that explain why the controller halted.
uint32_t targetAckControl
Controls for mid-transfer (N)ACK phase handling.
uint32_t targetFifoConfiguration
Target mode FIFO configuration.
uint32_t timing[5]
Detailed I2C Timings (directly corresponding to table 10 in the I2C Specification).
uint32_t formatData
I2C Host Format Data.
void interrupt_enable(Interrupt interrupt) volatile
Enables the given interrupt.
uint32_t fifoCtrl
I2C FIFO control register.
void reset_fifos() volatile
Reset all of the fifos.
uint32_t targetEvents
Latched events that can cause the target module to stretch the clock at the beginning of a read trans...
@ ControlLineLoopback
Enable I2C line loopback test If line loopback is enabled, the internal design sees ACQ and RX data a...
@ ControlEnableTarget
Enable Target I2C functionality.
@ ControlEnableHost
Enable Host I2C functionality.
uint32_t hostFifoConfiguration
Host mode FIFO configuration.
uint32_t targetFifoStatus
Target mode FIFO status register.
bool format_is_empty() volatile
Returns true when the format fifo is empty.
static constexpr bool DebugOpenTitanI2c
Flag set when we're debugging this driver.
uint32_t acquiredData
I2C target acquired data.
uint32_t targetTimeoutControl
I2C target internal stretching timeout control.
uint32_t reset_controller_events() volatile
Reset the controller Events so that the ControllerHalt interrupt will be released,...
@ FifoControlFormatReset
Format fifo reset. Write 1 to the register resets it. Read returns 0.
@ FifoControlTransmitReset
Transmit FIFO reset.
@ FifoControlReceiveReset
Receive fifo reset.
@ FifoControlAcquiredReset
Acquired FIFO reset.
static uint16_t round_up_divide(uint32_t a, uint32_t b)
Performs a 32-bit integer unsigned division, rounding up.
uint32_t timeoutControl
I2C clock stretching timeout control.
uint32_t hostFifoStatus
Host mode FIFO status register.
void interrupt_disable(Interrupt interrupt) volatile
Disables the given interrupt.
uint32_t control
I2C Control Register.
uint32_t hostTimeoutControl
I2C host clock generation timeout value (in units of input clock frequency).
uint32_t hostNackHandlerTimeout
Timeout in Host-Mode for an unhandled NACK before hardware automatically ends the transaction.
@ StatusTransmitFull
Target mode Transmit FIFO is full.
@ StatusReceiveFull
Host mode Receive FIFO is full.
@ StatusAcquiredFull
Target mode Acquired FIFO is full.
@ StatusFormatEmpty
Host mode Format FIFO is empty.
@ StatusTransmitEmpty
Target mode Transmit FIFO is empty.
@ StatusHostIdle
Host functionality is idle. No Host transaction is in progress.
@ StatusTargetIdle
Target functionality is idle. No Target transaction is in progress.
@ StatusAcquiredEmpty
Target mode Acquired FIFO is empty.
@ StatusFormatFull
Host mode Format FIFO is full.
@ SmatusReceiveEmpty
Host mode Receive FIFO is empty.
uint32_t interruptTest
Interrupt Test Register.
void speed_set(const uint32_t SpeedKhz) volatile
Set the I2C timing parameters appropriately for the given bit rate.
uint32_t alertTest
Alert Test Register (Unused in Sonata).
uint32_t transmitData
I2C target transmit data.
bool interrupt_is_asserted(Interrupt interrupt) volatile
Returns true if the given interrupt is asserted.
@ StatusAckControlStretch
Target mode stretching at (N)ACK phase due to zero count in the targetAckControl register.
void interrupt_clear(Interrupt interrupt) volatile
Clears the given interrupt.
uint32_t interruptEnable
Interrupt Enable Register.
uint32_t targetId
I2C target address and mask pairs.
void host_mode_set() volatile
Configure the I2C block to be in host mode.
ConditionalDebug< DebugOpenTitanI2c, "OpenTitan I2C"> Debug
Helper for conditional debug logs and assertions.
@ ControllerEventsArbitrationLost
Controller FSM is halted due to a Host-Mode active transaction being terminated because of lost arbit...
@ ControllerEventsBusTimeout
Controller FSM is halted due to a Host-Mode active transaction being terminated because of a bus time...
@ ControllerEventsNack
Controller FSM is halted due to receiving an unexpected NACK.
@ ControllerEventsUnhandledNackTimeout
Controller FSM is halted due to a Host-Mode active transaction being ended by the hostNackHandlerTime...
uint32_t acquireFifoNextData
The data byte pending to be written to the Acquire (ACQ) FIFO.
Miscellaneous utility functions and classes.