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