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
6/**
7 * A Simple Driver for the Sonata's SPI.
8 *
9 * Documentation source can be found at:
10 * https://github.com/lowRISC/sonata-system/blob/1a59633d2515d4fe186a07d53e49ff95c18d9bbf/doc/ip/spi.md
11 *
12 * Rendered documentation is served from:
13 * https://lowrisc.org/sonata-system/doc/ip/spi.html
14 */
16{
17 /**
18 * The Sonata SPI block doesn't currently have support for interrupts.
19 * The following registers are reserved for future use.
20 */
22 uint32_t interruptEnable;
23 uint32_t interruptTest;
24 /**
25 * Configuration register. Controls how the SPI block transmits and
26 * receives data. This register can be modified only whilst the SPI block
27 * is idle.
28 */
29 uint32_t configuration;
30 /**
31 * Controls the operation of the SPI block. This register can
32 * be modified only whilst the SPI block is idle.
33 */
34 uint32_t control;
35 /// Status information about the SPI block
36 uint32_t status;
37 /**
38 * Writes to this begin an SPI operation.
39 * Writes are ignored when the SPI block is active.
40 */
41 uint32_t start;
42 /**
43 * Data from the receive FIFO. When read the data is popped from the FIFO.
44 * If the FIFO is empty data read is undefined.
45 */
46 uint32_t receiveFifo;
47 /**
48 * Bytes written here are pushed to the transmit FIFO. If the FIFO is full
49 * writes are ignored.
50 */
51 uint32_t transmitFifo;
52
53 /// Configuration Register Fields
54 enum : uint32_t
55 {
56 /**
57 * The length of a half period (i.e. positive edge to negative edge) of
58 * the SPI clock, measured in system clock cycles reduced by 1. For
59 * example, at a 50 MHz system clock, a value of 0 gives a 25 MHz SPI
60 * clock, a value of 1 gives a 12.5 MHz SPI clock, a value of 2 gives
61 * a 8.33 MHz SPI clock and so on.
62 */
64 /*
65 * When set the most significant bit (MSB) is the first bit sent and
66 * received with each byte
67 */
68 ConfigurationMSBFirst = 1u << 29,
69 /*
70 * The phase of the spi_clk signal. when clockphase is 0, data is
71 * sampled on the leading edge and changes on the trailing edge. The
72 * first data bit is immediately available before the first leading edge
73 * of the clock when transmission begins. When clockphase is 1, data is
74 * sampled on the trailing edge and change on the leading edge.
75 */
76 ConfigurationClockPhase = 1u << 30,
77 /*
78 * The polarity of the spi_clk signal. When ClockPolarity is 0, clock is
79 * low when idle and the leading edge is positive. When ClkPolarity is
80 * 1, clock is high when idle and the leading edge is negative
81 */
82 ConfigurationClockPolarity = 1u << 31,
83 };
84
85 /// Control Register Fields
86 enum : uint32_t
87 {
88 /// Write 1 to clear the transmit FIFO.
89 ControlTransmitClear = 1 << 0,
90 /// Write 1 to clear the receive FIFO.
91 ControlReceiveClear = 1 << 1,
92 /**
93 * When set bytes from the transmit FIFO are sent. When clear the state
94 * of the outgoing spi_cipo is undefined whilst the SPI clock is
95 * running.
96 */
97 ControlTransmitEnable = 1 << 2,
98 /**
99 * When set incoming bits are written to the receive FIFO. When clear
100 * incoming bits are ignored.
101 */
102 ControlReceiveEnable = 1 << 3,
103 /**
104 * The watermark level for the transmit FIFO, depending on the value
105 * the interrupt will trigger at different points
106 */
107 ControlTransmitWatermarkMask = 0xf << 4,
108 /**
109 * The watermark level for the receive FIFO, depending on the value the
110 * interrupt will trigger at different points
111 */
112 ControlReceiveWatermarkMask = 0xf << 8,
113 };
114
115 /// Status Register Fields
116 enum : uint32_t
117 {
118 /// Number of items in the transmit FIFO.
119 StatusTxFifoLevel = 0xffu << 0,
120 /// Number of items in the receive FIFO.
121 StatusRxFifoLevel = 0xffu << 8,
122 /**
123 * When set the transmit FIFO is full and any data written to it will
124 * be ignored.
125 */
126 StatusTxFifoFull = 1u << 16,
127 /**
128 * When set the receive FIFO is empty and any data read from it will be
129 * undefined.
130 */
131 StatusRxFifoEmpty = 1u << 17,
132 /// When set the SPI block is idle and can accept a new start command.
133 StatusIdle = 1u << 18,
134 };
135
136 /// Start Register Fields
137 enum : uint32_t
138 {
139 /// Number of bytes to receive/transmit in the SPI operation
140 StartByteCountMask = 0x7ffu,
141 };
142
143 /// Flag set when we're debugging this driver.
144 static constexpr bool DebugSonataSpi = false;
145
146 /// Helper for conditional debug logs and assertions.
147 using Debug = ConditionalDebug<DebugSonataSpi, "Sonata SPI">;
148
149 /**
150 * Initialises the SPI block
151 *
152 * @param ClockPolarity When false, the clock is low when idle and the
153 * leading edge is positive. When true, the opposite behaviour is
154 * set.
155 * @param ClockPhase When false, data is sampled on the leading edge and
156 * changes on the trailing edge. When true, the opposite behaviour is
157 * set.
158 * @param MsbFirst When true, the first bit of each byte sent is the most
159 * significant bit, as oppose to the least significant bit.
160 * @param HalfClockPeriod The length of a half period of the SPI clock,
161 * measured in system clock cycles reduced by 1.
162 */
163 void init(const bool ClockPolarity,
164 const bool ClockPhase,
165 const bool MsbFirst,
166 const uint16_t HalfClockPeriod) volatile
167 {
168 configuration = (ClockPolarity ? ConfigurationClockPolarity : 0) |
169 (ClockPhase ? ConfigurationClockPhase : 0) |
170 (MsbFirst ? ConfigurationMSBFirst : 0) |
171 (HalfClockPeriod & ConfigurationHalfClockPeriodMask);
172 }
173
174 /// Waits for the SPI device to become idle
175 void wait_idle() volatile
176 {
177 // Wait whilst IDLE field in STATUS is low
178 while ((status & StatusIdle) == 0) {}
179 }
180
181 /**
182 * Sends `len` bytes from the given `data` buffer,
183 * where `len` is at most `0x7ff`.
184 */
185 void blocking_write(const uint8_t data[], uint16_t len) volatile
186 {
187 Debug::Assert(len <= 0x7ff,
188 "You can't transfer more than 0x7ff bytes at a time.");
189 len &= StartByteCountMask;
190
191 wait_idle();
193 start = len;
194
195 uint32_t transmitAvailable = 0;
196 for (uint32_t i = 0; i < len; ++i)
197 {
198 if (transmitAvailable == 0)
199 {
200 while (transmitAvailable < 64)
201 {
202 // Read number of bytes in TX FIFO to calculate space
203 // available for more bytes
204 transmitAvailable = 64 - (status & StatusTxFifoLevel);
205 }
206 }
207 transmitFifo = data[i];
208 transmitAvailable--;
209 }
210 }
211
212 /*
213 * Receives `len` bytes and puts them in the `data` buffer,
214 * where `len` is at most `0x7ff`.
215 *
216 * This method will block until the requested number of bytes
217 * has been seen. There is currently no timeout.
218 */
219 void blocking_read(uint8_t data[], uint16_t len) volatile
220 {
221 Debug::Assert(len <= 0x7ff,
222 "You can't receive more than 0x7ff bytes at a time.");
223 len &= StartByteCountMask;
224 wait_idle();
225 control = ControlReceiveEnable;
226 start = len;
227
228 for (uint32_t i = 0; i < len; ++i)
229 {
230 // Wait for at least one byte to be available in the RX FIFO
231 while ((status & StatusRxFifoLevel) == 0) {}
232 data[i] = static_cast<uint8_t>(receiveFifo);
233 }
234 }
235};
C++ APIs for assertions, invariants, and writing formatted debug messages to a UART.
A Simple Driver for the Sonata's SPI.
@ StartByteCountMask
Number of bytes to receive/transmit in the SPI operation.
static constexpr bool DebugSonataSpi
Flag set when we're debugging this driver.
uint32_t configuration
Configuration register.
uint32_t receiveFifo
Data from the receive 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.
void init(const bool ClockPolarity, const bool ClockPhase, const bool MsbFirst, const uint16_t HalfClockPeriod) volatile
Initialises the SPI block.
@ ControlTransmitEnable
When set bytes from the transmit FIFO are sent.
void wait_idle() volatile
Waits for the SPI device to become idle.
uint32_t status
Status information about the SPI block.
uint32_t transmitFifo
Bytes written here are pushed to the transmit FIFO.
@ ConfigurationHalfClockPeriodMask
The length of a half period (i.e.
uint32_t interruptState
The Sonata SPI block doesn't currently have support for interrupts.
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.
ConditionalDebug< DebugSonataSpi, "Sonata SPI"> Debug
Helper for conditional debug logs and assertions.
uint32_t start
Writes to this begin an SPI operation.
uint32_t control
Controls the operation of the SPI block.