1 // 2 // Copyright (c) 2010-2025 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.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.Bus; 13 using Antmicro.Renode.Peripherals.Miscellaneous; 14 using Antmicro.Renode.Time; 15 16 namespace Antmicro.Renode.Peripherals.Timers 17 { 18 public class SAM4S_WDT : LimitTimer, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 19 { SAM4S_WDT(IMachine machine, SAM4S_RSTC resetController, long slowClockFrequency = 32768)20 public SAM4S_WDT(IMachine machine, SAM4S_RSTC resetController, long slowClockFrequency = 32768) 21 : base(machine.ClockSource, slowClockFrequency, enabled: false, divider: 128, limit: MaximumWatchdogValue, workMode: WorkMode.Periodic, autoUpdate: true, eventEnabled: true) 22 { 23 if(resetController == null) 24 { 25 throw new ConstructionException($"'{nameof(resetController)}' was null"); 26 } 27 28 RegistersCollection = new DoubleWordRegisterCollection(this); 29 this.resetController = resetController; 30 this.machine = machine; 31 32 DefineRegisters(); 33 34 LimitReached += () => 35 { 36 underflowPending.Value = true; 37 TriggerWatchdogFault(); 38 }; 39 } 40 Reset()41 public override void Reset() 42 { 43 base.Reset(); 44 45 modeRegisterLocked = false; 46 RegistersCollection.Reset(); 47 IRQ.Unset(); 48 } 49 ReadDoubleWord(long offset)50 public uint ReadDoubleWord(long offset) 51 { 52 return RegistersCollection.Read(offset); 53 } 54 WriteDoubleWord(long offset, uint value)55 public void WriteDoubleWord(long offset, uint value) 56 { 57 if(offset == (uint)Registers.Mode && modeRegisterLocked) 58 { 59 this.Log(LogLevel.Warning, "Tried to write WDT_MR register after it has been locked"); 60 return; 61 } 62 63 RegistersCollection.Write(offset, value); 64 } 65 66 public DoubleWordRegisterCollection RegistersCollection { get; } 67 68 public long Size => 0x10; 69 70 public GPIO IRQ { get; } = new GPIO(); 71 TriggerWatchdogFault()72 private void TriggerWatchdogFault() 73 { 74 if(interruptEnabled.Value) 75 { 76 IRQ.Set(); 77 } 78 79 if(watchdogReset.Value) 80 { 81 resetController.InvokeReset(SAM4S_RSTC.ResetType.WatchdogReset, resetProcessor: true, resetPeripherals: !resetOnlyProcessor.Value); 82 } 83 } 84 DefineRegisters()85 private void DefineRegisters() 86 { 87 Registers.Control.Define(this) 88 .WithFlag(0, out var resetPending, name: "WDRSTT") 89 .WithReservedBits(1, 23) 90 .WithValueField(24, 8, out var providedKey, name: "KEY") 91 .WithWriteCallback((_, __) => 92 { 93 if(providedKey.Value != WatchdogPassword) 94 { 95 this.Log(LogLevel.Debug, "Software tried to modify WDT_CR with invalid password: {0:X} instead of {1:X}", 96 providedKey.Value, WatchdogPassword); 97 return; 98 } 99 100 if(machine.SystemBus.TryGetCurrentCPU(out var cpu)) 101 { 102 cpu.SyncTime(); 103 } 104 105 if(Value > watchdogDelta.Value) 106 { 107 this.Log(LogLevel.Info, "Tried to refresh watchdog while not in correct interval: {0} > {1}", Value, watchdogDelta.Value); 108 errorPending.Value = true; 109 TriggerWatchdogFault(); 110 return; 111 } 112 113 if(resetPending.Value) 114 { 115 Value = Limit; 116 } 117 118 resetPending.Value = false; 119 }) 120 ; 121 122 // NOTE: Only first write is accepted by this register, 123 // after first write has been handled this register behaves as read-only. 124 Registers.Mode.Define(this, 0x3FFF2FFF) 125 .WithValueField(0, 12, name: "WDV", 126 valueProviderCallback: _ => Value, 127 writeCallback: (_, value) => Limit = value) 128 // NOTE: We don't have to update IRQ on interruptEnabled as this value 129 // can be only changed once 130 .WithFlag(12, out interruptEnabled, name: "WDIFEN") 131 .WithFlag(13, out watchdogReset, name: "WDRSTEN") 132 .WithFlag(14, out resetOnlyProcessor, name: "WDRPROC") 133 .WithFlag(15, name: "WDDIS", 134 valueProviderCallback: _ => !Enabled, 135 writeCallback: (_, value) => Enabled = !value) 136 .WithValueField(16, 12, out watchdogDelta, name: "WDD") 137 .WithTaggedFlag("WDDBGHLT", 28) 138 .WithTaggedFlag("WDIDLEHLT", 29) 139 .WithReservedBits(30, 2) 140 .WithWriteCallback((_, __) => modeRegisterLocked = true) 141 ; 142 143 Registers.Status.Define(this, 0x00000000) 144 .WithFlag(0, out underflowPending, FieldMode.ReadToClear, name: "WDUNF") 145 .WithFlag(1, out errorPending, FieldMode.ReadToClear, name: "WDERR") 146 .WithReservedBits(2, 30) 147 .WithReadCallback((_, __) => 148 { 149 IRQ.Unset(); 150 }) 151 ; 152 } 153 154 private bool modeRegisterLocked; 155 private IValueRegisterField watchdogDelta; 156 private IFlagRegisterField interruptEnabled; 157 private IFlagRegisterField watchdogReset; 158 private IFlagRegisterField resetOnlyProcessor; 159 private IFlagRegisterField underflowPending; 160 private IFlagRegisterField errorPending; 161 162 private readonly SAM4S_RSTC resetController; 163 private readonly IMachine machine; 164 165 private const int MaximumWatchdogValue = 0xFFF; 166 private const int WatchdogPassword = 0xA5; 167 168 public enum Registers 169 { 170 Control = 0x00, 171 Mode = 0x04, 172 Status = 0x08, 173 } 174 } 175 } 176