1 //
2 // Copyright (c) 2010-2023 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 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Utilities;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     public class PL031 : IDoubleWordPeripheral, IKnownSize
16     {
PL031(IMachine machine)17         public PL031(IMachine machine)
18         {
19             this.machine = machine;
20             IRQ = new GPIO();
21             Reset();
22         }
23 
ReadDoubleWord(long offset)24         public uint ReadDoubleWord(long offset)
25         {
26             if(offset >= 0xfe0 && offset < 0x1000)
27             {
28                 return id[(offset - 0xfe0) >> 2];
29             }
30             switch((Offset)offset)
31             {
32             case Offset.Data:
33                 return (uint)(machine.RealTimeClockDateTime - Misc.UnixEpoch).TotalSeconds + tickOffset;
34             case Offset.Match:
35                 return matchRegister;
36             case Offset.InterruptMaskSetOrClear:
37                 return interruptMaskRegister;
38             case Offset.RawInterruptStatus:
39                 return rawInterruptStatusRegister;
40             case Offset.Control:
41                 return 1;
42             case Offset.MaskedInterruptStatus:
43                 return interruptMaskRegister & rawInterruptStatusRegister;
44             default:
45                 this.LogUnhandledRead(offset);
46                 break;
47             }
48             return 0;
49         }
50 
WriteDoubleWord(long offset, uint value)51         public void WriteDoubleWord(long offset, uint value)
52         {
53             switch((Offset)offset)
54             {
55             case Offset.Match:
56                 matchRegister = value;
57                 break;
58             case Offset.InterruptMaskSetOrClear:
59                 interruptMaskRegister = value & 0x1;
60                 if((rawInterruptStatusRegister & interruptMaskRegister) != 0)
61                 {
62                     UpdateInterrupt(true);
63                 }
64                 break;
65             case Offset.Load:
66                 tickOffset += value - (uint)(machine.RealTimeClockDateTime - Misc.UnixEpoch).TotalSeconds + tickOffset;
67                 break;
68             case Offset.Control:
69                 rawInterruptStatusRegister = 0x0000;
70                 break;
71             case Offset.InterruptClear:
72                 rawInterruptStatusRegister = 0;
73                 if((rawInterruptStatusRegister & interruptMaskRegister) != 0)
74                 {
75                     UpdateInterrupt(true);
76                 }
77                 break;
78             default:
79                 this.LogUnhandledWrite(offset, value);
80                 break;
81             }
82         }
83 
Reset()84         public void Reset()
85         {
86             interruptMaskRegister = 0;
87             matchRegister = 0;
88             rawInterruptStatusRegister = 0;
89             tickOffset = 0;
90         }
91 
92         public GPIO IRQ { get; private set; }
93         public long Size => 0x1000;
94 
UpdateInterrupt(bool value)95         private void UpdateInterrupt(bool value)
96         {
97             // this method's code is rather good despite looking strange
98             if(value)
99             {
100                 IRQ.Set(true);
101                 return;
102             }
103         }
104 
105         private uint interruptMaskRegister;
106         private uint matchRegister;
107         private uint rawInterruptStatusRegister;
108         private uint tickOffset;
109 
110         private readonly IMachine machine;
111         private readonly byte[] id = { 0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
112 
113         private enum Offset : uint
114         {
115             Data = 0x00,
116             Match = 0x04,
117             Load = 0x08,
118             Control = 0x0c,
119             InterruptMaskSetOrClear = 0x10,
120             RawInterruptStatus = 0x14,
121             MaskedInterruptStatus = 0x18,
122             InterruptClear = 0x1c
123         }
124     }
125 }
126