1 //
2 // Copyright (c) 2010-2023 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 using Antmicro.Renode.Peripherals.Bus;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Logging;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.IRQControllers
17 {
18     public class STM32F4_EXTI : BasicDoubleWordPeripheral, IKnownSize, IIRQController, INumberedGPIOOutput
19     {
STM32F4_EXTI(IMachine machine, int numberOfOutputLines = 14, int firstDirectLine = DefaultFirstDirectLine)20         public STM32F4_EXTI(IMachine machine, int numberOfOutputLines = 14, int firstDirectLine = DefaultFirstDirectLine) : base(machine)
21         {
22             var innerConnections = new Dictionary<int, IGPIO>();
23             for(var i = 0; i < numberOfOutputLines; ++i)
24             {
25                 innerConnections[i] = new GPIO();
26             }
27             Connections = new ReadOnlyDictionary<int, IGPIO>(innerConnections);
28 
29             // All lines lower than firstDirectLine are configurable so they should be masked
30             // treatOutOfRangeLinesAsDirect is set to true to preserve backwards compatibility
31             core = new STM32_EXTICore(this, BitHelper.CalculateQuadWordMask(firstDirectLine, 0), treatOutOfRangeLinesAsDirect: true, allowMaskingDirectLines: false);
32 
33             numberOfLinesMask = BitHelper.CalculateQuadWordMask((int)NumberOfLines, 0);
34 
35             DefineRegisters();
36             Reset();
37         }
38 
OnGPIO(int number, bool value)39         public void OnGPIO(int number, bool value)
40         {
41             if(number >= NumberOfLines)
42             {
43                 this.Log(LogLevel.Error, "GPIO number {0} is out of range [0; {1})", number, NumberOfLines);
44                 return;
45             }
46             var lineNumber = (byte)number;
47 
48             if(core.CanSetInterruptValue(lineNumber, value, out var isLineConfigurable))
49             {
50                 // Configurable lines can only be set in this place.
51                 value = isLineConfigurable ? true : value;
52                 core.UpdatePendingValue(lineNumber, value);
53                 Connections[number].Set(value);
54             }
55         }
56 
Reset()57         public override void Reset()
58         {
59             base.Reset();
60             softwareInterrupt = 0;
61             foreach(var gpio in Connections)
62             {
63                 gpio.Value.Unset();
64             }
65         }
66 
67         public long Size => 0x400;
68 
69         public IReadOnlyDictionary<int, IGPIO> Connections { get; }
70 
71         public long NumberOfLines => Connections.Count;
72 
DefineRegisters()73         private void DefineRegisters()
74         {
75             Registers.InterruptMask.Define(this)
76                 .WithValueField(0, 32, out core.InterruptMask, name: "IMR");
77 
78             // Blank implementation to preserve backwards compatibility with the previous version of this model
79             Registers.EventMask.Define(this)
80                 .WithValueField(0, 32, name: "EMR");
81 
82             Registers.RisingTriggerSelection.Define(this)
83                 .WithValueField(0, 32, out core.RisingEdgeMask, name: "RTSR");
84 
85             Registers.FallingTriggerSelection.Define(this)
86                 .WithValueField(0, 32, out core.FallingEdgeMask, name: "FTSR");
87 
88             Registers.SoftwareInterruptEvent.Define(this)
89                 .WithValueField(0, 32, name: "SWIER", valueProviderCallback: _ => softwareInterrupt,
90                     writeCallback: (_, value) =>
91                     {
92                         value &= numberOfLinesMask;
93                         BitHelper.ForeachActiveBit(value & core.InterruptMask.Value, x => Connections[x].Set());
94                     });
95 
96             Registers.PendingRegister.Define(this)
97                 .WithValueField(0, 32, out core.PendingInterrupts, FieldMode.Read | FieldMode.WriteOneToClear, name: "PR",
98                     writeCallback: (_, value) =>
99                     {
100                         softwareInterrupt &= ~value;
101                         value &= numberOfLinesMask;
102                         BitHelper.ForeachActiveBit(value, x => Connections[x].Unset());
103                     });
104         }
105 
106         // We treat lines above 23 as direct by default for backwards compatibility with
107         // the old behavior of the EXTI model.
108         protected const int DefaultFirstDirectLine = 23;
109 
110         private ulong softwareInterrupt;
111 
112         private readonly ulong numberOfLinesMask;
113         private readonly STM32_EXTICore core;
114 
115         private enum Registers
116         {
117             InterruptMask = 0x0,
118             EventMask = 0x4,
119             RisingTriggerSelection = 0x8,
120             FallingTriggerSelection = 0xC,
121             SoftwareInterruptEvent = 0x10,
122             PendingRegister = 0x14
123         }
124     }
125 }
126