1 //
2 // Copyright (c) 2010-2022 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 STM32L0_PWR : BasicDoubleWordPeripheral, IKnownSize
15     {
STM32L0_PWR(IMachine machine)16         public STM32L0_PWR(IMachine machine) : base(machine)
17         {
18             IRQ = new GPIO();
19 
20             Registers.PowerControl.Define(this, 0x1000)
21                 // The LPSDSR flag has no functionality because low-power run mode is not implemented
22                 .WithFlag(0, name: "LPSDSR")
23                 .WithTaggedFlag("PDDS", 1)
24                 .WithFlag(2, FieldMode.WriteOneToClear, writeCallback: (_, __) => wakeupFlag.Value = false, name: "CWUF")
25                 .WithFlag(3, FieldMode.WriteOneToClear, writeCallback: (_, __) => standbyFlag.Value = false, name: "CSBF")
26                 .WithFlag(4, out pvdEnableFlag, name: "PVDE")
27                 .WithEnumField<DoubleWordRegister, PvdLevelSelection>(5, 3, out pvdLevel, writeCallback: (_, value) =>
28                     {
29                         if(value == PvdLevelSelection.ExternalInput)
30                         {
31                             this.Log(LogLevel.Warning, "External PVD input selected, this is not supported");
32                         }
33                     }, name: "PLS")
34                 .WithFlag(8, name: "DBP")
35                 .WithTaggedFlag("ULP", 9)
36                 .WithTaggedFlag("FWU", 10)
37                 .WithEnumField<DoubleWordRegister, VoltageScalingRangeSelection>(11, 2, out vosValue, name: "VOS", writeCallback: (oldValue, value) =>
38                     {
39                         if(value == VoltageScalingRangeSelection.Forbidden)
40                         {
41                             this.Log(LogLevel.Warning, "Trying to set forbidden VOS value, ignoring write");
42                             vosValue.Value = oldValue;
43                         }
44                     })
45                 .WithTaggedFlag("DS_EE_KOFF", 13)
46                 .WithTaggedFlag("LPRUN", 14)
47                 .WithReservedBits(15, 1)
48                 .WithTaggedFlag("LPDS", 16)
49                 .WithReservedBits(17, 15);
50 
51             Registers.PowerControlStatus.Define(this, 0x8)
52                 .WithFlag(0, out wakeupFlag, FieldMode.Read, name: "WUF")
53                 .WithFlag(1, out standbyFlag, FieldMode.Read, name: "SBF")
54                 .WithFlag(2, out pvdoFlag, FieldMode.Read, name: "PVDO")
55                 .WithFlag(3, FieldMode.Read, valueProviderCallback: _ => true, name: "VREFINTRDYF")
56                 .WithFlag(4, FieldMode.Read, valueProviderCallback: _ => false, name: "VOSF")
57                 .WithFlag(5, FieldMode.Read, valueProviderCallback: _ => false, name: "REGLPF")
58                 .WithReservedBits(6, 2)
59                 .WithTaggedFlag("EWUP1", 8)
60                 .WithTaggedFlag("EWUP2", 9)
61                 .WithTaggedFlag("EWUP3", 10)
62                 .WithReservedBits(11, 21);
63 
64             Reset();
65         }
66 
Reset()67         public override void Reset()
68         {
69             base.Reset();
70             IRQ.Unset();
71             Voltage = 3.3;
72             prevPvdo = false;
73         }
74 
75         public long Size => 0x400;
76         public GPIO IRQ { get; }
77 
78         public double? ThresholdVoltage
79         {
80             get
81             {
82                 if(pvdLevel.Value == PvdLevelSelection.ExternalInput)
83                 {
84                     return null;
85                 }
86                 else
87                 {
88                     return 1.9 + (int)pvdLevel.Value * 0.2;
89                 }
90             }
91         }
92 
93         public double Voltage
94         {
95             get
96             {
97                 return voltage;
98             }
99             set
100             {
101                 voltage = value;
102                 UpdatePvd();
103             }
104         }
105 
106         public PvdLevelSelection PvdLevel
107         {
108             get
109             {
110                 return pvdLevel.Value;
111             }
112             set
113             {
114                 pvdLevel.Value = value;
115                 UpdatePvd();
116             }
117         }
118 
UpdatePvd()119         private void UpdatePvd()
120         {
121             bool pvdo;
122             if(prevPvdo && Voltage > ThresholdVoltage + Hysteresis)
123             {
124                 // PVDO should be false if the voltage was below and is above the threshold
125                 pvdo = false;
126             }
127             else if(!prevPvdo && Voltage < ThresholdVoltage - Hysteresis)
128             {
129                 // PVDO should be true if the voltage was above and is below the threshold
130                 pvdo = true;
131             }
132             else
133             {
134                 // No change (within hysteresis)
135                 pvdo = prevPvdo;
136             }
137             prevPvdo = pvdo;
138             pvdo &= pvdEnableFlag.Value;
139 
140             pvdoFlag.Value = pvdo;
141             IRQ.Set(pvdo);
142         }
143 
144         private IFlagRegisterField wakeupFlag;
145         private IFlagRegisterField standbyFlag;
146         private IFlagRegisterField pvdoFlag;
147         private IFlagRegisterField pvdEnableFlag;
148         private IEnumRegisterField<PvdLevelSelection> pvdLevel;
149         private IEnumRegisterField<VoltageScalingRangeSelection> vosValue;
150         private double voltage;
151         private bool prevPvdo;
152 
153         private const double Hysteresis = 0.1;
154 
155         public enum PvdLevelSelection
156         {
157             V1_9,
158             V2_1,
159             V2_3,
160             V2_5,
161             V2_7,
162             V2_9,
163             V3_1,
164             ExternalInput,
165         }
166 
167         private enum Registers
168         {
169             PowerControl = 0x00,
170             PowerControlStatus = 0x04,
171         }
172 
173         private enum VoltageScalingRangeSelection
174         {
175             Forbidden,
176             Range1_V1_8,
177             Range2_V1_5,
178             Range3_V1_2,
179         }
180     }
181 }
182