1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Peripherals.Bus; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.SPI 18 { 19 public class RenesasDA_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection> 20 { RenesasDA_SPI(IMachine machine, int fifoDepth = DefaultFifoDepth)21 public RenesasDA_SPI(IMachine machine, int fifoDepth = DefaultFifoDepth) : base(machine) 22 { 23 IRQ = new GPIO(); 24 receiveQueue = new Queue<byte>(); 25 if(fifoDepth < 1) 26 { 27 throw new ConstructionException($"{nameof(fifoDepth)} value cannot be less than 1, got {fifoDepth}"); 28 } 29 this.fifoDepth = fifoDepth; 30 31 RegistersCollection = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 32 Reset(); 33 } 34 Reset()35 public override void Reset() 36 { 37 receiveQueue.Clear(); 38 IRQ.Unset(); 39 RegistersCollection.Reset(); 40 } 41 ReadDoubleWord(long offset)42 public uint ReadDoubleWord(long offset) 43 { 44 return RegistersCollection.Read(offset); 45 } 46 WriteDoubleWord(long offset, uint value)47 public void WriteDoubleWord(long offset, uint value) 48 { 49 RegistersCollection.Write(offset, value); 50 } 51 52 public DoubleWordRegisterCollection RegistersCollection { get; } 53 public GPIO IRQ { get; } 54 public long Size => 0x100; 55 public bool ChipSelectEnableValue { get; set; } 56 BuildRegisterMap()57 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 58 { 59 var registerMap = new Dictionary<long, DoubleWordRegister> 60 { 61 {(long)Registers.Control, new DoubleWordRegister(this) 62 .WithFlag(0, out spiEnabled, name: "SPI_EN") 63 .WithFlag(1, out transmitEnabled, name: "SPI_TX_EN") 64 .WithFlag(2, out receiveEnabled, name: "SPI_RX_EN") 65 .WithTaggedFlag("SPI_DMA_TX_EN", 3) 66 .WithTaggedFlag("SPI_DMA_RX_EN", 4) 67 .WithFlag(5, FieldMode.Read | FieldMode.WriteOneToClear, name: "SPI_FIFO_RESET", 68 changeCallback: (_, value) => 69 { 70 if(!value) 71 { 72 return; 73 } 74 75 this.DebugLog("Resetting FIFO"); 76 receiveQueue.Clear(); 77 }) 78 .WithTaggedFlag("SPI_CAPTURE_AT_NEXT_EDGE", 6) 79 .WithTaggedFlag("SPI_SWAP_BYTES", 7) 80 .WithReservedBits(8, 24) 81 }, 82 83 {(long)Registers.Clock, new DoubleWordRegister(this) 84 .WithTag("SPI_CLK_DIV", 0, 7) 85 .WithReservedBits(7, 25) 86 }, 87 88 {(long)Registers.Configuration, new DoubleWordRegister(this) 89 .WithTag("SPI_MODE", 0, 2) 90 .WithValueField(2, 5, out wordBits, name: "SPI_WORD_LENGTH", 91 changeCallback: (_, value) => 92 { 93 var length = value + WordBitsOffset; 94 if(length != 8) 95 { 96 this.ErrorLog("Word length of {0} bits is currently not supported. Reverting to word length of 8 bits"); 97 wordBits.Value = length - WordBitsOffset; 98 } 99 }) 100 .WithTaggedFlag("SPI_SLAVE_EN", 7) 101 .WithReservedBits(8, 24) 102 }, 103 104 {(long)Registers.FifoConfiguration, new DoubleWordRegister(this) 105 .WithTag("SPI_TX_TL", 0, 4) 106 .WithValueField(4, 4, out receiveFIFOThresholdLevel, name: "SPI_RX_TL", changeCallback: (_, __) => UpdateInterrupts()) 107 .WithReservedBits(8, 24) 108 }, 109 110 {(long)Registers.InterruptMask, new DoubleWordRegister(this) 111 .WithFlag(0, out fifoTransmitEmptyInterruptEnabled, name: "SPI_IRQ_MASK_TX_EMPTY") 112 .WithFlag(1, out fifoReceiveFullInterruptEnabled, name: "SPI_IRQ_MASK_RX_FULL") 113 .WithReservedBits(2, 30) 114 .WithChangeCallback((_, __) => UpdateInterrupts()) 115 }, 116 117 {(long)Registers.Status, new DoubleWordRegister(this) 118 .WithFlag(0, FieldMode.Read, name: "SPI_STATUS_TX_EMPTY", valueProviderCallback: _ => true) 119 .WithFlag(1, FieldMode.Read, name: "SPI_STATUS_RX_FULL", valueProviderCallback: _ => IsReceiveFIFOFull) 120 .WithReservedBits(2, 30) 121 }, 122 123 {(long)Registers.FifoStatus, new DoubleWordRegister(this) 124 .WithValueField(0, 6, FieldMode.Read, name: "SPI_RX_FIFO_LEVEL", valueProviderCallback: _ => (ulong)Math.Min(fifoDepth, receiveQueue.Count)) 125 .WithValueField(6, 6, FieldMode.Read, name: "SPI_TX_FIFO_LEVEL", valueProviderCallback: _ => 0) 126 .WithFlag(12, FieldMode.Read, name: "SPI_STATUS_RX_EMPTY", valueProviderCallback: _ => receiveQueue.Count == 0) 127 .WithFlag(13, FieldMode.Read, name: "SPI_STATUS_TX_FULL", valueProviderCallback: _ => false) 128 .WithFlag(14, FieldMode.Read, name: "SPI_RX_FIFO_OVFL", valueProviderCallback: _ => false) 129 .WithFlag(15, FieldMode.Read, name: "SPI_TRANSACTION_ACTIVE", valueProviderCallback: _ => false) 130 .WithReservedBits(16, 16) 131 }, 132 133 {(long)Registers.FifoRead, new DoubleWordRegister(this) 134 .WithValueField(0, 32, FieldMode.Read, name: "SPI_FIFO_READ", 135 valueProviderCallback: _ => 136 { 137 if(!spiEnabled.Value) 138 { 139 this.WarningLog("SPI Module is disabled, returning 0"); 140 return 0; 141 } 142 143 if(!receiveEnabled.Value) 144 { 145 this.WarningLog("Reception is disabled, returning 0"); 146 return 0; 147 } 148 149 if(!receiveQueue.TryDequeue(out byte result)) 150 { 151 this.WarningLog("Reading from a empty receive FIFO, returning 0"); 152 return 0x0; 153 } 154 UpdateInterrupts(); 155 return result; 156 }) 157 }, 158 159 {(long)Registers.FifoWrite, new DoubleWordRegister(this) 160 .WithValueField(0, 32, FieldMode.Write, name: "SPI_FIFO_WRITE", 161 writeCallback: (_, value) => 162 { 163 if(!spiEnabled.Value) 164 { 165 this.WarningLog("SPI Module is disabled, returning"); 166 return; 167 } 168 169 if(!transmitEnabled.Value) 170 { 171 this.WarningLog("Transmission is disabled, returning"); 172 return; 173 } 174 175 if(selectedPeripheral == null) 176 { 177 this.WarningLog("No peripheral is selected, returning"); 178 return; 179 } 180 181 receiveQueue.Enqueue(selectedPeripheral.Transmit((byte)value)); 182 UpdateInterrupts(); 183 }) 184 }, 185 186 {(long)Registers.CsConfiguration, new DoubleWordRegister(this) 187 .WithEnumField<DoubleWordRegister, ChipSelect>(0, 3, name: "SPI_CS_SELECT", 188 changeCallback: (_, value) => SwitchActivePeripheral(value)) 189 .WithReservedBits(3, 29) 190 }, 191 192 {(long)Registers.TXBufferForce, new DoubleWordRegister(this) 193 .WithTag("SPI_TXBUFFER_FORCE", 0, 32) 194 } 195 }; 196 197 return registerMap; 198 } 199 UpdateInterrupts()200 private void UpdateInterrupts() 201 { 202 var state = fifoTransmitEmptyInterruptEnabled.Value || (fifoReceiveFullInterruptEnabled.Value && IsReceiveFIFOFull); 203 this.DebugLog("IRQ {0}", state ? "set" : "unset"); 204 IRQ.Set(state); 205 } 206 SwitchActivePeripheral(ChipSelect value)207 private void SwitchActivePeripheral(ChipSelect value) 208 { 209 switch(value) 210 { 211 case ChipSelect.None: 212 { 213 if(selectedPeripheral is IGPIOReceiver receiver) 214 { 215 receiver.OnGPIO(0, !ChipSelectEnableValue); 216 } 217 else 218 { 219 selectedPeripheral?.FinishTransmission(); 220 } 221 selectedPeripheral = null; 222 return; 223 } 224 case ChipSelect.SPIEnable: 225 case ChipSelect.SPIEnable2: 226 case ChipSelect.GPIO: 227 { 228 if(selectedPeripheral is IGPIOReceiver oldReceiver) 229 { 230 oldReceiver.OnGPIO(0, !ChipSelectEnableValue); 231 } 232 else 233 { 234 selectedPeripheral?.FinishTransmission(); 235 } 236 237 var address = ChipSelectToAddress(value); 238 if(!TryGetByAddress(address, out var peripheral)) 239 { 240 this.WarningLog("No peripheral with address {0} exists (Chip select: {1})", address, value); 241 selectedPeripheral = null; 242 return; 243 } 244 245 if(peripheral is IGPIOReceiver newReceiver) 246 { 247 newReceiver.OnGPIO(0, ChipSelectEnableValue); 248 } 249 250 selectedPeripheral = peripheral; 251 return; 252 } 253 default: 254 this.ErrorLog("Invalid chip select value 0x{0:X}", value); 255 return; 256 } 257 } 258 ChipSelectToAddress(ChipSelect chipSelect)259 private int ChipSelectToAddress(ChipSelect chipSelect) 260 { 261 switch(chipSelect) 262 { 263 case ChipSelect.SPIEnable: 264 return 1; 265 case ChipSelect.SPIEnable2: 266 return 2; 267 case ChipSelect.GPIO: 268 return 3; 269 default: 270 return -1; 271 } 272 } 273 274 private bool IsReceiveFIFOFull => (ulong)receiveQueue.Count >= (receiveFIFOThresholdLevel.Value + 1); 275 276 private readonly Queue<byte> receiveQueue; 277 private readonly int fifoDepth; 278 279 private ISPIPeripheral selectedPeripheral; 280 281 private IFlagRegisterField spiEnabled; 282 private IFlagRegisterField transmitEnabled; 283 private IFlagRegisterField receiveEnabled; 284 private IFlagRegisterField fifoTransmitEmptyInterruptEnabled; 285 private IFlagRegisterField fifoReceiveFullInterruptEnabled; 286 private IValueRegisterField receiveFIFOThresholdLevel; 287 private IValueRegisterField wordBits; 288 289 private const int DefaultFifoDepth = 4; 290 private const int WordBitsOffset = 1; 291 292 public enum Registers 293 { 294 Control = 0x0, // SPI_CTRL_REG 295 Configuration = 0x4, // SPI_CONFIG_REG 296 Clock = 0x8, // SPI_CLOCK_REG 297 FifoConfiguration = 0xC, // SPI_FIFO_CONFIG_REG 298 InterruptMask = 0x10, // SPI_IRQ_MASK_REG 299 Status = 0x14, // SPI_STATUS_REG 300 FifoStatus = 0x18, // SPI_FIFO_STATUS_REG 301 FifoRead = 0x1C, // SPI_FIFO_READ_REG 302 FifoWrite = 0x20, // SPI_FIFO_WRITE_REG 303 CsConfiguration = 0x24, // SPI_CS_CONFIG_REG 304 TXBufferForce = 0x2C, // SPI_TXBUFFER_FORCE_REG 305 } 306 307 private enum ChipSelect 308 { 309 None = 0, 310 SPIEnable = 1, 311 SPIEnable2 = 2, 312 GPIO = 4, 313 } 314 } 315 } 316