1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using Antmicro.Renode.Peripherals.Bus; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Utilities.Collections; 14 15 namespace Antmicro.Renode.Peripherals.SPI 16 { 17 public sealed class STM32SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IWordPeripheral, IDoubleWordPeripheral, IBytePeripheral, IKnownSize 18 { STM32SPI(IMachine machine, int bufferCapacity = DefaultBufferCapacity)19 public STM32SPI(IMachine machine, int bufferCapacity = DefaultBufferCapacity) : base(machine) 20 { 21 receiveBuffer = new CircularBuffer<byte>(bufferCapacity); 22 IRQ = new GPIO(); 23 DMARecieve = new GPIO(); 24 registers = new DoubleWordRegisterCollection(this); 25 SetupRegisters(); 26 Reset(); 27 } 28 29 // We can't use AllowedTranslations because then WriteByte/WriteWord will trigger 30 // an additional read (see ReadWriteExtensions:WriteByteUsingDoubleWord). 31 // We can't have this happen for the data register. ReadByte(long offset)32 public byte ReadByte(long offset) 33 { 34 // byte interface is there for DMA 35 if(offset % 4 == 0) 36 { 37 return (byte)ReadDoubleWord(offset); 38 } 39 this.LogUnhandledRead(offset); 40 return 0; 41 } 42 WriteByte(long offset, byte value)43 public void WriteByte(long offset, byte value) 44 { 45 if(offset % 4 == 0) 46 { 47 WriteDoubleWord(offset, (uint)value); 48 } 49 else 50 { 51 this.LogUnhandledWrite(offset, value); 52 } 53 } 54 ReadWord(long offset)55 public ushort ReadWord(long offset) 56 { 57 return (ushort)ReadDoubleWord(offset); 58 } 59 WriteWord(long offset, ushort value)60 public void WriteWord(long offset, ushort value) 61 { 62 WriteDoubleWord(offset, (uint)value); 63 } 64 ReadDoubleWord(long offset)65 public uint ReadDoubleWord(long offset) 66 { 67 return registers.Read(offset); 68 } 69 WriteDoubleWord(long offset, uint value)70 public void WriteDoubleWord(long offset, uint value) 71 { 72 registers.Write(offset, value); 73 } 74 Reset()75 public override void Reset() 76 { 77 IRQ.Unset(); 78 DMARecieve.Unset(); 79 lock(receiveBuffer) 80 { 81 receiveBuffer.Clear(); 82 } 83 registers.Reset(); 84 } 85 86 public long Size 87 { 88 get 89 { 90 return 0x400; 91 } 92 } 93 94 public GPIO IRQ { get; } 95 96 public GPIO DMARecieve { get; } 97 HandleDataRead()98 private uint HandleDataRead() 99 { 100 IRQ.Unset(); 101 lock(receiveBuffer) 102 { 103 if(receiveBuffer.TryDequeue(out var value)) 104 { 105 Update(); 106 return value; 107 } 108 // We don't warn when the data register is read while it's empty because the HAL 109 // (for example L0, F4) does this intentionally. 110 // See https://github.com/STMicroelectronics/STM32CubeL0/blob/bec4e499a74de98ab60784bf2ef1912bee9c1a22/Drivers/STM32L0xx_HAL_Driver/Src/stm32l0xx_hal_spi.c#L1368-L1372 111 return 0; 112 } 113 } 114 HandleDataWrite(uint value)115 private void HandleDataWrite(uint value) 116 { 117 IRQ.Unset(); 118 lock(receiveBuffer) 119 { 120 var peripheral = RegisteredPeripheral; 121 if(peripheral == null) 122 { 123 this.Log(LogLevel.Warning, "SPI transmission while no SPI peripheral is connected."); 124 receiveBuffer.Enqueue(0x0); 125 return; 126 } 127 var response = peripheral.Transmit((byte)value); // currently byte mode is the only one we support 128 receiveBuffer.Enqueue(response); 129 if(rxDmaEnable.Value) 130 { 131 // This blink is used to signal the DMA that it should perform the peripheral -> memory transaction now 132 // Without this signal DMA will never move data from the receive buffer to memory 133 // See STM32DMA:OnGPIO 134 DMARecieve.Blink(); 135 } 136 this.NoisyLog("Transmitted 0x{0:X}, received 0x{1:X}.", value, response); 137 } 138 Update(); 139 } 140 Update()141 private void Update() 142 { 143 var rxBufferNotEmpty = receiveBuffer.Count != 0; 144 var rxBufferNotEmptyInterruptFlag = rxBufferNotEmpty && rxBufferNotEmptyInterruptEnable.Value; 145 146 IRQ.Set(txBufferEmptyInterruptEnable.Value || rxBufferNotEmptyInterruptFlag); 147 } 148 SetupRegisters()149 private void SetupRegisters() 150 { 151 // Some fields relate to the physical layer of the SPI protocol, these are not 152 // taken into account in Renode and are defined as flags or value fields without 153 // a corresponding RegisterField or read/write callbacks. They are marked with 154 // comments. 155 Registers.Control1.Define(registers) 156 .WithFlag(0, name: "CPHA") // Physical 157 .WithFlag(1, name: "CPOL") // Physical 158 .WithFlag(2, writeCallback: (_, value) => 159 { 160 if(!value) 161 { 162 this.Log(LogLevel.Warning, "Slave mode is not supported"); 163 } 164 }, name: "MSTR") 165 .WithValueField(3, 3, name: "Baud") // Physical 166 .WithFlag(6, changeCallback: (oldValue, newValue) => 167 { 168 if(!newValue) 169 { 170 IRQ.Unset(); 171 } 172 }, name: "SpiEnable") 173 .WithFlag(7, name: "LSBFIRST") // Physical 174 // We keep these as flags to preserve written values. SSI flag is used by drivers to select/detect operation mode (Master or Slave) 175 .WithFlag(8, name: "SSI") // Internal slave select 176 .WithFlag(9, name: "SSM") // Software slave management 177 .WithTaggedFlag("RXONLY", 10) 178 .WithTaggedFlag("DFF", 11) 179 .WithTaggedFlag("CRCNEXT", 12) 180 .WithTaggedFlag("CRCEN", 13) 181 .WithTaggedFlag("BIDIOE", 14) 182 .WithTaggedFlag("BIDIMODE", 15); 183 184 Registers.Control2.Define(registers) 185 .WithFlag(0, out rxDmaEnable, name: "RXDMAEN") 186 .WithTaggedFlag("TXDMAEN", 1) 187 .WithTaggedFlag("SSOE", 2) 188 .WithReservedBits(3, 1) 189 .WithTaggedFlag("FRF", 4) 190 .WithTaggedFlag("ERRIE", 5) 191 .WithFlag(6, out rxBufferNotEmptyInterruptEnable, name: "RXNEIE") 192 .WithFlag(7, out txBufferEmptyInterruptEnable, name: "TXEIE") 193 .WithReservedBits(8, 24) 194 .WithWriteCallback((_, __) => Update()); 195 196 Registers.Status.Define(registers, 2) 197 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => receiveBuffer.Count != 0, name: "RXNE") 198 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => true, name: "TXE") // transfers are instant 199 .WithTaggedFlag("CHSIDE", 2) // r/o 200 .WithTaggedFlag("UDR", 3) // r/o 201 .WithTaggedFlag("CRCERR", 4) // rc_w0 202 .WithTaggedFlag("MODF", 5) // r/o 203 .WithTaggedFlag("OVR", 6) // r/o 204 .WithTaggedFlag("BSY", 7) // r/o 205 .WithTaggedFlag("FRE", 8) // r/o 206 .WithReservedBits(9, 23); 207 208 Registers.Data.Define(registers) 209 .WithValueField(0, 16, valueProviderCallback: _ => HandleDataRead(), 210 writeCallback: (_, value) => HandleDataWrite((uint)value), name: "DR") 211 .WithReservedBits(16, 16); 212 213 Registers.CRCPolynomial.Define(registers, 7) 214 .WithTag("CRCPOLY", 0, 16) 215 .WithReservedBits(16, 16); 216 217 Registers.ReceivedCRC.Define(registers) 218 .WithTag("RxCRC", 0, 16) // r/o 219 .WithReservedBits(16, 16); 220 221 Registers.TransmittedCRC.Define(registers) 222 .WithTag("TxCRC", 0, 16) // r/o 223 .WithReservedBits(16, 16); 224 225 Registers.I2SConfiguration.Define(registers) 226 .WithTaggedFlag("CHLEN", 0) 227 .WithTag("DATLEN", 1, 2) 228 .WithTaggedFlag("CKPOL", 3) 229 .WithTag("I2SSTD", 4, 2) 230 .WithReservedBits(6, 1) 231 .WithTaggedFlag("PCMSYNC", 7) 232 .WithTag("I2SCFG", 8, 2) 233 .WithFlag(10, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (oldValue, newValue) => 234 { 235 // write one to clear to keep this bit 0 236 if(newValue) 237 { 238 this.Log(LogLevel.Warning, "Trying to enable not supported I2S mode."); 239 } 240 }, name: "I2SE") 241 .WithTaggedFlag("I2SMOD", 11) 242 .WithReservedBits(12, 20); 243 244 Registers.I2SPrescaler.Define(registers, 2) 245 .WithTag("I2SDIV", 0, 8) 246 .WithTaggedFlag("ODD", 8) 247 .WithTaggedFlag("MCKOE", 9) 248 .WithReservedBits(10, 22); 249 } 250 251 private DoubleWordRegisterCollection registers; 252 private IFlagRegisterField txBufferEmptyInterruptEnable, rxBufferNotEmptyInterruptEnable, rxDmaEnable; 253 254 private readonly CircularBuffer<byte> receiveBuffer; 255 256 private const int DefaultBufferCapacity = 4; 257 258 private enum Registers 259 { 260 Control1 = 0x0, // SPI_CR1, 261 Control2 = 0x4, // SPI_CR2 262 Status = 0x8, // SPI_SR 263 Data = 0xC, // SPI_DR 264 CRCPolynomial = 0x10, // SPI_CRCPR 265 ReceivedCRC = 0x14, // SPI_RXCRCR 266 TransmittedCRC = 0x18, // SPI_TXCRCR 267 I2SConfiguration = 0x1C, // SPI_I2SCFGR 268 I2SPrescaler = 0x20, // SPI_I2SPR 269 } 270 } 271 } 272