CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-spi.hh
1#pragma once
2#include <cdefs.h>
3#include <debug.hh>
4#include <stdint.h>
5#include <utils.hh>
6
7namespace SonataSpi
8{
9 /// Sonata SPI Interrupts
10 typedef enum [[clang::flag_enum]] : uint32_t
11 {
12 /// Raised when a SPI operation completes and the block has become idle.
14 /*
15 * Asserted whilst the transmit FIFO level is at or below the
16 * transmit watermark.
17 */
18 InterruptTransmitWatermark = 1 << 3,
19 /// Asserted whilst the transmit FIFO is empty.
21 /*
22 * Asserted whilst the receive FIFO level is at or above the receive
23 * watermark.
24 */
25 InterruptReceiveWatermark = 1 << 1,
26 /// Asserted whilst the receive FIFO is full.
28 } Interrupt;
29
30 /// Configuration Register Fields
31 enum : uint32_t
32 {
33 /**
34 * The length of a half period (i.e. positive edge to negative edge) of
35 * the SPI clock, measured in system clock cycles reduced by 1. For
36 * example, at a 50 MHz system clock, a value of 0 gives a 25 MHz SPI
37 * clock, a value of 1 gives a 12.5 MHz SPI clock, a value of 2 gives
38 * a 8.33 MHz SPI clock and so on.
39 */
41 /*
42 * When set the most significant bit (MSB) is the first bit sent and
43 * received with each byte
44 */
45 ConfigurationMSBFirst = 1u << 29,
46 /*
47 * The phase of the spi_clk signal. when clockphase is 0, data is
48 * sampled on the leading edge and changes on the trailing edge. The
49 * first data bit is immediately available before the first leading edge
50 * of the clock when transmission begins. When clockphase is 1, data is
51 * sampled on the trailing edge and change on the leading edge.
52 */
53 ConfigurationClockPhase = 1u << 30,
54 /*
55 * The polarity of the spi_clk signal. When ClockPolarity is 0, clock is
56 * low when idle and the leading edge is positive. When ClkPolarity is
57 * 1, clock is high when idle and the leading edge is negative
58 */
59 ConfigurationClockPolarity = 1u << 31,
60 };
61
62 /// Control Register Fields
63 enum : uint32_t
64 {
65 /// Write 1 to clear the transmit FIFO.
67 /// Write 1 to clear the receive FIFO.
69 /**
70 * When set bytes from the transmit FIFO are sent. When clear the state
71 * of the outgoing spi_cipo is undefined whilst the SPI clock is
72 * running.
73 */
75 /**
76 * When set incoming bits are written to the receive FIFO. When clear
77 * incoming bits are ignored.
78 */
80 /**
81 * The watermark level for the transmit FIFO, depending on the value
82 * the interrupt will trigger at different points
83 */
85 /**
86 * The watermark level for the receive FIFO, depending on the value the
87 * interrupt will trigger at different points
88 */
90 /**
91 * Internal loopback function enabled when set to 1.
92 */
94 /**
95 * Software reset performed when written as 1.
96 */
98 };
99
100 /// Status Register Fields
101 enum : uint32_t
102 {
103 /// Number of items in the transmit FIFO.
104 StatusTxFifoLevel = 0xffu << 0,
105 /// Number of items in the receive FIFO.
106 StatusRxFifoLevel = 0xffu << 8,
107 /**
108 * When set the transmit FIFO is full and any data written to it will
109 * be ignored.
110 */
112 /**
113 * When set the receive FIFO is empty and any data read from it will be
114 * undefined.
115 */
117 /// When set the SPI block is idle and can accept a new start command.
118 StatusIdle = 1u << 18,
119 };
120
121 /// Start Register Fields
122 enum : uint32_t
123 {
124 /// Number of bytes to receive/transmit in the SPI operation
126 };
127
128 /// Info Register Fields
129 enum : uint32_t
130 {
131 /// Maximum number of items in the transmit FIFO.
132 InfoTxFifoDepth = 0xffu << 0,
133 /// Maximum number of items in the receive FIFO.
134 InfoRxFifoDepth = 0xffu << 8,
135 };
136
137 /**
138 * A driver for the Sonata's SPI device block.
139 *
140 * Documentation source can be found at:
141 * https://github.com/lowRISC/sonata-system/blob/1a59633d2515d4fe186a07d53e49ff95c18d9bbf/doc/ip/spi.md
142 *
143 * Rendered documentation is served from:
144 * https://lowrisc.org/sonata-system/doc/ip/spi.html
145 */
146 template<size_t NumChipSelects = 4>
148 {
149 /// The current state of the SPI interrupts.
151 /// Controls which interrupts are enabled.
153 /// Allows one to manually trigger an interrupt for testing.
155 /**
156 * Configuration register. Controls how the SPI block transmits and
157 * receives data. This register can be modified only whilst the SPI
158 * block is idle.
159 */
161 /**
162 * Controls the operation of the SPI block. This register can
163 * be modified only whilst the SPI block is idle.
164 */
165 uint32_t control;
166 /// Status information about the SPI block
167 uint32_t status;
168 /**
169 * Writes to this begin an SPI operation.
170 * Writes are ignored when the SPI block is active.
171 */
172 uint32_t start;
173 /**
174 * Data from the receive FIFO. When read the data is popped from the
175 * FIFO. If the FIFO is empty data read is undefined.
176 */
177 uint32_t receiveFifo;
178 /**
179 * Bytes written here are pushed to the transmit FIFO. If the FIFO is
180 * full writes are ignored.
181 */
182 uint32_t transmitFifo;
183 /**
184 * Information about the SPI controller. This register reports the
185 * depths of the transmit and receive FIFOs within the controller.
186 */
187 uint32_t info;
188 /**
189 * Chip Select lines; each bit controls a chip select line.
190 * When a bit set to zero, a chip select line is pulled low.
191 * Multiple chip select lines can be pulled low at a time.
192 */
193 uint32_t chipSelects;
194
195 /// Flag set when we're debugging this driver.
196 static constexpr bool DebugSonataSpi = false;
197
198 /// Helper for conditional debug logs and assertions.
199 using Debug = ConditionalDebug<DebugSonataSpi, "Sonata SPI">;
200
201 /// Enable the given interrupt(s).
202 inline void interrupt_enable(Interrupt interrupt) volatile
203 {
204 interruptEnable = interruptEnable | interrupt;
205 }
206
207 /// Disable the given interrupt(s).
208 inline void interrupt_disable(Interrupt interrupt) volatile
209 {
210 interruptEnable = interruptEnable & ~interrupt;
211 }
212
213 /**
214 * Initialises the SPI block
215 *
216 * @param ClockPolarity When false, the clock is low when idle and the
217 * leading edge is positive. When true, the opposite behaviour is
218 * set.
219 * @param ClockPhase When false, data is sampled on the leading edge and
220 * changes on the trailing edge. When true, the opposite
221 * behaviour is set.
222 * @param MsbFirst When true, the first bit of each byte sent is the
223 * most significant bit, as oppose to the least significant bit.
224 * @param HalfClockPeriod The length of a half period of the SPI clock,
225 * measured in system clock cycles reduced by 1.
226 */
227 void init(const bool ClockPolarity,
228 const bool ClockPhase,
229 const bool MsbFirst,
230 const uint16_t HalfClockPeriod) volatile
231 {
233 (ClockPolarity ? ConfigurationClockPolarity : 0) |
234 (ClockPhase ? ConfigurationClockPhase : 0) |
235 (MsbFirst ? ConfigurationMSBFirst : 0) |
236 (HalfClockPeriod & ConfigurationHalfClockPeriodMask);
237
238 // Ensure that FIFOs are emptied of any stale data and the
239 // controller core has returned to Idle if it is presently active
240 // (eg. an incomplete previous operation, perhaps one that was
241 // interrupted/failed).
242 //
243 // Note: although, from a logical perspective, the three operations
244 // (i) tx clear, (ii) core reset and (iii) rx clear should be
245 // performed in that order, presently the implementation supports
246 // performing them as a single write.
247 control =
248 ControlTransmitClear | ControlSoftwareReset | ControlReceiveClear;
249 }
250
251 /// Waits for the SPI device to become idle
252 void wait_idle() volatile
253 {
254 // Wait whilst IDLE field in STATUS is low
255 while ((status & StatusIdle) == 0)
256 {
257 }
258 }
259
260 /**
261 * Sends `len` bytes from the given `data` buffer,
262 * where `len` is at most `0x7ff`.
263 */
264 void blocking_write(const uint8_t data[], uint16_t len) volatile
265 {
266 Debug::Assert(
267 len <= StartByteCountMask,
268 "You can't transfer more than 0x7ff bytes at a time.");
269 len &= StartByteCountMask;
270
271 wait_idle();
272 // Do not attempt a zero-byte transfer; not supported by the
273 // controller.
274 if (len)
275 {
276 control = ControlTransmitEnable;
277 start = len;
278
279 uint32_t transmitAvailable = 0;
280 for (uint32_t i = 0; i < len; ++i)
281 {
282 while (!transmitAvailable)
283 {
284 // Read number of bytes in TX FIFO to calculate space
285 // available for more bytes
286 transmitAvailable = 8 - (status & StatusTxFifoLevel);
287 }
288 transmitFifo = data[i];
289 transmitAvailable--;
290 }
291 }
292 }
293
294 /*
295 * Receives `len` bytes and puts them in the `data` buffer,
296 * where `len` is at most `0x7ff`.
297 *
298 * This method will block until the requested number of bytes
299 * has been seen. There is currently no timeout.
300 */
301 void blocking_read(uint8_t data[], uint16_t len) volatile
302 {
303 Debug::Assert(len <= StartByteCountMask,
304 "You can't receive more than 0x7ff bytes at a time.");
305 len &= StartByteCountMask;
306 wait_idle();
307 // Do not attempt a zero-byte transfer; not supported by the
308 // controller.
309 if (len)
310 {
312 start = len;
313
314 for (uint32_t i = 0; i < len; ++i)
315 {
316 // Wait for at least one byte to be available in the RX FIFO
317 while ((status & StatusRxFifoLevel) == 0)
318 {
319 }
320 data[i] = static_cast<uint8_t>(receiveFifo);
321 }
322 }
323 }
324
325 /**
326 * Asserts/de-asserts a given chip select.
327 *
328 * Note, SPI chip selects are active low signals, so the register bit is
329 * zero when asserted and one when de-asserted.
330 *
331 * @tparam Index The index of the chip select to be set.
332 * @tparam DeassertOthers Whether to de-assert all other chip selects.
333 * @param Assert Whether to assert (true) or de-assert (false).
334 */
335 template<uint8_t Index, bool DeassertOthers = true>
336 inline void chip_select_assert(const bool Assert = true) volatile
337 {
338 static_assert(Index < NumChipSelects,
339 "SPI chip select index out of bounds");
340
341 const uint32_t State =
342 DeassertOthers ? (1 << NumChipSelects) - 1 : chipSelects;
343
344 const uint32_t Bit = (1 << Index);
345 chipSelects = Assert ? State & ~Bit : State | Bit;
346 }
347 };
348
349 /// A specialised driver for the SPI device connected to the Ethernet MAC.
350 class EthernetMac : public Generic<2>
351 {
352 enum : uint8_t
353 {
354 ChipSelectLine = 0,
355 ResetLine = 1,
356 };
357
358 public:
359 /**
360 * Assert the chip select line.
361 * @param Assert Whether to assert (true) or de-assert (false) the chip
362 * select line.
363 */
364 inline void chip_select_assert(const bool Assert) volatile
365 {
367 }
368 /**
369 * Assert the reset line.
370 * @param Assert Whether to assert (true) or de-assert (false) the reset
371 * line.
372 */
373 inline void reset_assert(const bool Assert = true) volatile
374 {
376 }
377 };
378
379 /// A specialised driver for the SPI device connected to the LCD screen.
380 class Lcd : public Generic<3>
381 {
382 enum : uint8_t
383 {
384 ChipSelectLine = 0,
385 DataCommandLine = 1,
386 ResetLine = 2,
387 };
388
389 public:
390 /**
391 * Assert the chip select line.
392 * @param Assert Whether to assert (true) or de-assert (false) the chip
393 * select line.
394 */
395 inline void chip_select_assert(const bool Assert) volatile
396 {
398 }
399 /**
400 * Assert the chip select line.
401 * @param Assert Whether to assert (true) or de-assert (false) the reset
402 * line.
403 */
404 inline void reset_assert(const bool Assert = true) volatile
405 {
407 }
408 /**
409 * Set the data/command line.
410 * @param high Whether to set high (true) or low (false).
411 */
412 inline void data_command_set(const bool High) volatile
413 {
415 }
416 };
417} // namespace SonataSpi
A specialised driver for the SPI device connected to the Ethernet MAC.
void reset_assert(const bool Assert=true) volatile
Assert the reset line.
void chip_select_assert(const bool Assert) volatile
Assert the chip select line.
A specialised driver for the SPI device connected to the LCD screen.
void data_command_set(const bool High) volatile
Set the data/command line.
void chip_select_assert(const bool Assert) volatile
Assert the chip select line.
void reset_assert(const bool Assert=true) volatile
Assert the chip select line.
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.
A driver for the Sonata's SPI device block.
uint32_t interruptEnable
Controls which interrupts are enabled.
uint32_t chipSelects
Chip Select lines; each bit controls a chip select line.
uint32_t transmitFifo
Bytes written here are pushed to the transmit FIFO.
uint32_t start
Writes to this begin an SPI operation.
uint32_t control
Controls the operation of the SPI block.
uint32_t interruptState
The current state of the SPI interrupts.
void interrupt_disable(Interrupt interrupt) volatile
Disable the given interrupt(s).
void init(const bool ClockPolarity, const bool ClockPhase, const bool MsbFirst, const uint16_t HalfClockPeriod) volatile
Initialises the SPI block.
uint32_t configuration
Configuration register.
uint32_t interruptTest
Allows one to manually trigger an interrupt for testing.
void interrupt_enable(Interrupt interrupt) volatile
Enable the given interrupt(s).
uint32_t status
Status information about the SPI block.
void chip_select_assert(const bool Assert=true) volatile
Asserts/de-asserts a given chip select.
uint32_t receiveFifo
Data from the receive FIFO.
ConditionalDebug< DebugSonataSpi, "Sonata SPI"> Debug
Helper for conditional debug logs and assertions.
uint32_t info
Information about the SPI controller.
static constexpr bool DebugSonataSpi
Flag set when we're debugging this driver.
void blocking_write(const uint8_t data[], uint16_t len) volatile
Sends len bytes from the given data buffer, where len is at most 0x7ff.
void wait_idle() volatile
Waits for the SPI device to become idle.
@ StartByteCountMask
Number of bytes to receive/transmit in the SPI operation.
@ InfoRxFifoDepth
Maximum number of items in the receive FIFO.
@ InfoTxFifoDepth
Maximum number of items in the transmit FIFO.
@ StatusTxFifoLevel
Number of items in the transmit FIFO.
@ StatusRxFifoEmpty
When set the receive FIFO is empty and any data read from it will be undefined.
@ StatusRxFifoLevel
Number of items in the receive FIFO.
@ StatusIdle
When set the SPI block is idle and can accept a new start command.
@ StatusTxFifoFull
When set the transmit FIFO is full and any data written to it will be ignored.
@ ControlReceiveEnable
When set incoming bits are written to the receive FIFO.
@ ControlReceiveClear
Write 1 to clear the receive FIFO.
@ ControlTransmitEnable
When set bytes from the transmit FIFO are sent.
@ ControlReceiveWatermarkMask
The watermark level for the receive FIFO, depending on the value the interrupt will trigger at differ...
@ ControlTransmitClear
Write 1 to clear the transmit FIFO.
@ ControlTransmitWatermarkMask
The watermark level for the transmit FIFO, depending on the value the interrupt will trigger at diffe...
@ InterruptComplete
Raised when a SPI operation completes and the block has become idle.
@ InterruptTransmitEmpty
Asserted whilst the transmit FIFO is empty.
@ InterruptReceiveFull
Asserted whilst the receive FIFO is full.
@ ConfigurationHalfClockPeriodMask
The length of a half period (i.e.
@ ControlSoftwareReset
Software reset performed when written as 1.
@ ControlInternalLoopback
Internal loopback function enabled when set to 1.
Miscellaneous utility functions and classes.