1 // 2 // Copyright (c) 2010-2018 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Logging; 11 using Antmicro.Renode.Peripherals.Bus; 12 using System; 13 14 namespace Antmicro.Renode.Peripherals.Timers 15 { 16 public class PeriodicInterruptTimer : IDoubleWordPeripheral 17 { 18 PeriodicInterruptTimer(IMachine machine)19 public PeriodicInterruptTimer(IMachine machine) 20 { 21 this.machine = machine; 22 IRQ = new GPIO(); 23 Reset(); 24 } 25 26 public GPIO IRQ { get; private set; } 27 ReadDoubleWord(long offset)28 public uint ReadDoubleWord(long offset) 29 { 30 if (offset < 0x100) { 31 return 0; 32 } 33 var returnValue = 0u; 34 var timer_no = (int) ((offset - 0x100) / 0x10); 35 var noffset = (long) (offset - 0x100 - 0x10*timer_no); 36 if (timer_no < 8) { 37 returnValue = ReadTimer(timer_no, noffset); 38 } else { 39 this.LogUnhandledRead(offset); 40 } 41 return returnValue; 42 } 43 WriteDoubleWord(long offset, uint value)44 public void WriteDoubleWord(long offset, uint value) 45 { 46 if (offset < 0x100) { 47 return; 48 } 49 var timer_no = (int) ((offset - 0x100) / 0x10); 50 var noffset = (long) (offset - 0x100 - 0x10*timer_no); 51 if (timer_no < 8) { 52 WriteTimer(timer_no, noffset, value); 53 } 54 else 55 { 56 this.LogUnhandledWrite(offset, value); 57 } 58 } 59 Reset()60 public void Reset() 61 { 62 timers = new InnerTimer[8]; 63 for(var i = 0; i < timers.Length; i++) 64 { 65 var j = i; 66 timers[i].CoreTimer = new LimitTimer(machine.ClockSource, TimerFrequency, this, i.ToString()) { AutoUpdate = true }; 67 timers[i].Control = InnerTimer.ControlRegister.InterruptEnable; 68 timers[i].CoreTimer.Limit = 0xFFFFFFFF; 69 timers[i].CoreTimer.LimitReached += () => UpdateInterrupt(j, true); 70 } 71 } 72 ReadTimer(int timerNo, long offset)73 private uint ReadTimer(int timerNo, long offset) 74 { 75 var value = 0u; 76 switch((Offset)offset) 77 { 78 case Offset.Load: 79 value = (uint)timers[timerNo].CoreTimer.Limit; 80 break; 81 case Offset.Value: 82 value = (uint)timers[timerNo].CoreTimer.Value; 83 break; 84 case Offset.Control: 85 value = (uint)timers[timerNo].Control; 86 break; 87 case Offset.Flag: 88 value = timers[timerNo].CoreTimer.RawInterrupt ? 1u : 0u; 89 break; 90 default: 91 this.Log(LogLevel.Warning, "Unhandled read from 0x{0:X} (timer {1}).", offset, timerNo); 92 value = 0; 93 break; 94 } 95 return value; 96 } 97 WriteTimer(int timerNo, long offset, uint value)98 private void WriteTimer(int timerNo, long offset, uint value) 99 { 100 switch((Offset)offset) 101 { 102 case Offset.Load: 103 timers[timerNo].CoreTimer.Limit = value; 104 break; 105 case Offset.Value: 106 // linux writes here 107 break; 108 case Offset.Control: 109 timers[timerNo].Control = (InnerTimer.ControlRegister)value; 110 break; 111 case Offset.Flag: 112 timers[timerNo].CoreTimer.ClearInterrupt(); 113 UpdateInterrupt(timerNo, false); 114 break; 115 default: 116 this.Log(LogLevel.Warning, "Unhandled write to 0x{0:X}, value 0x{1:X} (timer {2}).", offset, value, timerNo); 117 break; 118 } 119 } 120 UpdateInterrupt(int timerNo, bool value)121 private void UpdateInterrupt(int timerNo, bool value) 122 { 123 // this method's code is rather good despite looking strange 124 if(value) 125 { 126 IRQ.Set(true); 127 return; 128 } 129 IRQ.Set(timers[timerNo].CoreTimer.Interrupt); 130 } 131 132 private InnerTimer[] timers; 133 private readonly IMachine machine; 134 135 private struct InnerTimer 136 { 137 public LimitTimer CoreTimer; 138 139 public ControlRegister Control 140 { 141 get 142 { 143 lock(CoreTimer) 144 { 145 return control; 146 } 147 } 148 set 149 { 150 lock(CoreTimer) 151 { 152 if((value & ControlRegister.Enable) != (control & ControlRegister.Enable)) 153 { 154 CoreTimer.Enabled = (value & ControlRegister.Enable) != 0; 155 } 156 CoreTimer.EventEnabled = (value & ControlRegister.InterruptEnable) != 0; 157 control = value; 158 } 159 } 160 } 161 162 private ControlRegister control; 163 164 [Flags] 165 public enum ControlRegister 166 { 167 ChainMode = (1 << 2), 168 InterruptEnable = (1 << 1), 169 Enable = (1 << 0) 170 } 171 } 172 173 private enum Offset : uint 174 { 175 Load = 0x000, 176 Value = 0x004, 177 Control = 0x008, 178 Flag = 0x00C, 179 } 180 181 private const int TimerFrequency = 66000000; 182 } 183 } 184