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 Antmicro.Renode.Core;
9 using Antmicro.Renode.Peripherals.Bus;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Utilities;
12 
13 namespace Antmicro.Renode.Peripherals.GPIOPort
14 {
15     public class RenesasDA14_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
16     {
RenesasDA14_GPIO(IMachine machine)17         public RenesasDA14_GPIO(IMachine machine) : base(machine, NumberOfPorts * PinsPerPort)
18         {
19             RegistersCollection = new DoubleWordRegisterCollection(this);
20 
21             ports = new Port[NumberOfPorts];
22             for(var portNumber = 0; portNumber < ports.Length; ++portNumber)
23             {
24                 ports[portNumber] = new Port(portNumber, this);
25             }
26 
27             DefineCommonRegisters();
28 
29             Reset();
30         }
31 
ReadDoubleWord(long offset)32         public uint ReadDoubleWord(long offset)
33         {
34             return RegistersCollection.Read(offset);
35         }
36 
WriteDoubleWord(long offset, uint value)37         public void WriteDoubleWord(long offset, uint value)
38         {
39             RegistersCollection.Write(offset, value);
40         }
41 
Reset()42         public override void Reset()
43         {
44             base.Reset();
45             RegistersCollection.Reset();
46         }
47 
DefineCommonRegisters()48         private void DefineCommonRegisters()
49         {
50             Registers.ClockSel.Define(this)
51                 .WithTag("FUNC_CLOCK_SEL", 0, 3)
52                 .WithTaggedFlag("FUNC_CLOCK_EN", 3)
53                 .WithReservedBits(4, 3)
54                 .WithTaggedFlag("XTAL32M_OUTPUT_EN", 8)
55                 .WithTaggedFlag("RC32M_OUTPUT_EN", 9)
56                 .WithTaggedFlag("DIVN_OUTPUT_EN", 10);
57         }
58 
59         public DoubleWordRegisterCollection RegistersCollection { get; }
60 
61         public long Size => 0x200;
62 
63         private readonly Port[] ports;
64 
65         private const int NumberOfPorts = 2;
66         private const int PinsPerPort = 16;
67 
68         private class Port
69         {
Port(int portNumber, RenesasDA14_GPIO parent)70             public Port(int portNumber, RenesasDA14_GPIO parent)
71             {
72                 this.portNumber = portNumber;
73                 this.parent = parent;
74 
75                 parent.RegistersCollection.DefineRegister((long)Registers.DataPort0 + (4 * portNumber), 0x142)
76                     .WithValueField(0, 16, name: $"P{portNumber}_DATA",
77                         valueProviderCallback: _ => GetStateValue(),
78                         writeCallback: (_, value) => SetStateValue(value));
79                 parent.RegistersCollection.DefineRegister((long)Registers.SetDataPort0 + (4 * portNumber))
80                     .WithValueField(0, 16, name: $"P{portNumber}_SET",
81                         valueProviderCallback: _ => 0,
82                         writeCallback: (_, value) => SetStateValue(GetStateValue() | value));
83                 parent.RegistersCollection.DefineRegister((long)Registers.ResetDataPort0 + (4 * portNumber))
84                     .WithValueField(0, 16, name: $"P{portNumber}_RESET",
85                         valueProviderCallback: _ => 0,
86                         writeCallback: (_, value) => SetStateValue(GetStateValue() | ~value));
87                 parent.RegistersCollection.DefineRegister((long)Registers.WeakControlPort0 + (4 * portNumber))
88                     .WithValueField(0, 16, out strength, name: $"P{portNumber}_LOWDRV");
89 
90                 direction = new IEnumRegisterField<Directions>[PinsPerPort];
91 
92                 for(var pin = 0; pin < PinsPerPort; ++pin)
93                 {
94                     parent.RegistersCollection.DefineRegister((long)Registers.ModePort0Pin00 + (4 * pin) + (4 * PinsPerPort * portNumber), 0x200)
95                             .WithTag("PID", 0, 6) // pin function
96                             .WithReservedBits(6, 2)
97                             .WithEnumField<DoubleWordRegister, Directions>(8, 2, out direction[pin], name: "PUPD",
98                                 writeCallback: (_, __) => RefreshConnectionsState())
99                             .WithTaggedFlag("PPOD", 10);
100                 }
101             }
102 
IsOutputPin(int pinNumber)103             private bool IsOutputPin(int pinNumber)
104             {
105                 return direction[pinNumber].Value == Directions.Output;
106             }
107 
GetStateValue()108             private ulong GetStateValue()
109             {
110                 ulong result = 0;
111 
112                 for(byte bitIndex = 0; bitIndex < PinsPerPort; bitIndex++)
113                 {
114                     var idx = PinsPerPort * portNumber + bitIndex;
115 
116                     BitHelper.SetBit(ref result, bitIndex, IsOutputPin(bitIndex)
117                         ? parent.Connections[idx].IsSet
118                         : parent.State[idx]);
119                 }
120 
121                 return result;
122             }
123 
SetStateValue(ulong value)124             private void SetStateValue(ulong value)
125             {
126                 state = value;
127                 RefreshConnectionsState();
128             }
129 
RefreshConnectionsState()130             private void RefreshConnectionsState()
131             {
132                 for(byte bitIndex = 0; bitIndex < PinsPerPort; bitIndex++)
133                 {
134                     if(IsOutputPin(bitIndex))
135                     {
136                         var connection = parent.Connections[PinsPerPort * portNumber + bitIndex];
137                         var pinState = BitHelper.IsBitSet(state, bitIndex);
138 
139                         connection.Set(pinState);
140                     }
141                 }
142             }
143 
144             private ulong state;
145 
146             private readonly IEnumRegisterField<Directions>[] direction;
147             private readonly IValueRegisterField strength;
148 
149             private readonly int portNumber;
150             private readonly RenesasDA14_GPIO parent;
151 
152             private enum Directions
153             {
154                 InputNoResistors    = 0b00,
155                 InputPullUp         = 0b01,
156                 InputPullDown       = 0b10,
157                 Output              = 0b11
158             }
159         }
160 
161         private enum Registers
162         {
163             DataPort0 = 0x0,
164             DataPort1 = 0x4,
165             SetDataPort0 = 0x8,
166             SetDataPort1 = 0xC,
167             ResetDataPort0 = 0x10,
168             ResetDataPort1 = 0x14,
169 
170             ModePort0Pin00 = 0x18,
171             ModePort0Pin01 = 0x1C,
172             ModePort0Pin02 = 0x20,
173             ModePort0Pin03 = 0x24,
174             ModePort0Pin04 = 0x28,
175             ModePort0Pin05 = 0x2C,
176             ModePort0Pin06 = 0x30,
177             ModePort0Pin07 = 0x34,
178             ModePort0Pin08 = 0x38,
179             ModePort0Pin09 = 0x3c,
180             ModePort0Pin10 = 0x40,
181             ModePort0Pin11 = 0x44,
182             ModePort0Pin12 = 0x48,
183             ModePort0Pin13 = 0x4c,
184             ModePort0Pin14 = 0x50,
185             ModePort0Pin15 = 0x54,
186 
187             ModePort1Pin00 = 0x58,
188             ModePort1Pin01 = 0x5c,
189             ModePort1Pin02 = 0x60,
190             ModePort1Pin03 = 0x64,
191             ModePort1Pin04 = 0x68,
192             ModePort1Pin05 = 0x6c,
193             ModePort1Pin06 = 0x70,
194             ModePort1Pin07 = 0x74,
195             ModePort1Pin08 = 0x78,
196             ModePort1Pin09 = 0x7c,
197             ModePort1Pin10 = 0x80,
198             ModePort1Pin11 = 0x84,
199             ModePort1Pin12 = 0x88,
200             ModePort1Pin13 = 0x8c,
201             ModePort1Pin14 = 0x90,
202             ModePort1Pin15 = 0x94,
203 
204             ClockSel = 0xA0,
205             WeakControlPort0 = 0xA4,
206             WeakControlPort1 = 0xA8,
207         }
208     }
209 }
210