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 16 namespace Antmicro.Renode.Peripherals.SPI 17 { 18 public class HiFive_SPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 19 { HiFive_SPI(IMachine machine, bool isFlashEnabled = false, int numberOfSupportedSlaves = 1)20 public HiFive_SPI(IMachine machine, bool isFlashEnabled = false, int numberOfSupportedSlaves = 1) : base(machine) 21 { 22 if(numberOfSupportedSlaves < 1 || numberOfSupportedSlaves > 32) 23 { 24 throw new ConstructionException($"Wrong number of supported slaves: {numberOfSupportedSlaves}. Provide a value in range from 1 to 32."); 25 } 26 27 this.csWidth = numberOfSupportedSlaves; 28 receiveQueue = new Queue<byte>(); 29 IRQ = new GPIO(); 30 31 var registersMap = new Dictionary<long, DoubleWordRegister>() 32 { 33 {(long)Registers.SerialClockDivisor, new DoubleWordRegister(this, 0x3) 34 .WithValueField(0, 12, name: "div") 35 .WithReservedBits(12, 20) 36 }, 37 {(long)Registers.SerialClockMode, new DoubleWordRegister(this, 0x0) 38 .WithFlag(0, name: "pha") 39 .WithFlag(1, name: "pol") 40 .WithReservedBits(2, 30) 41 }, 42 {(long)Registers.ChipSelectId, new DoubleWordRegister(this) 43 .WithValueField(0, 32, out selectedSlave, name: "csid", writeCallback: (previousValue, value) => 44 { 45 if(!ChildCollection.ContainsKey(checked((int)value))) 46 { 47 this.Log(LogLevel.Warning, "Selected pin {0}, but there is no device connected to it.", value); 48 } 49 50 if(previousValue != value) 51 { 52 FinishTransmission(); 53 } 54 else 55 { 56 ClearReceiveQueue(); 57 } 58 }) 59 }, 60 61 {(long)Registers.ChipSelectDefault, new DoubleWordRegister(this, (1u << numberOfSupportedSlaves) - 1) 62 // this field's width and reset value depend on a constructor parameter `numberOfSupportedSlaves` 63 .WithValueField(0, numberOfSupportedSlaves, name: "csdef") 64 }, 65 66 {(long)Registers.ChipSelectMode, new DoubleWordRegister(this) 67 .WithEnumField<DoubleWordRegister, ChipSelectMode>(0, 2, name: "csmode", 68 writeCallback: (_, val) => 69 { 70 // means off 71 if(val == ChipSelectMode.Auto) 72 { 73 FinishTransmission(); 74 } 75 }) 76 }, 77 78 {(long)Registers.FrameFormat, new DoubleWordRegister(this, 0x80000) 79 .WithValueField(0, 2, out spiProtocol, name: "proto", writeCallback: (oldValue, value) => 80 { 81 if(value != 0x0) 82 { 83 this.WarningLog("Only single SPI protocol (value 0x0) is supported"); 84 spiProtocol.Value = oldValue; 85 } 86 }) 87 .WithFlag(2, out spiEndianess, name: "endian", writeCallback: (oldValue, value) => 88 { 89 if(value) 90 { 91 this.WarningLog("Only transmitting MSB first (value 0x0) is supported"); 92 spiEndianess.Value = oldValue; 93 } 94 }) 95 .WithFlag(3, out spiIODirection, name: "dir", writeCallback: (oldValue, value) => 96 { 97 if(value) 98 { 99 this.WarningLog("Only Rx direction (value 0x0) is supported"); 100 spiIODirection.Value = oldValue; 101 } 102 }) 103 .WithReservedBits(4, 12) 104 .WithValueField(16, 4, out numberOfBitsPerFrame, name: "len", writeCallback: (oldValue, value) => 105 { 106 if(value != 0x8) 107 { 108 this.WarningLog("Only 8 bits per frame (value 0x8) are supported"); 109 numberOfBitsPerFrame.Value = oldValue; 110 } 111 }) 112 .WithReservedBits(20, 12) 113 }, 114 115 {(long)Registers.TxFifoData, new DoubleWordRegister(this, 0x0) 116 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) => HandleFifoWrite((byte)value), name: "data") 117 .WithReservedBits(8, 23) 118 .WithFlag(31, FieldMode.Read, valueProviderCallback: _ => false, name: "full") 119 }, 120 121 {(long)Registers.RxFifoData, new DoubleWordRegister(this, 0x0) 122 // According to the documentation this registers is divided into two fields and a reserved block. 123 // I decided not to split it because the value of both fields must be evaluated IN PROPER ORDER 124 // (flag before dequeuing) and currently the API does not guarantee that. 125 .WithValueField(0, 32, FieldMode.Read, name: "data+empty", valueProviderCallback: _ => 126 { 127 var result = (receiveQueue.Count == 0) 128 ? FifoEmptyMarker 129 : receiveQueue.Dequeue(); 130 131 UpdateInterrupts(); 132 return result; 133 }) 134 }, 135 136 {(long)Registers.TxFifoWatermark, new DoubleWordRegister(this, isFlashEnabled ? 0x1 : 0x0u) 137 .WithValueField(0, 3, out transmitWatermark, writeCallback: (_, __) => UpdateInterrupts(), name: "txmark") 138 .WithReservedBits(3, 29) 139 }, 140 141 {(long)Registers.RxFifoWatermark, new DoubleWordRegister(this, 0x0) 142 .WithValueField(0, 3, out receiveWatermark, writeCallback: (_, __) => UpdateInterrupts(), name: "rxmark") 143 .WithReservedBits(3, 29) 144 }, 145 146 {(long)Registers.InterruptEnable, new DoubleWordRegister(this, 0x0) 147 .WithFlag(0, out transmitWatermarkInterruptEnable, writeCallback: (_, __) => UpdateInterrupts(), name: "txwm") 148 .WithFlag(1, out receiveWatermarkInterruptEnable, writeCallback: (_, __) => UpdateInterrupts(), name: "rxwm") 149 .WithReservedBits(2, 30) 150 }, 151 152 {(long)Registers.InterruptPending, new DoubleWordRegister(this, 0x0) 153 .WithFlag(0, out transmitWatermarkPending, FieldMode.Read, name: "txwm") 154 .WithFlag(1, out receiveWatermarkPending, FieldMode.Read, name: "rxwm") 155 .WithReservedBits(2, 30) 156 } 157 }; 158 159 registers = new DoubleWordRegisterCollection(this, registersMap); 160 } 161 Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint)162 public override void Register(ISPIPeripheral peripheral, NumberRegistrationPoint<int> registrationPoint) 163 { 164 if(registrationPoint.Address < 0 || registrationPoint.Address > csWidth - 1) 165 { 166 throw new RecoverableException($"Wrong registration point: {registrationPoint}. Provide address in range from 0 to {csWidth - 1}."); 167 } 168 169 base.Register(peripheral, registrationPoint); 170 } 171 Reset()172 public override void Reset() 173 { 174 registers.Reset(); 175 UpdateInterrupts(); 176 } 177 ReadDoubleWord(long offset)178 public uint ReadDoubleWord(long offset) 179 { 180 return registers.Read(offset); 181 } 182 WriteDoubleWord(long offset, uint value)183 public void WriteDoubleWord(long offset, uint value) 184 { 185 registers.Write(offset, value); 186 } 187 188 public long Size => 0x78; 189 190 public GPIO IRQ { get; private set; } 191 HandleFifoWrite(byte value)192 private void HandleFifoWrite(byte value) 193 { 194 receiveQueue.Enqueue(!TryGetByAddress((int)selectedSlave.Value, out var slavePeripheral) 195 ? (byte)0 196 : slavePeripheral.Transmit(value)); 197 198 UpdateInterrupts(); 199 } 200 UpdateInterrupts()201 private void UpdateInterrupts() 202 { 203 transmitWatermarkPending.Value = (transmitWatermark.Value > 0); 204 receiveWatermarkPending.Value = (receiveQueue.Count > (int)receiveWatermark.Value); 205 206 IRQ.Set((transmitWatermarkInterruptEnable.Value && transmitWatermarkPending.Value) 207 || (receiveWatermarkInterruptEnable.Value && receiveWatermarkPending.Value)); 208 } 209 ClearReceiveQueue()210 private void ClearReceiveQueue() 211 { 212 if(receiveQueue.Count > 0) 213 { 214 this.Log(LogLevel.Warning, "Clearing receive queue, but there are still {0} bytes there that will be lost...", receiveQueue.Count); 215 } 216 217 receiveQueue.Clear(); 218 UpdateInterrupts(); 219 } 220 FinishTransmission()221 private void FinishTransmission() 222 { 223 if(TryGetByAddress((int)selectedSlave.Value, out var slavePeripheral)) 224 { 225 slavePeripheral.FinishTransmission(); 226 } 227 228 ClearReceiveQueue(); 229 } 230 231 private readonly int csWidth; 232 private readonly Queue<byte> receiveQueue; 233 private readonly IFlagRegisterField transmitWatermarkInterruptEnable; 234 private readonly IFlagRegisterField receiveWatermarkInterruptEnable; 235 private readonly IFlagRegisterField transmitWatermarkPending; 236 private readonly IFlagRegisterField receiveWatermarkPending; 237 private readonly IValueRegisterField transmitWatermark; 238 private readonly IValueRegisterField receiveWatermark; 239 private readonly IValueRegisterField selectedSlave; 240 private readonly IValueRegisterField spiProtocol; 241 private readonly IFlagRegisterField spiEndianess; 242 private readonly IFlagRegisterField spiIODirection; 243 private readonly IValueRegisterField numberOfBitsPerFrame; 244 private readonly DoubleWordRegisterCollection registers; 245 246 private const uint FifoEmptyMarker = 0x80000000; 247 248 private enum Registers 249 { 250 SerialClockDivisor = 0x0, 251 SerialClockMode = 0x4, 252 // 0x8, 0xC - reserved 253 ChipSelectId = 0x10, 254 ChipSelectDefault = 0x14, 255 ChipSelectMode = 0x18, 256 // 0x1C, 0x20, 0x24 - reserved 257 DelayControl0 = 0x28, 258 DelayControl1 = 0x2C, 259 // 0x30, 0x34, 0x38, 0x3C - reserved 260 FrameFormat = 0x40, 261 // 0x44 - reserved 262 TxFifoData = 0x48, 263 RxFifoData = 0x4C, 264 TxFifoWatermark = 0x50, 265 RxFifoWatermark = 0x54, 266 // 0x58, 0x5C - reserved 267 FlashInterfaceControl = 0x60, 268 FlashInstructionFormat = 0x64, 269 // 0x68, 0x6C - reserved 270 InterruptEnable = 0x70, 271 InterruptPending = 0x74 272 } 273 274 private enum ChipSelectMode 275 { 276 Auto = 0x0, // Assert/deassert CS at the beginning/end of each frame 277 // 0x1 is undefined in the documentation 278 Hold = 0x2, // Keep CS continously asserted after the initial frame 279 Off = 0x3 // Disable hardware control of the CS pin 280 } 281 } 282 } 283