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 using System;
8 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Time;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Peripherals.Bus;
14 
15 namespace Antmicro.Renode.Peripherals.Timers
16 {
17     [AllowedTranslations(AllowedTranslation.ByteToWord)]
18     public class MSP430F2XXX_Watchdog : BasicWordPeripheral, IKnownSize
19     {
MSP430F2XXX_Watchdog(IMachine machine, long baseFrequency)20         public MSP430F2XXX_Watchdog(IMachine machine, long baseFrequency) : base(machine)
21         {
22             mainTimer = new LimitTimer(machine.ClockSource, baseFrequency, this, "wdt", limit: 0xFFFF, workMode: WorkMode.Periodic);
23             mainTimer.LimitReached += LimitReached;
24 
25             InterruptEnableRegister = new ByteRegister(this);
26             InterruptStatusRegister = new ByteRegister(this);
27 
28             DefineRegisters();
29             Reset();
30         }
31 
Reset()32         public override void Reset()
33         {
34             base.Reset();
35 
36             mainTimer.Reset();
37             UpdateLimit(Interval.Default);
38         }
39 
40         [ConnectionRegionAttribute("interruptEnable")]
WriteByteToInterruptEnable(long offset, byte value)41         public void WriteByteToInterruptEnable(long offset, byte value)
42         {
43             if(offset != 0)
44             {
45                 this.Log(LogLevel.Warning, "Illegal write access at non-zero offset (0x{0:X}) to interruptEnable region", offset);
46                 return;
47             }
48             // NOTE: This region is single byte wide, so we are ignoring offset argument
49             InterruptEnableRegister.Write(0, value);
50         }
51 
52         [ConnectionRegionAttribute("interruptEnable")]
ReadByteFromInterruptEnable(long offset)53         public byte ReadByteFromInterruptEnable(long offset)
54         {
55             if(offset != 0)
56             {
57                 this.Log(LogLevel.Warning, "Illegal read access at non-zero offset (0x{0:X}) to interruptEnable region", offset);
58             }
59             // NOTE: This region is single byte wide, so we are ignoring offset argument
60             return InterruptEnableRegister.Read();
61         }
62 
63         [ConnectionRegionAttribute("interruptStatus")]
WriteByteToInterruptStatus(long offset, byte value)64         public void WriteByteToInterruptStatus(long offset, byte value)
65         {
66             if(offset != 0)
67             {
68                 this.Log(LogLevel.Warning, "Illegal write access at non-zero offset (0x{0:X}) to interruptStatus region", offset);
69                 return;
70             }
71             // NOTE: This region is single byte wide, so we are ignoring offset argument
72             InterruptStatusRegister.Write(0, value);
73             UpdateInterrupts();
74         }
75 
76         [ConnectionRegionAttribute("interruptStatus")]
ReadByteFromInterruptStatus(long offset)77         public byte ReadByteFromInterruptStatus(long offset)
78         {
79             if(offset != 0)
80             {
81                 this.Log(LogLevel.Warning, "Illegal read access at non-zero offset (0x{0:X}) to interruptStatus region", offset);
82             }
83             // NOTE: This region is single byte wide, so we are ignoring offset argument
84             return InterruptStatusRegister.Read();
85         }
86 
87         public long Size => 0x02;
88 
89         public GPIO IntervalIRQ { get; } = new GPIO();
90 
UpdateInterrupts()91         private void UpdateInterrupts()
92         {
93             var interrupt = intervalInterruptPending.Value && intervalInterruptEnabled.Value;
94             IntervalIRQ.Set(interrupt);
95         }
96 
LimitReached()97         private void LimitReached()
98         {
99             if(intervalMode.Value)
100             {
101                 intervalInterruptPending.Value = true;
102                 UpdateInterrupts();
103                 return;
104             }
105 
106             machine.RequestReset();
107         }
108 
DefineRegisters()109         private void DefineRegisters()
110         {
111             Registers.Control.Define(this, 0x6900)
112                 .WithEnumField<WordRegister, Interval>(0, 2, name: "WDTISx",
113                     changeCallback: (_, value) => UpdateLimit(value))
114                 // NOTE: Change of clock source is not supported in runtime
115                 .WithFlag(2, name: "WDTSSEL")
116                 .WithFlag(3, FieldMode.Read | FieldMode.WriteOneToClear, name: "WDTCNTCL",
117                     writeCallback: (_, value) => mainTimer.Value = 0)
118                 .WithFlag(4, out intervalMode, name: "WDTTMSEL")
119                 .WithTaggedFlag("WDTNMI", 5)
120                 .WithTaggedFlag("WDTNMIES", 6)
121                 .WithFlag(7, name: "WDTHOLD",
122                     changeCallback: (_, value) => mainTimer.Enabled = !value)
123                 .WithValueField(8, 8, name: "WDTPW",
124                     valueProviderCallback: _ => 0x69,
125                     writeCallback: (_, value) =>
126                     {
127                         if(value != WatchdogPassword)
128                         {
129                             machine.RequestReset();
130                         }
131                     })
132             ;
133 
134             InterruptEnableRegister
135                 .WithFlag(0, out intervalInterruptEnabled, name: "WDTIE")
136                 .WithReservedBits(1, 3)
137                 .WithTaggedFlag("NMIIE", 4)
138                 .WithReservedBits(5, 3)
139                 .WithChangeCallback((_, __) => UpdateInterrupts())
140             ;
141 
142             InterruptStatusRegister
143                 .WithFlag(0, out intervalInterruptPending, name: "WDTIFG")
144                 .WithReservedBits(1, 3)
145                 .WithTaggedFlag("NMIIFG", 4)
146                 .WithReservedBits(5, 3)
147                 .WithChangeCallback((_, __) => UpdateInterrupts())
148             ;
149         }
150 
UpdateLimit(Interval interval)151         private void UpdateLimit(Interval interval)
152         {
153             switch(interval)
154             {
155                 case Interval._32768:
156                     mainTimer.Limit = 32768;
157                     break;
158 
159                 case Interval._8192:
160                     mainTimer.Limit = 8192;
161                     break;
162 
163                 case Interval._512:
164                     mainTimer.Limit = 512;
165                     break;
166 
167                 case Interval._64:
168                     mainTimer.Limit = 64;
169                     break;
170 
171                 default:
172                     throw new Exception("unreachable");
173             }
174         }
175 
176         private ByteRegister InterruptEnableRegister { get; }
177         private ByteRegister InterruptStatusRegister { get; }
178 
179         private IFlagRegisterField intervalMode;
180         private IFlagRegisterField intervalInterruptPending;
181         private IFlagRegisterField intervalInterruptEnabled;
182 
183         private readonly LimitTimer mainTimer;
184 
185         private const uint WatchdogPassword = 0x5A;
186 
187         private enum Interval
188         {
189             _32768 = 0,
190             _8192,
191             _512,
192             _64,
193             Default = Interval._32768,
194         }
195 
196         private enum Registers
197         {
198             Control = 0x00,
199         }
200     }
201 }
202