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 using Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure.Registers;
9 using Antmicro.Renode.Time;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     public class AmbiqApollo4_Watchdog : BasicDoubleWordPeripheral, IKnownSize
16     {
AmbiqApollo4_Watchdog(IMachine machine)17         public AmbiqApollo4_Watchdog(IMachine machine) : base(machine)
18         {
19             resetTimer = new LimitTimer(machine.ClockSource, 1, this, "Reset", enabled: false, eventEnabled: true, direction: Direction.Ascending, workMode: WorkMode.OneShot);
20             resetTimer.LimitReached += () =>
21             {
22                 if(resetEnabled.Value)
23                 {
24                     this.Log(LogLevel.Warning, "Watchog reset triggered");
25                     machine.RequestReset();
26                 }
27             };
28 
29             DefineRegisters();
30         }
31 
DefineRegisters()32         private void DefineRegisters()
33         {
34             Registers.Configuration.Define(this, 0xFFFF00)
35                 .WithFlag(0, out var timerEnabled, name: "WDTEN")
36                 .WithTaggedFlag("INTEN", 1)
37                 .WithFlag(2, out resetEnabled, name: "RESEN")
38                 .WithTaggedFlag("DSPRESETINTEN", 3)
39                 .WithReservedBits(4, 4)
40                 .WithValueField(8, 8, out var resetLimit, name: "RESVAL")
41                 .WithValueField(16, 8, out var interruptLimit, name: "INTVAL")
42                 .WithEnumField<DoubleWordRegister, ClockSelect>(24, 3, out var clockSelect, name: "CLKSEL")
43                 .WithReservedBits(27, 5)
44                 .WithChangeCallback((_, __) =>
45                     {
46                         var enableTimers = timerEnabled.Value;
47                         long frequency = 1;
48 
49                         switch(clockSelect.Value)
50                         {
51                             case ClockSelect.Off:
52                                 enableTimers = false;
53                                 break;
54                             case ClockSelect._128Hz:
55                                 frequency = 128 * FrequencyMultiplier;
56                                 break;
57                             case ClockSelect._16Hz:
58                                 frequency = 16 * FrequencyMultiplier;
59                                 break;
60                             case ClockSelect._1Hz:
61                                 frequency = 1 * FrequencyMultiplier;
62                                 break;
63                             case ClockSelect._1_16Hz:
64                                 frequency = FrequencyMultiplier / 16;
65                                 break;
66                             default:
67                                 this.Log(LogLevel.Error, "Invalid frequency value: {0}. Timer will be disabled", clockSelect.Value);
68                                 enableTimers = false;
69                                 break;
70                         }
71 
72                         resetTimer.Frequency = frequency;
73                         resetTimer.Limit = resetLimit.Value * FrequencyMultiplier;
74                         resetTimer.Enabled = enableTimers && resetEnabled.Value;
75                     });
76 
77             Registers.Restart.Define(this)
78                 .WithValueField(0, 8, FieldMode.Write, writeCallback: (_, value) =>
79                     {
80                         if(value == WatchdogReloadValue)
81                         {
82                             resetTimer.ResetValue();
83                         }
84                     });
85 
86             Registers.CounterValue.Define(this)
87                 .WithValueField(0, 8, FieldMode.Read, name: "COUNT", valueProviderCallback: _ => TimerValue);
88         }
89 
90         private ulong TimerValue
91         {
92             get
93             {
94                 if(sysbus.TryGetCurrentCPU(out var cpu))
95                 {
96                     cpu.SyncTime();
97                 }
98                 return resetTimer.Value / FrequencyMultiplier;
99             }
100         }
101 
102         public long Size => 0x400;
103 
104         private IFlagRegisterField resetEnabled;
105 
106         private readonly LimitTimer resetTimer;
107 
108         private const ulong WatchdogReloadValue = 0xB2;
109         private const int FrequencyMultiplier = 16;
110 
111         private enum ClockSelect
112         {
113             Off = 0x0,
114             _128Hz = 0x1,
115             _16Hz = 0x2,
116             _1Hz = 0x3,
117             _1_16Hz = 0x4,
118         }
119 
120         private enum Registers
121         {
122             Configuration       = 0x0,      // CFG
123             Restart             = 0x4,      // RSTRT
124             Lock                = 0x8,      // LOCK
125             CounterValue        = 0xC,      // COUNT
126             DSP0Configuration   = 0x10,     // DPS0CFG
127             DSP0Restart         = 0x14,     // DSP0RSTRT
128             DSP0Lock            = 0x18,     // DSP0LOCK
129             DSP0CounterValue    = 0x1C,     // DSP1COUNT
130             DSP1Configuration   = 0x20,     // DPS1CFG
131             DSP1Restart         = 0x24,     // DSP1RSTRT
132             DSP1Lock            = 0x28,     // DSP1LOCK
133             DSP1CounterValue    = 0x2C,     // DSP1COUNT
134             InterruptEnable     = 0x200,    // WDTIEREN
135             InterruptStatus     = 0x204,    // WDTIERSTAT
136             InterruptClear      = 0x208,    // WDTIERCLR
137             InterruptSet        = 0x20C,    // WDTIERSET
138             DSP0InterruptEnable = 0x210,    // DSP0IEREN
139             DSP0InterruptStatus = 0x214,    // DSP0IERSTAT
140             DSP0InterruptClear  = 0x218,    // DSP0IERCLR
141             DSP0InterruptSet    = 0x21C,    // DSP0IERSET
142             DSP1InterruptEnable = 0x220,    // DSP1IEREN
143             DSP1InterruptStatus = 0x224,    // DSP1IERSTAT
144             DSP1InterruptClear  = 0x228,    // DSP1IERCLR
145             DSP1InterruptSet    = 0x22C,    // DSP1IERSET
146         }
147     }
148 }
149