1 // 2 // Copyright (c) 2010-2019 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 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.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 15 namespace Antmicro.Renode.Peripherals.SPI 16 { 17 public class LiteX_SPI : NullRegistrationPointPeripheralContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize 18 { LiteX_SPI(IMachine machine)19 public LiteX_SPI(IMachine machine) : base(machine) 20 { 21 var registers = new Dictionary<long, DoubleWordRegister> 22 { 23 {(long)Registers.Control, new DoubleWordRegister(this) 24 .WithValueField(0, 8, out bitsPerWord, name: "bits per word", changeCallback: (_, __) => VerifyBitsPerWord()) 25 // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated 26 .WithIgnoredBits(8, 8) 27 .WithReservedBits(16, 16) 28 }, 29 30 // see comment above 31 {(long)Registers.Control2, new DoubleWordRegister(this) 32 .WithFlag(0, name: "start", writeCallback: (_, startBit) => 33 { 34 if(!startBit) 35 { 36 return; 37 } 38 39 if(loopbackMode.Value) 40 { 41 this.Log(LogLevel.Noisy, "Sending data in loopback mode"); 42 masterInBuffer.Value = masterOutBuffer.Value; 43 } 44 else 45 { 46 if(!chipSelect.Value) 47 { 48 this.Log(LogLevel.Warning, "Tried to send some data over SPI, but CS is not set"); 49 masterInBuffer.Value = NoResponse; 50 return; 51 } 52 53 if(RegisteredPeripheral == null) 54 { 55 this.Log(LogLevel.Warning, "Tried to send some data over SPI, but no device is currently connected"); 56 masterInBuffer.Value = NoResponse; 57 return; 58 } 59 60 if(bitsPerWord.Value == 0) 61 { 62 this.Log(LogLevel.Warning, "Transfer length set to 0. Ignoring the transfer"); 63 masterInBuffer.Value = NoResponse; 64 return; 65 } 66 67 // see method's comment for details 68 if(!VerifyBitsPerWord()) 69 { 70 masterInBuffer.Value = NoResponse; 71 return; 72 } 73 74 var result = RegisteredPeripheral.Transmit((byte)masterOutBuffer.Value); 75 masterInBuffer.Value = (byte)result; 76 } 77 }) 78 // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated 79 .WithIgnoredBits(1, 15) 80 .WithReservedBits(16, 16) 81 }, 82 83 {(long)Registers.Status, new DoubleWordRegister(this) 84 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true, name: "done") 85 .WithReservedBits(1, 31) 86 }, 87 88 {(long)Registers.MasterOutSlaveIn, new DoubleWordRegister(this) 89 .WithValueField(0, 8, out masterOutBuffer) 90 // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated 91 .WithIgnoredBits(8, 24) 92 }, 93 94 {(long)Registers.MasterInSlaveOut, new DoubleWordRegister(this) 95 .WithValueField(0, 8, out masterInBuffer) 96 // the driver do not limit written value to a single byte, so without ignoring the following bits a warning would be generated 97 .WithIgnoredBits(8, 24) 98 }, 99 100 {(long)Registers.Loopback, new DoubleWordRegister(this) 101 .WithFlag(0, out loopbackMode) 102 .WithReservedBits(1, 31) 103 }, 104 105 {(long)Registers.ChipSelect, new DoubleWordRegister(this) 106 // for now we support only one device 107 // this should be improved to support more devices 108 .WithFlag(0, out chipSelect, name: "cs", writeCallback: (_, val) => 109 { 110 if(val && RegisteredPeripheral == null) 111 { 112 this.Log(LogLevel.Warning, "CS set, but no device is currently connected"); 113 } 114 }) 115 .WithReservedBits(1, 31) 116 } 117 }; 118 119 registersCollection = new DoubleWordRegisterCollection(this, registers); 120 Reset(); 121 } 122 Reset()123 public override void Reset() 124 { 125 registersCollection.Reset(); 126 } 127 ReadDoubleWord(long offset)128 public uint ReadDoubleWord(long offset) 129 { 130 return registersCollection.Read(offset); 131 } 132 WriteDoubleWord(long offset, uint value)133 public void WriteDoubleWord(long offset, uint value) 134 { 135 registersCollection.Write(offset, value); 136 } 137 138 public long Size => 0x50; 139 140 // This is due to limitations in Renode: 141 // proper handling of other values of BPR would require support for bit-by-bit transmission in SPI. 142 // Remove once the SPI interface is reworked. VerifyBitsPerWord()143 private bool VerifyBitsPerWord() 144 { 145 if(bitsPerWord.Value != 8) 146 { 147 this.Log(LogLevel.Warning, "Bits per word set to: {0}. This configuration is not supported by this model - ignoring the transfer", bitsPerWord.Value); 148 return false; 149 } 150 151 return true; 152 } 153 154 private readonly DoubleWordRegisterCollection registersCollection; 155 private readonly IValueRegisterField masterOutBuffer; 156 private readonly IValueRegisterField masterInBuffer; 157 private readonly IFlagRegisterField loopbackMode; 158 private readonly IFlagRegisterField chipSelect; 159 private readonly IValueRegisterField bitsPerWord; 160 161 private const byte NoResponse = 0xFF; 162 163 private enum Registers 164 { 165 // in LiteX this is defined as one CSRStorage(16) 166 Control = 0x0, 167 Control2 = 0x4, 168 169 Status = 0x8, 170 MasterOutSlaveIn = 0xC, 171 MasterInSlaveOut = 0x10, 172 ChipSelect = 0x14, 173 Loopback = 0x18 174 } 175 } 176 } 177