1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2022-2025 Silicon Labs 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using System; 10 using System.Collections.Generic; 11 using System.Linq; 12 using System.IO; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Exceptions; 16 using Antmicro.Renode.Logging; 17 using Antmicro.Renode.Peripherals.Bus; 18 using Antmicro.Renode.Peripherals.Timers; 19 using Antmicro.Renode.Time; 20 using Antmicro.Renode.Utilities; 21 using Antmicro.Renode.Utilities.Packets; 22 using Org.BouncyCastle.Crypto; 23 using Org.BouncyCastle.Crypto.Engines; 24 using Org.BouncyCastle.Crypto.Modes; 25 using Org.BouncyCastle.Crypto.Macs; 26 using Org.BouncyCastle.Crypto.Parameters; 27 using Org.BouncyCastle.Crypto.Digests; 28 29 namespace Antmicro.Renode.Peripherals.Miscellaneous.SiLabs 30 { 31 public class EFR32xG2_SEMAILBOX_1 : IDoubleWordPeripheral, IKnownSize 32 { EFR32xG2_SEMAILBOX_1(Machine machine)33 public EFR32xG2_SEMAILBOX_1(Machine machine) 34 { 35 this.machine = machine; 36 37 txFifo = new Queue<uint>(); 38 rxFifo = new Queue<uint>(); 39 40 RxIRQ = new GPIO(); 41 TxIRQ = new GPIO(); 42 43 Silabs_SecureElement = new Silabs_SecureElement(machine, this, txFifo, rxFifo, false); 44 45 registersCollection = BuildRegistersCollection(); 46 } 47 Reset()48 public void Reset() 49 { 50 txFifo.Clear(); 51 rxFifo.Clear(); 52 Silabs_SecureElement.Reset(); 53 rxHeaderAvailable = false; 54 } 55 ReadDoubleWord(long offset)56 public uint ReadDoubleWord(long offset) 57 { 58 var result = 0U; 59 60 if(!registersCollection.TryRead(offset, out result)) 61 { 62 this.Log(LogLevel.Noisy, "Unhandled read at offset 0x{0:X} ({1}).", offset, (Registers)offset); 63 } 64 else 65 { 66 this.Log(LogLevel.Noisy, "Read at offset 0x{0:X} ({1}), returned 0x{2:X}.", offset, (Registers)offset, result); 67 } 68 69 return result; 70 } 71 WriteDoubleWord(long offset, uint value)72 public void WriteDoubleWord(long offset, uint value) 73 { 74 this.Log(LogLevel.Noisy, "Write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value); 75 if(!registersCollection.TryWrite(offset, value)) 76 { 77 this.Log(LogLevel.Noisy, "Unhandled write at offset 0x{0:X} ({1}), value 0x{2:X}.", offset, (Registers)offset, value); 78 return; 79 } 80 } 81 BuildRegistersCollection()82 private DoubleWordRegisterCollection BuildRegistersCollection() 83 { 84 var registerDictionary = new Dictionary<long, DoubleWordRegister> 85 { 86 {(long)Registers.TxStatus, new DoubleWordRegister(this) 87 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => (uint)TxFifoWordsCount, name: "REMBYTES") 88 .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => 0 /* TODO */, name: "MSGINFO") 89 .WithFlag(20, FieldMode.Read, valueProviderCallback: _ => !TxFifoIsAlmostFull, name: "TXINT") 90 .WithFlag(21, FieldMode.Read, valueProviderCallback: _ => TxFifoIsFull, name: "TXFULL") 91 .WithReservedBits(22, 1) 92 .WithFlag(23, FieldMode.Read, valueProviderCallback: _ => false /* TODO */, name: "TXERROR") 93 .WithReservedBits(24, 8) 94 }, 95 {(long)Registers.RxStatus, new DoubleWordRegister(this) 96 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: _ => (uint)RxFifoWordsCount, name: "REMBYTES") 97 .WithValueField(16, 4, FieldMode.Read, valueProviderCallback: _ => 0 /* TODO */, name: "MSGINFO") 98 .WithFlag(20, out rxInterrupt, FieldMode.Read, name: "RXINT") 99 .WithFlag(21, FieldMode.Read, valueProviderCallback: _ => RxFifoIsEmpty, name: "RXEMPTY") 100 .WithFlag(22, FieldMode.Read, valueProviderCallback: _ => RxHeaderAvailable, name: "RXHDR") 101 .WithFlag(23, FieldMode.Read, valueProviderCallback: _ => false /* TODO */, name: "RXERROR") 102 .WithReservedBits(24, 8) 103 }, 104 {(long)Registers.TxProtection, new DoubleWordRegister(this) 105 .WithReservedBits(0, 21) 106 .WithTaggedFlag("UNPROTECTED", 21) 107 .WithTaggedFlag("PRIVILGED", 22) 108 .WithTaggedFlag("NONSECURE", 23) 109 .WithTag("USER", 24, 8) 110 }, 111 {(long)Registers.RxProtection, new DoubleWordRegister(this) 112 .WithReservedBits(0, 21) 113 .WithTaggedFlag("UNPROTECTED", 21) 114 .WithTaggedFlag("PRIVILGED", 22) 115 .WithTaggedFlag("NONSECURE", 23) 116 .WithTag("USER", 24, 8) 117 }, 118 {(long)Registers.TxHeader, new DoubleWordRegister(this) 119 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, value) => { TxHeader = (uint)value; }, name: "TXHEADER") 120 }, 121 {(long)Registers.RxHeader, new DoubleWordRegister(this) 122 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => (uint)RxHeader, name: "RXHEADER") 123 }, 124 {(long)Registers.Config, new DoubleWordRegister(this) 125 .WithFlag(0, out txInterruptEnable, name: "TXINTEN") 126 .WithFlag(1, out rxInterruptEnable, name: "RXINTEN") 127 .WithReservedBits(2, 30) 128 .WithChangeCallback((_, __) => UpdateInterrupts()) 129 }, 130 }; 131 132 var startOffset = (long)Registers.Fifo0; 133 var blockSize = (long)Registers.Fifo1 - (long)Registers.Fifo0; 134 135 for(var index = 0; index < FifoWordSize; index++) 136 { 137 var i = index; 138 139 registerDictionary.Add(startOffset + blockSize*i, 140 new DoubleWordRegister(this) 141 .WithValueField(0, 32, valueProviderCallback: _ => RxFifoDequeue(), writeCallback: (_, value) => { TxFifoEnqueue((uint)value); }, name: $"FIFO{i}") 142 ); 143 } 144 return new DoubleWordRegisterCollection(this, registerDictionary); 145 } 146 147 public long Size => 0x4000; 148 public GPIO RxIRQ { get; } 149 public GPIO TxIRQ { get; } 150 151 #region fields 152 protected readonly Machine machine; 153 protected readonly DoubleWordRegisterCollection registersCollection; 154 private readonly Silabs_SecureElement Silabs_SecureElement; 155 private const uint FifoWordSize = 16; 156 // TODO: according to the design book, TXSTATUS.TXINT field: "Interrupt status (same value as interrupt signal). 157 // High when TX FIFO is not almost-full (enough available space to start sending a message)." 158 // As of now I don't know what "enough available space to send a message" means, so for now I assume a message 159 // needs the whole FIFO. 160 private const uint TxFifoAlmostFullThreshold = 1; 161 private Queue<uint> txFifo; 162 private Queue<uint> rxFifo; 163 private int TxFifoWordsCount => txFifo.Count; 164 private int RxFifoWordsCount => rxFifo.Count; 165 private bool TxFifoIsAlmostFull => (TxFifoWordsCount >= TxFifoAlmostFullThreshold); 166 private bool TxFifoIsFull => (TxFifoWordsCount == FifoWordSize); 167 private bool RxFifoIsFull => (RxFifoWordsCount == FifoWordSize); 168 private bool TxFifoIsEmpty => (TxFifoWordsCount == 0); 169 private bool RxFifoIsEmpty => (RxFifoWordsCount == 0); 170 private bool rxHeaderAvailable = false; 171 private IFlagRegisterField txInterruptEnable; 172 private IFlagRegisterField rxInterruptEnable; 173 private IFlagRegisterField rxInterrupt; 174 175 private uint TxHeader 176 { 177 set 178 { 179 Silabs_SecureElement.TxHeaderSetCallback(value); 180 TxFifoEnqueue(value); 181 } 182 } 183 184 private uint RxHeader 185 { 186 get 187 { 188 uint retValue; 189 if (RxHeaderAvailable) 190 { 191 retValue = RxFifoDequeue(); 192 RxHeaderAvailable = false; 193 } 194 else 195 { 196 // Return an error response code in case the RXHEADER is not available. 197 retValue = Silabs_SecureElement.GetDefaultErrorStatus(); 198 } 199 rxInterrupt.Value = false; 200 UpdateInterrupts(); 201 return retValue; 202 } 203 } 204 205 private bool RxHeaderAvailable 206 { 207 get 208 { 209 return rxHeaderAvailable; 210 } 211 212 set 213 { 214 rxHeaderAvailable = value; 215 } 216 } 217 #endregion 218 219 #region system methods UpdateInterrupts()220 private void UpdateInterrupts() 221 { 222 machine.ClockSource.ExecuteInLock(delegate { 223 // TXINT: Interrupt status (same value as interrupt signal). 224 // High when TX FIFO is not almost-full (enough available space to start sending a message). 225 var irq = txInterruptEnable.Value && !TxFifoIsAlmostFull; 226 if (irq) 227 { 228 this.Log(LogLevel.Noisy, "IRQ TX set"); 229 } 230 TxIRQ.Set(irq); 231 232 // RXINT: Interrupt status (same value as interrupt signal). High when RX FIFO is not almost-empty 233 // or when the end of the message is ready in the FIFO (enough data available to start reading). 234 irq = rxInterruptEnable.Value && rxInterrupt.Value; 235 if (irq) 236 { 237 this.Log(LogLevel.Noisy, "IRQ RX set"); 238 } 239 RxIRQ.Set(irq); 240 }); 241 } 242 TxFifoEnqueue(uint value)243 private void TxFifoEnqueue(uint value) 244 { 245 if (!TxFifoIsFull) 246 { 247 txFifo.Enqueue(value); 248 249 // If true, a command was processed and a response was added to the RX queue. 250 if (Silabs_SecureElement.TxFifoEnqueueCallback(value)) 251 { 252 rxInterrupt.Value = true; 253 RxHeaderAvailable = true; 254 } 255 256 UpdateInterrupts(); 257 } 258 else 259 { 260 this.Log(LogLevel.Error, "TxFifoEnqueue(): queue is FULL!"); 261 } 262 } 263 TxFifoDequeue()264 private uint TxFifoDequeue() 265 { 266 uint ret = 0; 267 268 if (!TxFifoIsEmpty) 269 { 270 ret = txFifo.Dequeue(); 271 this.Log(LogLevel.Info, "TxFifo Dequeued: {0:X}", ret); 272 UpdateInterrupts(); 273 } 274 else 275 { 276 this.Log(LogLevel.Error, "TxFifoDequeue(): queue is EMPTY!"); 277 } 278 279 return ret; 280 } 281 RxFifoEnqueue(uint value)282 private void RxFifoEnqueue(uint value) 283 { 284 if (!RxFifoIsFull) 285 { 286 rxFifo.Enqueue(value); 287 } 288 else 289 { 290 this.Log(LogLevel.Error, "RxFifoEnqueue(): queue is FULL!"); 291 } 292 } 293 RxFifoDequeue()294 private uint RxFifoDequeue() 295 { 296 uint ret = 0; 297 298 if (!RxFifoIsEmpty) 299 { 300 ret = rxFifo.Dequeue(); 301 this.Log(LogLevel.Info, "RxFifo Dequeued: {0:X}", ret); 302 } 303 else 304 { 305 this.Log(LogLevel.Error, "RxFifoDequeue(): queue is EMPTY!"); 306 } 307 308 return ret; 309 } 310 #endregion 311 312 #region enums 313 private enum Registers 314 { 315 Fifo0 = 0x00, 316 Fifo1 = 0x04, 317 Fifo2 = 0x08, 318 Fifo3 = 0x0C, 319 Fifo4 = 0x10, 320 Fifo5 = 0x14, 321 Fifo6 = 0x18, 322 Fifo7 = 0x1C, 323 Fifo8 = 0x20, 324 Fifo9 = 0x24, 325 Fifo10 = 0x28, 326 Fifo11 = 0x2C, 327 Fifo12 = 0x30, 328 Fifo13 = 0x34, 329 Fifo14 = 0x38, 330 Fifo15 = 0x3C, 331 TxStatus = 0x40, 332 RxStatus = 0x44, 333 TxProtection = 0x48, 334 RxProtection = 0x4C, 335 TxHeader = 0x50, 336 RxHeader = 0x54, 337 Config = 0x58, 338 } 339 #endregion 340 } 341 }