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 
8 using System;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Peripherals.GPIOPort
18 {
19     public class LiteX_ControlAndStatus : BaseGPIOPort, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral, IKnownSize
20     {
LiteX_ControlAndStatus(IMachine machine)21         public LiteX_ControlAndStatus(IMachine machine) : base(machine, LedsCount + SwitchesCount + ButtonsCount)
22         {
23             buttonsPending = new bool[ButtonsCount];
24             IRQ = new GPIO();
25 
26             RegistersCollection = new DoubleWordRegisterCollection(this);
27             DefineRegisters();
28         }
29 
ReadDoubleWord(long offset)30         public uint ReadDoubleWord(long offset)
31         {
32             return RegistersCollection.Read(offset);
33         }
34 
WriteDoubleWord(long offset, uint value)35         public void WriteDoubleWord(long offset, uint value)
36         {
37             RegistersCollection.Write(offset, value);
38         }
39 
Reset()40         public override void Reset()
41         {
42             base.Reset();
43             RegistersCollection.Reset();
44             Array.Clear(buttonsPending, 0, buttonsPending.Length);
45 
46             UpdateInterrupt();
47         }
48 
OnGPIO(int number, bool value)49         public override void OnGPIO(int number, bool value)
50         {
51             if(number >= 0 && number < LedsCount)
52             {
53                 this.Log(LogLevel.Warning, "Input not allowed on LED ports");
54                 return;
55             }
56 
57             base.OnGPIO(number, value);
58 
59             // here we are interested only in buttons as their state is latached and generates interrupt
60             if(number >= LedsCount + SwitchesCount)
61             {
62                 var buttonId = number - (LedsCount + SwitchesCount);
63                 buttonsPending[buttonId] |= value;
64 
65                 UpdateInterrupt();
66             }
67         }
68 
69         public GPIO IRQ { get; }
70 
71         public long Size => 0x14;
72 
73         public DoubleWordRegisterCollection RegistersCollection { get; }
74 
DefineRegisters()75         private void DefineRegisters()
76         {
77             // according to LiteX-generated `csr.h` this register is readable
78             Registers.Leds.Define(this)
79                 .WithValueField(0, LedsCount,
80                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(Connections.Where(x => x.Key >= 0).OrderBy(x => x.Key).Select(x => x.Value.IsSet)),
81                     writeCallback: (_, val) =>
82                     {
83                         var bits = BitHelper.GetBits((uint)val);
84                         for(var i = 0; i < LedsCount; i++)
85                         {
86                             Connections[i].Set(bits[i]);
87                         }
88                     })
89             ;
90 
91             Registers.Switches.Define(this)
92                 .WithValueField(0, SwitchesCount, FieldMode.Read,
93                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(this.State.Skip(LedsCount).Take(SwitchesCount)))
94             ;
95 
96             Registers.ButtonsStatus.Define(this)
97                 .WithValueField(0, ButtonsCount, FieldMode.Read,
98                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(this.State.Skip(LedsCount + SwitchesCount).Take(ButtonsCount)))
99             ;
100 
101             Registers.ButtonsPending.Define(this)
102                 .WithValueField(0, ButtonsCount,
103                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(buttonsPending),
104                     writeCallback: (_, val) =>
105                     {
106                         foreach(var bit in BitHelper.GetSetBits(val))
107                         {
108                             buttonsPending[bit] = false;
109                         }
110 
111                         UpdateInterrupt();
112                     })
113             ;
114 
115             Registers.ButtonsEnabled.Define(this)
116                 .WithValueField(0, ButtonsCount, out buttonsEnabled, writeCallback: (_, __) => { UpdateInterrupt(); })
117             ;
118         }
119 
UpdateInterrupt()120         private void UpdateInterrupt()
121         {
122             var enabled = BitHelper.GetBits((uint)buttonsEnabled.Value).Take(ButtonsCount);
123             IRQ.Set(enabled.Zip(buttonsPending, (en, pe) => en && pe).Any());
124         }
125 
126         private IValueRegisterField buttonsEnabled;
127 
128         private readonly bool[] buttonsPending;
129 
130         private const int LedsCount = 32;
131         private const int SwitchesCount = 32;
132         private const int ButtonsCount = 32;
133 
134         private enum Registers
135         {
136             Leds = 0x0,
137             Switches = 0x4,
138             ButtonsStatus = 0x8,
139             ButtonsPending = 0xC,
140             ButtonsEnabled = 0x10,
141         }
142     }
143 }
144