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 using Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Extensions;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Utilities;
12 
13 namespace Antmicro.Renode.Peripherals.GPIOPort
14 {
15     public class SAMD21_GPIO : BaseGPIOPort, IBytePeripheral, IDoubleWordPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize
16     {
SAMD21_GPIO(IMachine machine)17         public SAMD21_GPIO(IMachine machine) : base(machine, NumberOfPins)
18         {
19             RegistersCollection = new ByteRegisterCollection(this);
20             configurationRegister = new DoubleWordRegister(this);
21 
22             pinIsOutput = new bool[NumberOfPins];
23             pinInputEnabled = new bool[NumberOfPins];
24 
25             DefineRegisters();
26         }
27 
ReadDoubleWord(long offset)28         public uint ReadDoubleWord(long offset)
29         {
30             if(offset != (long)Registers.WriteConfiguration)
31             {
32                 return this.ReadDoubleWordUsingByte(offset);
33             }
34             // NOTE: We are using additional DoubleWordRegister for WriteConfiguration (0x28)
35             //       to simplify implementation of transactions
36             return configurationRegister.Read();
37         }
38 
WriteDoubleWord(long offset, uint value)39         public void WriteDoubleWord(long offset, uint value)
40         {
41             if(offset != (long)Registers.WriteConfiguration)
42             {
43                 this.WriteDoubleWordUsingByte(offset, value);
44                 return;
45             }
46             // NOTE: We are using additional DoubleWordRegister for WriteConfiguration (0x28)
47             //       to simplify implementation of transactions
48             configurationRegister.Write(0, value);
49         }
50 
ReadByte(long offset)51         public byte ReadByte(long offset)
52         {
53             return RegistersCollection.Read(offset);
54         }
55 
WriteByte(long offset, byte value)56         public void WriteByte(long offset, byte value)
57         {
58             RegistersCollection.Write(offset, value);
59         }
60 
61         public ByteRegisterCollection RegistersCollection { get; }
62 
63         public long Size => 0x80;
64 
DefineRegisters()65         private void DefineRegisters()
66         {
67             Registers.Direction.DefineMany(this, 4, (register, index) =>
68             {
69                 register.WithFlags(0, 8, name: $"DIR[{(index+1)*8-1}:{index*8}]",
70                     valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j],
71                     writeCallback: (j, _, value) => pinIsOutput[index * 8 + j] = value);
72             });
73 
74             Registers.DirectionClear.DefineMany(this, 4, (register, index) =>
75             {
76                 register.WithFlags(0, 8, name: $"DIRCLR[{(index+1)*8-1}:{index*8}]",
77                     valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j],
78                     writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] = false; });
79             });
80 
81             Registers.DirectionSet.DefineMany(this, 4, (register, index) =>
82             {
83                 register.WithFlags(0, 8, name: $"DIRSET[{(index+1)*8-1}:{index*8}]",
84                     valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j],
85                     writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] = true; });
86             });
87 
88             Registers.DirectionToggle.DefineMany(this, 4, (register, index) =>
89             {
90                 register.WithFlags(0, 8, name: $"DIRTGL[{(index+1)*8-1}:{index*8}]",
91                     valueProviderCallback: (j, _) => pinIsOutput[index * 8 + j],
92                     writeCallback: (j, _, value) => { if(value) pinIsOutput[index * 8 + j] ^= true; });
93             });
94 
95             Registers.Output.DefineMany(this, 4, (register, index) =>
96             {
97                 register.WithFlags(0, 8, name: $"OUT[{(index+1)*8-1}:{index*8}]",
98                     valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet,
99                     writeCallback: (j, _, value) => Connections[index * 8 + j].Set(value));
100             });
101 
102             Registers.OutputClear.DefineMany(this, 4, (register, index) =>
103             {
104                 register.WithFlags(0, 8, name: $"OUTCLR[{(index+1)*8-1}:{index*8}]",
105                     valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet,
106                     writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Unset(); });
107             });
108 
109             Registers.OutputSet.DefineMany(this, 4, (register, index) =>
110             {
111                 register.WithFlags(0, 8, name: $"OUTSET[{(index+1)*8-1}:{index*8}]",
112                     valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet,
113                     writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Set(); });
114             });
115 
116             Registers.OutputToggle.DefineMany(this, 4, (register, index) =>
117             {
118                 register.WithFlags(0, 8, name: $"OUTTGL[{(index+1)*8-1}:{index*8}]",
119                     valueProviderCallback: (j, _) => Connections[index * 8 + j].IsSet,
120                     writeCallback: (j, _, value) => { if(value) Connections[index * 8 + j].Toggle(); });
121             });
122 
123             Registers.Input.DefineMany(this, 4, (register, index) =>
124             {
125                 register.WithFlags(0, 8, FieldMode.Read, name: $"IN[{(index+1)*8-1}:{index*8}]",
126                     valueProviderCallback: (j, _) => pinInputEnabled[index * 8 + j] && State[index * 8 + j]);
127             });
128 
129             configurationRegister
130                 .WithValueField(0, 16, out var pinMask, name: "PINMASK")
131                 .WithTaggedFlag("PMUXEN", 16)
132                 .WithFlag(17, out var pinInputBuffer, name: "INEN")
133                 .WithTaggedFlag("PULLEN", 18)
134                 .WithReservedBits(19, 3)
135                 .WithTaggedFlag("DRVSTR", 22)
136                 .WithReservedBits(23, 1)
137                 .WithTag("PMUX", 24, 4)
138                 .WithTaggedFlag("WRPMUX", 28)
139                 .WithReservedBits(29, 1)
140                 .WithFlag(30, out var writePinConfig, name: "WRPINCFG")
141                 .WithFlag(31, out var halfWordSelect, name: "HWSEL")
142                 .WithWriteCallback((_, __) =>
143                 {
144                     if(!writePinConfig.Value)
145                     {
146                         return;
147                     }
148 
149                     var mask = pinMask.Value << (halfWordSelect.Value ? 16 : 0);
150                     BitHelper.ForeachActiveBit(mask, index =>
151                     {
152                         pinInputEnabled[index] = pinInputBuffer.Value;
153                     });
154                 });
155 
156             Registers.PinConfiguration.DefineMany(this, NumberOfPins, (register, index) =>
157             {
158                 register
159                     .WithTaggedFlag("PMUXEN", 0)
160                     .WithFlag(1, name: "INEN",
161                         valueProviderCallback: _ => pinInputEnabled[index],
162                         writeCallback: (_, value) => pinInputEnabled[index] = value)
163                     .WithTaggedFlag("PULLEN", 2)
164                     .WithReservedBits(3, 3)
165                     .WithTaggedFlag("DRVSTR", 6)
166                     .WithReservedBits(7, 1)
167                 ;
168             });
169         }
170 
171         public const int NumberOfPins = 32;
172 
173         private bool[] pinIsOutput;
174         private bool[] pinInputEnabled;
175         private readonly DoubleWordRegister configurationRegister;
176 
177         private enum Registers
178         {
179             Direction = 0x00,
180             DirectionClear = 0x04,
181             DirectionSet = 0x08,
182             DirectionToggle = 0xC,
183             Output = 0x10,
184             OutputClear = 0x14,
185             OutputSet = 0x18,
186             OutputToggle = 0x1C,
187             Input = 0x20,
188             Control = 0x24,
189             WriteConfiguration = 0x28,
190             PeripheralMultiplexing = 0x2C,
191             PinConfiguration = 0x40,
192         }
193     }
194 }
195