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