1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2021-2023 Precursor Project 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System.Collections.Generic; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core.Structure.Registers; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Time; 15 16 // This model is a reimplementation of the OpenCoresI2C module. 17 namespace Antmicro.Renode.Peripherals.I2C 18 { 19 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 20 public class BetrustedSocI2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral, IKnownSize 21 { BetrustedSocI2C(IMachine machine)22 public BetrustedSocI2C(IMachine machine) : base(machine) 23 { 24 dataToSlave = new Queue<byte>(); 25 dataFromSlave = new Queue<byte>(); 26 IRQ = new GPIO(); 27 28 irqTimeoutCallback = new ClockEntry(IrqTimeout, 1000, this.FinishTransaction, machine, "Irq Scheduler"); 29 30 var registersMap = new Dictionary<long, DoubleWordRegister>() 31 { 32 {(long)Registers.ClockPrescale, new DoubleWordRegister(this) 33 .WithReservedBits(16, 16) 34 .WithValueField(0, 16, FieldMode.Read | FieldMode.Write) 35 }, 36 37 {(long)Registers.Control, new DoubleWordRegister(this) 38 .WithReservedBits(8, 24) 39 .WithFlag(7, out enabled) 40 .WithTag("Interrupt enable", 6, 1) 41 .WithReservedBits(0, 6) 42 }, 43 44 {(long)Registers.Transmit, new DoubleWordRegister(this) 45 .WithReservedBits(8, 24) 46 .WithValueField(0, 8, out transmitBuffer, FieldMode.Write) 47 }, 48 {(long)Registers.Reset, new DoubleWordRegister(this) 49 .WithReservedBits(1, 31) 50 .WithFlag(0, writeCallback: (_, val) => 51 { 52 if(val) 53 { 54 dataToSlave.Clear(); 55 dataFromSlave.Clear(); 56 UpdateInterrupts(); 57 } 58 }) 59 }, 60 61 {(long)Registers.Receive, new DoubleWordRegister(this) 62 .WithReservedBits(8, 24) 63 .WithValueField(0, 8, out receiveBuffer, FieldMode.Read) 64 }, 65 66 {(long)Registers.Status, new DoubleWordRegister(this) 67 .WithReservedBits(8, 24) 68 .WithFlag(7, out receivedAckFromSlaveNegated, FieldMode.Read) 69 .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => false, name: "Busy") 70 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "Arbitration lost") 71 .WithReservedBits(2, 3) 72 .WithFlag(1, FieldMode.Read, valueProviderCallback: _ => false, name: "Transfer in progress") 73 .WithFlag(0, out i2cIntIrqStatus, FieldMode.Read) 74 }, 75 76 {(long)Registers.EventStatus, new DoubleWordRegister(this) 77 .WithReservedBits(2, 30) 78 .WithFlag(1, FieldMode.Read, name: "TX_RX_DONE_STATUS", valueProviderCallback: _ => txRxDoneIrqStatus) 79 .WithFlag(0, FieldMode.Read, name: "I2C_INT_STATUS", valueProviderCallback: _ => i2cIntIrqStatus.Value) 80 }, 81 82 {(long)Registers.EventPending, new DoubleWordRegister(this) 83 .WithReservedBits(2, 30) 84 .WithFlag(1, out txRxDoneIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "TX_RX_DONE_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 85 .WithFlag(0, out i2cIntIrqPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "I2C_INT_PENDING", changeCallback: (_, __) => UpdateInterrupts()) 86 }, 87 88 {(long)Registers.EventEnable, new DoubleWordRegister(this) 89 .WithReservedBits(2, 30) 90 .WithFlag(1, out txRxDoneIrqEnabled, name: "TX_RX_DONE_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 91 .WithFlag(0, out i2cIntIrqEnabled, name: "I2C_INT_ENABLE", changeCallback: (_, __) => UpdateInterrupts()) 92 }, 93 94 {(long)Registers.Command, new DoubleWordRegister(this) 95 .WithReservedBits(8, 24) 96 .WithFlag(7, out generateStartCondition, FieldMode.Write) 97 .WithFlag(6, out generateStopCondition, FieldMode.Write) 98 .WithFlag(5, out readFromSlave, FieldMode.Write) 99 .WithFlag(4, out writeToSlave, FieldMode.Write) 100 .WithFlag(3, FieldMode.Read, name: "ACK", valueProviderCallback: _ => true) 101 // .WithTag("ACK", 3, 1) 102 .WithReservedBits(1, 2) 103 .WithFlag(0, FieldMode.Write, writeCallback: (_, __) => i2cIntIrqStatus.Value = false) 104 .WithWriteCallback((_, __) => 105 { 106 if(!enabled.Value) 107 { 108 return; 109 } 110 111 if(generateStartCondition.Value) 112 { 113 generateStartCondition.Value = false; 114 if(transactionInProgress) 115 { 116 // repeated start - finish previous transaction first 117 SendDataToSlave(); 118 } 119 else 120 { 121 transactionInProgress = true; 122 } 123 124 dataFromSlave.Clear(); 125 selectedSlave?.FinishTransmission(); 126 127 if(!TryResolveSelectedSlave(out selectedSlave)) 128 { 129 return; 130 } 131 } 132 133 if(writeToSlave.Value) 134 { 135 writeToSlave.Value = false; 136 HandleWriteToSlaveCommand(); 137 } 138 139 if(readFromSlave.Value) 140 { 141 readFromSlave.Value = false; 142 HandleReadFromSlaveCommand(); 143 } 144 145 if (transactionInProgress) { 146 machine.ClockSource.AddClockEntry(irqTimeoutCallback); 147 } 148 149 if(generateStopCondition.Value) 150 { 151 selectedSlave.FinishTransmission(); 152 153 generateStopCondition.Value = false; 154 if(!transactionInProgress) 155 { 156 return; 157 } 158 159 SendDataToSlave(); 160 transactionInProgress = false; 161 } 162 163 shouldSendTxRxIrq = true; 164 }) 165 } 166 }; 167 168 registers = new DoubleWordRegisterCollection(this, registersMap); 169 UpdateInterrupts(); 170 } 171 FinishTransaction()172 private void FinishTransaction() 173 { 174 machine.ClockSource.TryRemoveClockEntry(FinishTransaction); 175 if(shouldSendTxRxIrq) 176 { 177 shouldSendTxRxIrq = false; 178 txRxDoneIrqStatus = true; 179 UpdateInterrupts(); 180 txRxDoneIrqStatus = false; 181 } 182 } 183 Reset()184 public override void Reset() 185 { 186 registers.Reset(); 187 dataToSlave.Clear(); 188 dataFromSlave.Clear(); 189 UpdateInterrupts(); 190 } 191 ReadDoubleWord(long offset)192 public uint ReadDoubleWord(long offset) 193 { 194 return registers.Read(offset); 195 } 196 WriteDoubleWord(long offset, uint value)197 public void WriteDoubleWord(long offset, uint value) 198 { 199 registers.Write(offset, value); 200 } 201 UpdateInterrupts()202 private void UpdateInterrupts() 203 { 204 if(i2cIntIrqStatus.Value && i2cIntIrqEnabled.Value) 205 { 206 i2cIntIrqPending.Value = true; 207 } 208 if(txRxDoneIrqStatus && txRxDoneIrqEnabled.Value) 209 { 210 txRxDoneIrqPending.Value = true; 211 } 212 // this.Log(LogLevel.Noisy, " Setting status: {0}, enabled: {1}, pending: {2}", txRxDoneIrqStatus, txRxDoneIrqEnabled.Value, txRxDoneIrqPending.Value); 213 IRQ.Set(i2cIntIrqPending.Value || txRxDoneIrqPending.Value); 214 } 215 216 public GPIO IRQ { get; } 217 218 public long Size => 0x1000; 219 TryResolveSelectedSlave(out II2CPeripheral selectedSlave)220 private bool TryResolveSelectedSlave(out II2CPeripheral selectedSlave) 221 { 222 var slaveAddress = (byte)(transmitBuffer.Value >> 1); 223 if(!ChildCollection.TryGetValue(slaveAddress, out selectedSlave)) 224 { 225 this.Log(LogLevel.Warning, "Addressing unregistered slave: 0x{0:X}", slaveAddress); 226 receivedAckFromSlaveNegated.Value = true; 227 return false; 228 } 229 230 receivedAckFromSlaveNegated.Value = false; 231 return true; 232 } 233 HandleReadFromSlaveCommand()234 private void HandleReadFromSlaveCommand() 235 { 236 if(dataFromSlave.Count == 0) 237 { 238 foreach(var b in selectedSlave.Read()) 239 { 240 dataFromSlave.Enqueue(b); 241 } 242 243 if(dataFromSlave.Count == 0) 244 { 245 this.Log(LogLevel.Warning, "Trying to read from slave, but no data is available"); 246 receiveBuffer.Value = 0; 247 return; 248 } 249 } 250 251 receiveBuffer.Value = dataFromSlave.Dequeue(); 252 UpdateInterrupts(); 253 } 254 SendDataToSlave()255 private void SendDataToSlave() 256 { 257 if(dataToSlave.Count == 0 || selectedSlave == null) 258 { 259 this.Log(LogLevel.Warning, "Trying to send data to slave, but either no data is available or the slave is not selected"); 260 return; 261 } 262 263 selectedSlave.Write(dataToSlave.ToArray()); 264 dataToSlave.Clear(); 265 } 266 HandleWriteToSlaveCommand()267 private void HandleWriteToSlaveCommand() 268 { 269 if(!transactionInProgress) 270 { 271 this.Log(LogLevel.Warning, "Writing to slave without generating START signal"); 272 return; 273 } 274 275 dataToSlave.Enqueue((byte)transmitBuffer.Value); 276 i2cIntIrqStatus.Value = true; 277 UpdateInterrupts(); 278 } 279 280 private bool transactionInProgress; 281 private II2CPeripheral selectedSlave; 282 283 private readonly Queue<byte> dataToSlave; 284 private readonly Queue<byte> dataFromSlave; 285 private readonly IValueRegisterField receiveBuffer; 286 private readonly IValueRegisterField transmitBuffer; 287 private readonly IFlagRegisterField readFromSlave; 288 private readonly IFlagRegisterField writeToSlave; 289 private readonly IFlagRegisterField i2cIntIrqStatus; 290 private IFlagRegisterField i2cIntIrqEnabled; 291 private IFlagRegisterField i2cIntIrqPending; 292 private bool txRxDoneIrqStatus; 293 private bool shouldSendTxRxIrq; 294 private IFlagRegisterField txRxDoneIrqEnabled; 295 private IFlagRegisterField txRxDoneIrqPending; 296 private readonly IFlagRegisterField enabled; 297 private readonly IFlagRegisterField receivedAckFromSlaveNegated; 298 private readonly IFlagRegisterField generateStartCondition; 299 private readonly IFlagRegisterField generateStopCondition; 300 private readonly DoubleWordRegisterCollection registers; 301 private ClockEntry irqTimeoutCallback; 302 303 private const int IrqTimeout = 5; 304 305 private enum Registers 306 { 307 ClockPrescale = 0x0, 308 Control = 0x4, 309 Transmit = 0x8, 310 Receive = 0xC, 311 Command = 0x10, 312 Status = 0x14, 313 Reset = 0x18, 314 EventStatus = 0x1c, 315 EventPending = 0x20, 316 EventEnable = 0x24 317 } 318 } 319 } 320