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 8 using System; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Peripherals.Timers; 15 16 namespace Antmicro.Renode.Peripherals.Miscellaneous 17 { 18 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 19 public sealed class STM32H7_HardwareSemaphore : BasicDoubleWordPeripheral, IKnownSize 20 { STM32H7_HardwareSemaphore(IMachine machine)21 public STM32H7_HardwareSemaphore(IMachine machine) : base(machine) 22 { 23 this.IRQ = new GPIO(); 24 25 for(var i = 0; i < SemaphoreCount; ++i) 26 { 27 semaphores[i] = new Semaphore(); 28 } 29 30 DefineRegisters(); 31 } 32 Reset()33 public override void Reset() 34 { 35 lock(lockObject) 36 { 37 base.Reset(); 38 foreach(var semaphore in semaphores) 39 { 40 semaphore.Reset(); 41 } 42 IRQ.Unset(); 43 } 44 } 45 ReadDoubleWord(long offset)46 public override uint ReadDoubleWord(long offset) 47 { 48 lock(lockObject) 49 { 50 return RegistersCollection.Read(offset); 51 } 52 } 53 WriteDoubleWord(long offset, uint value)54 public override void WriteDoubleWord(long offset, uint value) 55 { 56 lock(lockObject) 57 { 58 RegistersCollection.Write(offset, value); 59 } 60 } 61 62 public GPIO IRQ { get; } 63 64 public long Size => 0x400; 65 DefineRegisters()66 private void DefineRegisters() 67 { 68 // Two-step write lock 69 // Locking happens by writing a LOCK=1 value, along with the identifiers 70 // Reading this register allows checking the status of the lock 71 Registers.Semaphore.DefineMany(this, SemaphoreCount, (reg, idx) => 72 { 73 reg 74 .WithValueField(0, 8, out var processIdBits, name: "PROCID", valueProviderCallback: _ => semaphores[idx].ProcessID) 75 .WithValueField(8, 8, out var masterIdBits, name: "MASTERID", valueProviderCallback: _ => semaphores[idx].MasterID) 76 .WithReservedBits(16, 15) 77 .WithFlag(31, out var lockBits, name: "LOCK", valueProviderCallback: _ => semaphores[idx].Locked) 78 .WithWriteCallback((_, __) => 79 { 80 semaphores[idx].WriteLock(lockBits.Value, (uint)processIdBits.Value, (uint)masterIdBits.Value); 81 }); 82 }); 83 84 // One-step lock, read-only 85 // Locking happens automatically just by reading the register 86 Registers.ReadLockSemaphore.DefineMany(this, SemaphoreCount, (reg, idx) => 87 { 88 reg 89 .WithValueField(0, 8, FieldMode.Read, name: "PROCID", valueProviderCallback: _ => semaphores[idx].ProcessID) 90 .WithValueField(8, 8, FieldMode.Read, name: "MASTERID", valueProviderCallback: _ => semaphores[idx].MasterID) 91 .WithReservedBits(16, 15) 92 .WithFlag(31, FieldMode.Read, name: "LOCK", valueProviderCallback: _ => semaphores[idx].Locked) 93 .WithReadCallback((_, __) => 94 { 95 // When software uses 1 Step locking method, hardware is expected 96 // to read MasterID from AHB bus, which is a constant value dependent 97 // on the CPU type. There are 2 possible values: 0x3 for Cortex-M7 98 // and 0x1 for Cortex-M4 99 if(!machine.SystemBus.TryGetCurrentCPU(out var cpu)) 100 { 101 this.Log(LogLevel.Warning, "Failed getting current CPU"); 102 return; 103 } 104 105 var masterId = 0u; 106 if(cpu.Model == "cortex-m4" || cpu.Model == "cortex-m4f") 107 { 108 masterId = 0x1; 109 } 110 else if(cpu.Model == "cortex-m7") 111 { 112 masterId = 0x3; 113 } 114 else 115 { 116 this.Log(LogLevel.Warning, "Unsupported cpu model: {0}", cpu.Model); 117 return; 118 } 119 semaphores[idx].ReadLock(masterId); 120 }); 121 }); 122 } 123 124 private readonly object lockObject = new object(); 125 private readonly Semaphore[] semaphores = new Semaphore[SemaphoreCount]; 126 127 private const uint SemaphoreCount = 32; 128 129 private enum Registers 130 { 131 Semaphore = 0x0, 132 // The above serves as a base offset, 32 double word registers follow 133 ReadLockSemaphore = 0x80, 134 // The above serves as a base offset, 32 double word registers follow 135 InterruptEnable = 0x100, 136 InterruptClear = 0x104, 137 InterruptStatus = 0x108, 138 MaskedInterruptStatus = 0x10c, 139 Clear = 0x140, 140 Key = 0x144 141 } 142 143 private class Semaphore 144 { Semaphore()145 public Semaphore() 146 { 147 Reset(); 148 } 149 150 // Reset the state of the semaphore Reset()151 public void Reset() 152 { 153 Locked = false; 154 ProcessID = 0; 155 MasterID = 0; 156 } 157 158 // Attempt to lock the semaphore via 1-step read lock ReadLock(uint masterId)159 public void ReadLock(uint masterId) 160 { 161 // Process ID is unused and is always 0 in this case 162 TryLock(0x0000, masterId); 163 } 164 165 // Handles a write directed at the 2-step write lock register WriteLock(bool lockBit, uint processId, uint masterId)166 public void WriteLock(bool lockBit, uint processId, uint masterId) 167 { 168 if(lockBit) 169 { 170 TryUnlock(processId, masterId); 171 } 172 else 173 { 174 TryLock(processId, masterId); 175 } 176 } 177 178 public bool Locked { get; private set; } 179 public uint ProcessID { get; private set; } 180 public uint MasterID { get; private set; } 181 182 // Tries to acquire a lock on the semaphore, with the given process ID and master ID TryLock(uint processId, uint masterId)183 private void TryLock(uint processId, uint masterId) 184 { 185 if(Locked) 186 { 187 return; 188 } 189 190 Locked = true; 191 ProcessID = processId; 192 MasterID = masterId; 193 } 194 195 // Tries to unlock the semaphore with the given credentials 196 // The semaphore will only be unlocked if it was previously locked, and its process ID and the master ID 197 // matches the ones used for unlocking. TryUnlock(uint processId, uint masterId)198 private void TryUnlock(uint processId, uint masterId) 199 { 200 if(!Locked) 201 { 202 return; 203 } 204 if(ProcessID != processId) 205 { 206 return; 207 } 208 if(MasterID != masterId) 209 { 210 return; 211 } 212 213 Reset(); 214 } 215 } 216 } 217 } 218