1 //
2 // Copyright (c) 2010-2023 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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Logging;
11 
12 namespace Antmicro.Renode.Peripherals.Miscellaneous
13 {
14     public class STM32WBA_PWR : BasicDoubleWordPeripheral, IKnownSize
15     {
STM32WBA_PWR(IMachine machine)16         public STM32WBA_PWR(IMachine machine) : base(machine)
17         {
18             IRQ = new GPIO();
19 
20             DefineRegisters();
21             Reset();
22         }
23 
Reset()24         public override void Reset()
25         {
26             base.Reset();
27             IRQ.Unset();
28             Voltage = 3.3;
29             prevPvdo = false;
30         }
31 
32         public long Size => 0x400;
33         public GPIO IRQ { get; }
34 
35         public double? ThresholdVoltage { get => PvdLevelToVoltage(pvdLevel.Value); }
36 
37         public double Voltage
38         {
39             get
40             {
41                 return voltage;
42             }
43             set
44             {
45                 voltage = value;
46                 UpdatePvd();
47             }
48         }
49 
50         public PvdLevelSelection PvdLevel
51         {
52             get
53             {
54                 return pvdLevel.Value;
55             }
56             set
57             {
58                 pvdLevel.Value = value;
59                 UpdatePvd();
60             }
61         }
62 
DefineRegisters()63         private void DefineRegisters()
64         {
65             Registers.Control1.Define(this)
66                 .WithTag("LPMS", 0, 3)
67                 .WithReservedBits(3, 2)
68                 .WithTaggedFlag("R2RSB1", 5)
69                 .WithReservedBits(6, 1)
70                 .WithTaggedFlag("ULPMEN", 7)
71                 .WithReservedBits(8, 1)
72                 .WithTaggedFlag("RADIORSB", 9)
73                 .WithReservedBits(10, 2)
74                 .WithTaggedFlag("R1RSB1", 12)
75                 .WithReservedBits(13, 19);
76             Registers.Control2.Define(this)
77                 .WithTaggedFlag("SRAM1PDS1", 0)
78                 .WithReservedBits(1, 3)
79                 .WithTaggedFlag("SRAM2PDS1", 4)
80                 .WithReservedBits(5, 3)
81                 .WithTaggedFlag("ICRAMPDS", 8)
82                 .WithReservedBits(9, 5)
83                 .WithTaggedFlag("FLASHFWU", 14)
84                 .WithReservedBits(15, 17);
85             Registers.Control3.Define(this)
86                 .WithReservedBits(0, 2)
87                 .WithTaggedFlag("FSTEN", 2)
88                 .WithReservedBits(3, 29);
89             Registers.VoltageScaling.Define(this, 0x0000_8000)
90                 .WithReservedBits(0, 15)
91                 .WithTaggedFlag("VOSRDY", 15)
92                 .WithEnumField<DoubleWordRegister, VoltageScalingRangeSelection>(16, 1, out vosValue, name: "VOS")
93                 .WithReservedBits(17, 15);
94             Registers.SupplyVoltageMonitoringControl.Define(this)
95                 .WithReservedBits(0, 4)
96                 .WithFlag(4, out pvdEnableFlag, name: "PVDE")
97                 .WithEnumField<DoubleWordRegister, PvdLevelSelection>(5, 3, out pvdLevel, writeCallback: (_, value) =>
98                     {
99                         if(value == PvdLevelSelection.ExternalInput)
100                         {
101                             this.Log(LogLevel.Warning, "External PVD input selected, this is not supported");
102                         }
103                         UpdatePvd();
104                     }, name: "PVDLS")
105                 .WithReservedBits(8, 24);
106             Registers.WakeupControl1.Define(this)
107                 .WithTaggedFlag("WUPEN1", 0)
108                 .WithTaggedFlag("WUPEN2", 1)
109                 .WithTaggedFlag("WUPEN3", 2)
110                 .WithTaggedFlag("WUPEN4", 3)
111                 .WithTaggedFlag("WUPEN5", 4)
112                 .WithTaggedFlag("WUPEN6", 5)
113                 .WithTaggedFlag("WUPEN7", 6)
114                 .WithTaggedFlag("WUPEN8", 7)
115                 .WithReservedBits(8, 24);
116             Registers.WakeupControl2.Define(this)
117                 .WithTaggedFlag("WUPP1", 0)
118                 .WithTaggedFlag("WUPP2", 1)
119                 .WithTaggedFlag("WUPP3", 2)
120                 .WithTaggedFlag("WUPP4", 3)
121                 .WithTaggedFlag("WUPP5", 4)
122                 .WithTaggedFlag("WUPP6", 5)
123                 .WithTaggedFlag("WUPP7", 6)
124                 .WithTaggedFlag("WUPP8", 7)
125                 .WithReservedBits(8, 24);
126             Registers.WakeupControl3.Define(this)
127                 .WithTag("WUSEL1", 0, 2)
128                 .WithTag("WUSEL2", 2, 2)
129                 .WithTag("WUSEL3", 4, 2)
130                 .WithTag("WUSEL4", 6, 2)
131                 .WithTag("WUSEL5", 8, 2)
132                 .WithTag("WUSEL6", 10, 2)
133                 .WithTag("WUSEL7", 12, 2)
134                 .WithTag("WUSEL8", 14, 2)
135                 .WithReservedBits(16, 16);
136             Registers.BackupDomain.Define(this)
137                 .WithFlag(0, out backupDomainDisabled, name: "DBP")
138                 .WithReservedBits(1, 31);
139             Registers.SecurityConfiguration.Define(this)
140                 .WithTaggedFlag("WUP1SEC", 0)
141                 .WithTaggedFlag("WUP2SEC", 1)
142                 .WithTaggedFlag("WUP3SEC", 2)
143                 .WithTaggedFlag("WUP4SEC", 3)
144                 .WithTaggedFlag("WUP5SEC", 4)
145                 .WithTaggedFlag("WUP6SEC", 5)
146                 .WithTaggedFlag("WUP7SEC", 6)
147                 .WithTaggedFlag("WUP8SEC", 7)
148                 .WithReservedBits(8, 4)
149                 .WithTaggedFlag("LPMSEC", 12)
150                 .WithTaggedFlag("VDMSEC", 13)
151                 .WithTaggedFlag("VBSEC", 14)
152                 .WithReservedBits(15, 17);
153             Registers.PrivilegeControl.Define(this)
154                 .WithTaggedFlag("SPRIV", 0)
155                 .WithTaggedFlag("NSPRIV", 1)
156                 .WithReservedBits(2, 30);
157             Registers.Status.Define(this)
158                 .WithFlag(0, FieldMode.WriteOneToClear, name: "CSSF",
159                     writeCallback: (_, __) =>
160                     {
161                         standbyFlag.Value = false;
162                         stopFlag.Value = false;
163                     })
164                 .WithFlag(1, out stopFlag, FieldMode.Read, name: "STOPF")
165                 .WithFlag(2, out standbyFlag, FieldMode.Read, name: "SBF")
166                 .WithReservedBits(3, 29);
167             Registers.SupplyVoltageMonitoringStatus.Define(this, 0x0000_8000)
168                 .WithReservedBits(0, 4)
169                 .WithFlag(4, out pvdoFlag, FieldMode.Read, name: "PVDO")
170                 .WithReservedBits(5, 10)
171                 .WithTaggedFlag("ACTVOSRDY", 15)
172                 .WithTaggedFlag("ACTVOS", 16)
173                 .WithReservedBits(17, 15);
174             Registers.WakeupStatus.Define(this)
175                 .WithFlags(0, 8, out wakeupFlags, FieldMode.Read, name: "WUF")
176                 .WithReservedBits(8, 24);
177             Registers.WakeupStatusClear.Define(this)
178                 .WithFlags(0, 8, FieldMode.WriteOneToClear, name: "CWUF",
179                     writeCallback: (idx, _, __) => wakeupFlags[idx].Value = false)
180                 .WithReservedBits(8, 24);
181             Registers.GpioAStandbyEnable.Define(this)
182                 .WithTag("EN[0:3]", 0, 4)
183                 .WithReservedBits(4, 1)
184                 .WithTag("EN[5:15]", 5, 11)
185                 .WithReservedBits(16, 16);
186             Registers.GpioAStandbyStatus.Define(this)
187                 .WithTag("RET[0:3]", 0, 4)
188                 .WithReservedBits(4, 1)
189                 .WithTag("RET[5:15]", 5, 11)
190                 .WithReservedBits(16, 16);
191             Registers.GpioBStandbyEnable.Define(this)
192                 .WithTag("EN[0:15]", 0, 16)
193                 .WithReservedBits(16, 16);
194             Registers.GpioBStandbyStatus.Define(this)
195                 .WithTag("RET[0:15]", 0, 16)
196                 .WithReservedBits(16, 16);
197             Registers.GpioCStandbyEnable.Define(this)
198                 .WithReservedBits(0, 13)
199                 .WithTag("EN[13:15]", 13, 3)
200                 .WithReservedBits(16, 16);
201             Registers.GpioCStandbyStatus.Define(this)
202                 .WithReservedBits(0, 13)
203                 .WithTag("RET[13:15]", 13, 3)
204                 .WithReservedBits(16, 16);
205             Registers.GpioHStandbyEnable.Define(this)
206                 .WithReservedBits(0, 3)
207                 .WithTaggedFlag("EN3", 3)
208                 .WithReservedBits(4, 28);
209             Registers.GpioHStandbyStatus.Define(this)
210                 .WithReservedBits(0, 3)
211                 .WithTaggedFlag("RET3", 3)
212                 .WithReservedBits(4, 28);
213             Registers.RadioStatusAndControl.Define(this)
214                 .WithTag("MODE", 0, 2)
215                 .WithTaggedFlag("PHYMODE", 2)
216                 .WithTaggedFlag("ENCMODE", 3)
217                 .WithReservedBits(4, 4)
218                 .WithTag("RFVDDHPA", 8, 5)
219                 .WithReservedBits(13, 2)
220                 .WithTaggedFlag("REGPARDYVDDRFPA", 15)
221                 .WithReservedBits(16, 16);
222         }
223 
PvdLevelToVoltage(PvdLevelSelection level)224         private double? PvdLevelToVoltage(PvdLevelSelection level)
225         {
226             switch(level)
227             {
228             case PvdLevelSelection.V2_0:
229                 return 2.0;
230             case PvdLevelSelection.V2_2:
231                 return 2.2;
232             case PvdLevelSelection.V2_4:
233                 return 2.4;
234             case PvdLevelSelection.V2_5:
235                 return 2.5;
236             case PvdLevelSelection.V2_6:
237                 return 2.6;
238             case PvdLevelSelection.V2_8:
239                 return 2.8;
240             case PvdLevelSelection.V2_9:
241                 return 2.9;
242             case PvdLevelSelection.ExternalInput:
243             default:
244                 return null;
245             }
246         }
247 
UpdatePvd()248         private void UpdatePvd()
249         {
250             if(PvdLevel == PvdLevelSelection.ExternalInput)
251             {
252                 // External input is not supported yet, skip updating the pvd
253                 return;
254             }
255 
256             bool pvdo;
257             if(prevPvdo && Voltage > ThresholdVoltage + Hysteresis)
258             {
259                 // PVDO should be false if the voltage was below and is above the threshold
260                 pvdo = false;
261             }
262             else if(!prevPvdo && Voltage < ThresholdVoltage - Hysteresis)
263             {
264                 // PVDO should be true if the voltage was above and is below the threshold
265                 pvdo = true;
266             }
267             else
268             {
269                 // No change (within hysteresis)
270                 pvdo = prevPvdo;
271             }
272             prevPvdo = pvdo;
273             pvdo &= pvdEnableFlag.Value;
274 
275             pvdoFlag.Value = pvdo;
276             IRQ.Set(pvdo);
277         }
278 
279         private IFlagRegisterField[] wakeupFlags = new IFlagRegisterField[8];
280         private IFlagRegisterField standbyFlag;
281         private IFlagRegisterField stopFlag;
282         private IFlagRegisterField pvdoFlag;
283         private IFlagRegisterField pvdEnableFlag;
284         private IEnumRegisterField<PvdLevelSelection> pvdLevel;
285         private IEnumRegisterField<VoltageScalingRangeSelection> vosValue;
286         private IFlagRegisterField backupDomainDisabled;
287         private double voltage;
288         private bool prevPvdo;
289 
290         private const double Hysteresis = 0.1;
291 
292         public enum PvdLevelSelection
293         {
294             V2_0          = 0b000,
295             V2_2          = 0b001,
296             V2_4          = 0b010,
297             V2_5          = 0b011,
298             V2_6          = 0b100,
299             V2_8          = 0b101,
300             V2_9          = 0b110,
301             ExternalInput = 0b111,
302         }
303 
304         private enum Registers
305         {
306             Control1                       = 0x00,
307             Control2                       = 0x04,
308             Control3                       = 0x08,
309             VoltageScaling                 = 0x0C,
310             SupplyVoltageMonitoringControl = 0x10,
311             WakeupControl1                 = 0x14,
312             WakeupControl2                 = 0x18,
313             WakeupControl3                 = 0x1C,
314             // Reserved                    = 0x20 - 0x24
315             BackupDomain                   = 0x28,
316             SecurityConfiguration          = 0x30,
317             PrivilegeControl               = 0x34,
318             Status                         = 0x38,
319             SupplyVoltageMonitoringStatus  = 0x3C,
320             // Reserved                    = 0x40
321             WakeupStatus                   = 0x44,
322             WakeupStatusClear              = 0x48,
323             // Reserved                    = 0x4C
324             GpioAStandbyEnable             = 0x50,
325             GpioAStandbyStatus             = 0x54,
326             GpioBStandbyEnable             = 0x58,
327             GpioBStandbyStatus             = 0x5C,
328             GpioCStandbyEnable             = 0x60,
329             GpioCStandbyStatus             = 0x64,
330             // Reserved                    = 0x68 - 0x84
331             GpioHStandbyEnable             = 0x88,
332             GpioHStandbyStatus             = 0x8C,
333             // Reserved                    = 0x90 - 0xFC
334             RadioStatusAndControl          = 0x100,
335         }
336 
337         private enum VoltageScalingRangeSelection
338         {
339             LowestPower,
340             HighestFrequency,
341         }
342     }
343 }
344