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.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Utilities;
14 
15 namespace Antmicro.Renode.Peripherals.SPI
16 {
17     public class SPIMultiplexer : SimpleContainer<ISPIPeripheral>, IGPIOReceiver, ISPIPeripheral
18     {
SPIMultiplexer(IMachine machine, bool suppressExplicitFinishTransmission = true)19         public SPIMultiplexer(IMachine machine, bool suppressExplicitFinishTransmission = true) : base(machine)
20         {
21             this.suppressExplicitFinishTransmission = suppressExplicitFinishTransmission;
22             chipSelects = new HashSet<int>();
23             activeLowSignals = new HashSet<int>();
24             inputState = new Dictionary<int, bool>();
25         }
26 
OnGPIO(int number, bool value)27         public void OnGPIO(int number, bool value)
28         {
29             this.Log(LogLevel.Noisy, "GPIO #{0} set to {1}", number, value);
30             inputState[number] = value;
31             UpdateChipSelectState();
32         }
33 
SetActiveLow(int number)34         public void SetActiveLow(int number)
35         {
36             activeLowSignals.Add(number);
37             UpdateChipSelectState();
38         }
39 
SetActiveHigh(int number)40         public void SetActiveHigh(int number)
41         {
42             activeLowSignals.Remove(number);
43             UpdateChipSelectState();
44         }
45 
Reset()46         public override void Reset()
47         {
48             inputState.Clear();
49         }
50 
Transmit(byte data)51         public byte Transmit(byte data)
52         {
53             if(chipSelects.Count == 0)
54             {
55                 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X}, but no device is currently selected - ignoring transfer and returning a dummy byte", data);
56                 return 0xFF;
57             }
58 
59             if(chipSelects.Count > 1)
60             {
61                 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X}, but multiple devices are currently selected ({1}) - ignoring transfer and returning a dummy byte", data, Misc.PrettyPrintCollectionHex(chipSelects));
62                 return 0xFF;
63             }
64 
65             var deviceAddress = chipSelects.First();
66             if(!TryGetByAddress(deviceAddress, out var device))
67             {
68                 this.Log(LogLevel.Warning, "Tried to transmit byte 0x{0:X} to device 0x{1:X}, but it's not connected - ignoring transfer and returning a dummy byte", data, deviceAddress);
69                 return 0xFF;
70             }
71 
72             return device.Transmit(data);
73         }
74 
FinishTransmission()75         public void FinishTransmission()
76         {
77             if(suppressExplicitFinishTransmission)
78             {
79                 return;
80             }
81 
82             if(chipSelects.Count == 0)
83             {
84                 this.Log(LogLevel.Warning, "Tried to finish transmission, but no device is currently selected");
85                 return;
86             }
87 
88             if(chipSelects.Count > 1)
89             {
90                 this.Log(LogLevel.Warning, "Tried to finish transmission, but multiple devices are currently selected ({0})", Misc.PrettyPrintCollectionHex(chipSelects));
91                 return;
92             }
93 
94             var deviceAddress = chipSelects.First();
95             FinishTransmissionByAddress(deviceAddress);
96         }
97 
FinishTransmissionByAddress(int deviceAddress)98         private void FinishTransmissionByAddress(int deviceAddress)
99         {
100             if(!TryGetByAddress(deviceAddress, out var device))
101             {
102                 this.Log(LogLevel.Warning, "Tried to finish transmission to device 0x{0:X}, but it's not connected", deviceAddress);
103                 return;
104             }
105 
106             this.Log(LogLevel.Noisy, "Finisihing transmission on device 0x{0:X}", deviceAddress);
107             device.FinishTransmission();
108         }
109 
UpdateChipSelectState()110         private void UpdateChipSelectState()
111         {
112             foreach(var state in inputState)
113             {
114                 var number = state.Key;
115                 var value = state.Value;
116 
117                 var active = activeLowSignals.Contains(number) ? !value : value;
118                 if(active)
119                 {
120                     chipSelects.Add(number);
121                 }
122                 else
123                 {
124                     chipSelects.Remove(number);
125                     FinishTransmissionByAddress(number);
126                 }
127             }
128         }
129 
130         private readonly HashSet<int> chipSelects;
131         private readonly HashSet<int> activeLowSignals;
132         private readonly Dictionary<int, bool> inputState;
133         private readonly bool suppressExplicitFinishTransmission;
134     }
135 }
136 
137