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 
8 using System;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 
16 namespace Antmicro.Renode.Peripherals.Miscellaneous
17 {
18     public sealed class STM32_SYSCFG : IDoubleWordPeripheral, INumberedGPIOOutput, IKnownSize, ILocalGPIOReceiver
19     {
STM32_SYSCFG()20         public STM32_SYSCFG()
21         {
22             var gpios = new Dictionary<int, IGPIO>();
23             for(var i = 0; i < GpioPins; ++i)
24             {
25                 gpios.Add(i, new GPIO());
26             }
27             Connections = new ReadOnlyDictionary<int, IGPIO>(gpios);
28             internalReceiversCache = new Dictionary<int, InternalReceiver>();
29             registers = CreateRegisters();
30         }
31 
32         /* The pattern to connect IRQs in REPL would be:
33          * `[a-b] -> syscfg@index[a-b]`, where:
34          * -> index - index of the mapped peripheral
35          * -> a, b - the range of exposed GPIO pins (lines to be redirected to EXTI)
36          * since this peripheral maps IRQs line-to-line (e.g. line X of input into line X of output)
37          * the thing that's muxed is the peripheral, the line belongs to (the index)
38          */
GetLocalReceiver(int index)39         public IGPIOReceiver GetLocalReceiver(int index)
40         {
41             if(!internalReceiversCache.TryGetValue(index, out var receiver))
42             {
43                 receiver = new InternalReceiver(this, index);
44                 internalReceiversCache.Add(index, receiver);
45             }
46             return receiver;
47         }
48 
ReadDoubleWord(long offset)49         public uint ReadDoubleWord(long offset)
50         {
51             return registers.Read(offset);
52         }
53 
WriteDoubleWord(long offset, uint value)54         public void WriteDoubleWord(long offset, uint value)
55         {
56             registers.Write(offset, value);
57         }
58 
Reset()59         public void Reset()
60         {
61             foreach(var connection in Connections.Values)
62             {
63                 connection.Unset();
64             }
65             foreach(var receiver in internalReceiversCache.Values)
66             {
67                 for(int pin = 0; pin < GpioPins; ++pin)
68                 {
69                     receiver.UpdateGPIO(pin);
70                 }
71             }
72             registers.Reset();
73         }
74 
75         public IReadOnlyDictionary<int, IGPIO> Connections
76         {
77             get; private set;
78         }
79 
80         public long Size
81         {
82             get
83             {
84                 return 0x400;
85             }
86         }
87 
CreateRegisters()88         private DoubleWordRegisterCollection CreateRegisters()
89         {
90             var map = new Dictionary<long, DoubleWordRegister>();
91             for(var regNumber = 0; regNumber < 4; ++regNumber)
92             {
93                 var reg = new DoubleWordRegister(this, 0);
94                 for(var fieldNumber = 0; fieldNumber < 4; ++fieldNumber)
95                 {
96                     var rn = regNumber;
97                     var fn = fieldNumber;
98                     var pinNumber = regNumber * 4 + fieldNumber;
99                     extiMappings[pinNumber] = reg.DefineValueField(4 * fieldNumber, 4, name: "EXTI" + pinNumber,
100                         changeCallback: (_, portNumber) =>
101                         {
102                             Connections[pinNumber].Unset();
103                             ((InternalReceiver)GetLocalReceiver((int)portNumber)).UpdateGPIO(pinNumber);
104                         }
105                     );
106                 }
107                 map.Add((long)Registers.ExternalInterruptConfiguration1 + 4 * regNumber, reg);
108             }
109             return new DoubleWordRegisterCollection(this, map);
110         }
111 
112         private readonly DoubleWordRegisterCollection registers;
113         private readonly Dictionary<int, InternalReceiver> internalReceiversCache;
114 
115         private readonly IValueRegisterField[] extiMappings = new IValueRegisterField[GpioPins];
116 
117         private const int GpioPins = 16;
118 
119         private class InternalReceiver : IGPIOReceiver
120         {
InternalReceiver(STM32_SYSCFG parent, int portNumber)121             public InternalReceiver(STM32_SYSCFG parent, int portNumber)
122             {
123                 this.parent = parent;
124                 this.portNumber = portNumber;
125                 this.state = new bool[GpioPins];
126             }
127 
OnGPIO(int pinNumber, bool value)128             public void OnGPIO(int pinNumber, bool value)
129             {
130                 if(pinNumber >= GpioPins)
131                 {
132                     parent.Log(LogLevel.Error, "GPIO port {0}, pin {1}, is not supported. Up to {2} pins are supported", portNumber, pinNumber, GpioPins);
133                     return;
134                 }
135                 parent.Log(LogLevel.Noisy, "GPIO port {0}, pin {1}, raised IRQ: {2}", portNumber, pinNumber, value);
136                 state[pinNumber] = value;
137                 UpdateGPIO(pinNumber);
138             }
139 
UpdateGPIO(int pinNumber)140             public void UpdateGPIO(int pinNumber)
141             {
142                 if((int)parent.extiMappings[pinNumber].Value == portNumber)
143                 {
144                     parent.Connections[pinNumber].Set(state[pinNumber]);
145                 }
146             }
147 
Reset()148             public void Reset()
149             {
150                 // IRQs are cleared on Parent reset
151                 // Don't clear `state` array here - as it represents the state of input signals, and is not a property of this peripheral
152                 // The state can only be cleared when the input signal is reset - but it's not controlled by us, but by the peripheral connected to OnGPIO (IRQ/GPIO line)
153                 // and since peripherals with connected GPIOs will naturally unset them in their Reset, the state array won't contain stale data
154             }
155 
156             // The state is recorded, so it's possible to update the GPIO state when changing source peripheral
157             // since this peripheral is effectively a mux - when changing input source, it's needed to get the other source's value
158             private readonly bool[] state;
159             private readonly STM32_SYSCFG parent;
160             private readonly int portNumber;
161         }
162 
163         private enum Registers
164         {
165             MemoryRemap = 0x0,
166             PeripheralModeConfiguration = 0x4,
167             ExternalInterruptConfiguration1 = 0x8,
168             ExternalInterruptConfiguration2 = 0xC,
169             ExternalInterruptConfiguration3 = 0x10,
170             ExternalInterruptConfiguration4 = 0x14,
171             ConfigurationRegister = 0x18,
172             CompensationCellControl = 0x20,
173             CompensationCellValue = 0x24,
174             CompensationCellCode = 0x28,
175             PowerControl = 0x2C,
176             PackageType = 0x124,
177             User0 = 0x300,
178             User1 = 0x304,
179             User2 = 0x308,
180             User3 = 0x30C,
181             User4 = 0x310,
182             User5 = 0x314,
183             User6 = 0x318,
184             User7 = 0x31C,
185             User8 = 0x320,
186             User9 = 0x324,
187             User10 = 0x328,
188             User11 = 0x32C,
189             User12 = 0x330,
190             User13 = 0x334,
191             User14 = 0x338,
192             User15 = 0x33C,
193             User16 = 0x340,
194             User17 = 0x344,
195         }
196     }
197 }
198