1 // 2 // Copyright (c) 2010-2025 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 Antmicro.Migrant; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 14 namespace Antmicro.Renode.Peripherals.Miscellaneous 15 { 16 public class SAM4S_DACC : BasicDoubleWordPeripheral, IKnownSize 17 { SAM4S_DACC(IMachine machine, decimal referenceVoltage = 3.3m)18 public SAM4S_DACC(IMachine machine, decimal referenceVoltage = 3.3m) : base(machine) 19 { 20 ReferenceVoltage = referenceVoltage; 21 DefineRegisters(); 22 } 23 WriteDoubleWord(long offset, uint value)24 public override void WriteDoubleWord(long offset, uint value) 25 { 26 if(writeProtectionEnabled && Array.IndexOf(writeProtectedOffsets, offset) != -1) 27 { 28 writeViolationStatus.Value = true; 29 writeViolationSource.Value = (ulong)offset; 30 this.Log(LogLevel.Warning, "Tried to write {0} while write protection is enabled", Enum.GetName(typeof(Registers), offset)); 31 return; 32 } 33 34 base.WriteDoubleWord(offset, value); 35 } 36 37 public long Size => 0x100; 38 39 public GPIO IRQ { get; } = new GPIO(); 40 41 public decimal ReferenceVoltage { get; set; } 42 43 public decimal MinimumVoltage => ReferenceVoltage / 6m; 44 45 public decimal MaximumVoltage => 5m * ReferenceVoltage / 6m; 46 47 public decimal VoltageRange => 4m * ReferenceVoltage / 6m; 48 49 public decimal DAC0 => channelEnabled[0].Value ? ConvertVoltage(channelValue[0]) : 0m; 50 51 public decimal DAC1 => channelEnabled[1].Value ? ConvertVoltage(channelValue[1]) : 0m; 52 53 [field: Transient] 54 public event Action<int, decimal, ulong> OutputChanged; 55 UpdateInterrupts()56 private void UpdateInterrupts() 57 { 58 var interrupt = false; 59 60 interrupt |= transmitReadyInterruptEnabled.Value; 61 interrupt |= endOfConversionInterruptPending.Value && endOfConversionInterruptEnabled.Value; 62 63 this.Log(LogLevel.Debug, "IRQ set to {0}", interrupt); 64 IRQ.Set(interrupt); 65 } 66 ConvertVoltage(ulong value)67 private decimal ConvertVoltage(ulong value) 68 { 69 return MinimumVoltage + ((decimal)value / (decimal)MaximumChannelValue) * VoltageRange; 70 } 71 PerformConversion(ulong data)72 private void PerformConversion(ulong data) 73 { 74 // NOTE: Split and truncate to 16 bits 75 var requests = wordMode.Value 76 ? new ulong[] { data & 0xFFFF, (data >> 16) & 0xFFFF } 77 : new ulong[] { data & 0xFFFF }; 78 79 foreach(var request in requests) 80 { 81 var value = request & MaximumChannelValue; 82 var channel = tagSelectionMode.Value ? (int)request >> 12 : (int)userSelectedChannel.Value; 83 84 if(channel >= NumberOfChannels) 85 { 86 this.Log(LogLevel.Warning, "Tried to set channel#{0}, but only {1} channels are available; ignoring", channel, NumberOfChannels); 87 continue; 88 } 89 90 if(channelEnabled[channel].Value && channelValue[channel] != value) 91 { 92 channelValue[channel] = value; 93 OutputChanged?.Invoke(channel, ConvertVoltage(value), value); 94 } 95 } 96 97 endOfConversionInterruptPending.Value = true; 98 UpdateInterrupts(); 99 } 100 DefineRegisters()101 private void DefineRegisters() 102 { 103 Registers.Control.Define(this) 104 .WithFlag(0, FieldMode.Write, name: "SWRST", 105 writeCallback: (_, value) => { if(value) Reset(); }) 106 .WithReservedBits(1, 31) 107 ; 108 109 Registers.Mode.Define(this) 110 .WithTaggedFlag("TRGEN", 0) 111 .WithTag("TRGSEL", 1, 3) 112 .WithFlag(4, out wordMode, name: "WORD") 113 .WithReservedBits(5, 3) 114 .WithFlag(8, name: "ONE", 115 valueProviderCallback: _ => true) 116 .WithReservedBits(9, 7) 117 .WithValueField(16, 2, out userSelectedChannel, name: "USER_SEL") 118 .WithReservedBits(18, 2) 119 .WithFlag(20, out tagSelectionMode, name: "TAG") 120 .WithTaggedFlag("MAXS", 21) 121 .WithReservedBits(22, 2) 122 .WithTag("STARTUP", 24, 6) 123 .WithReservedBits(30, 2) 124 ; 125 126 Registers.ChannelEnable.Define(this) 127 .WithFlags(0, 2, FieldMode.Set, name: "CH", 128 writeCallback: (index, _, value) => { if(value) channelEnabled[index].Value = true; }) 129 .WithReservedBits(2, 30) 130 ; 131 132 Registers.ChannelDisable.Define(this) 133 .WithFlags(0, 2, FieldMode.WriteToClear, name: "CH", 134 writeCallback: (index, _, value) => 135 { 136 if(!value || !channelEnabled[index].Value) 137 { 138 return; 139 } 140 141 channelEnabled[index].Value = false; 142 // NOTE: There is no analog output when channel is disabled 143 OutputChanged?.Invoke(index, 0, 0); 144 }) 145 .WithReservedBits(2, 30) 146 ; 147 148 Registers.ChannelStatus.Define(this) 149 .WithFlags(0, 2, out channelEnabled, FieldMode.Read, name: "CH") 150 .WithReservedBits(2, 30) 151 ; 152 153 Registers.ConversionData.Define(this) 154 .WithValueField(0, 32, FieldMode.Write, name: "DATA", 155 writeCallback: (_, value) => PerformConversion(value)) 156 ; 157 158 Registers.InterruptEnable.Define(this) 159 .WithFlag(0, FieldMode.Set, name: "TXRDY", 160 writeCallback: (_, value) => { if(value) transmitReadyInterruptEnabled.Value = true; }) 161 .WithFlag(1, FieldMode.Set, name: "EOC", 162 writeCallback: (_, value) => { if(value) endOfConversionInterruptEnabled.Value = true; }) 163 .WithTaggedFlag("ENDTX", 2) 164 .WithTaggedFlag("TXBUFE", 3) 165 .WithReservedBits(4, 28) 166 .WithWriteCallback((_, __) => UpdateInterrupts()) 167 ; 168 169 Registers.InterruptDisable.Define(this) 170 .WithFlag(0, FieldMode.WriteToClear, name: "TXRDY", 171 writeCallback: (_, value) => { if(value) transmitReadyInterruptEnabled.Value = false; }) 172 .WithFlag(1, FieldMode.WriteToClear, name: "EOC", 173 writeCallback: (_, value) => { if(value) endOfConversionInterruptEnabled.Value = false; }) 174 .WithTaggedFlag("ENDTX", 2) 175 .WithTaggedFlag("TXBUFE", 3) 176 .WithReservedBits(4, 28) 177 .WithWriteCallback((_, __) => UpdateInterrupts()) 178 ; 179 180 Registers.InterruptMask.Define(this) 181 .WithFlag(0, out transmitReadyInterruptEnabled, FieldMode.Read, name: "TXRDY") 182 .WithFlag(1, out endOfConversionInterruptEnabled, FieldMode.Read, name: "EOC") 183 .WithTaggedFlag("ENDTX", 2) 184 .WithTaggedFlag("TXBUFE", 3) 185 .WithReservedBits(4, 28) 186 ; 187 188 Registers.InterruptStatus.Define(this) 189 .WithFlag(0, FieldMode.Read, name: "TXRDY", 190 valueProviderCallback: _ => true) 191 .WithFlag(1, out endOfConversionInterruptPending, FieldMode.ReadToClear, name: "EOC") 192 .WithTaggedFlag("ENDTX", 2) 193 .WithTaggedFlag("TXBUFE", 3) 194 .WithReservedBits(4, 28) 195 .WithReadCallback((_, __) => UpdateInterrupts()) 196 ; 197 198 Registers.AnalogCurrent.Define(this) 199 .WithTag("IBCTLCH0", 0, 2) 200 .WithTag("IBCTLCH1", 2, 2) 201 .WithReservedBits(4, 4) 202 .WithTag("IBCTLDACCORE", 8, 2) 203 .WithReservedBits(10, 22) 204 ; 205 206 Registers.WriteProtectionMode.Define(this) 207 .WithFlag(0, out var enableWriteProtection, FieldMode.Write, name: "WPEN") 208 .WithReservedBits(1, 7) 209 .WithValueField(8, 24, out var writeProtectionKey, FieldMode.Write, name: "WPKEY") 210 .WithWriteCallback((_, __) => 211 { 212 if(writeProtectionKey.Value != WriteProtectionKey) 213 { 214 return; 215 } 216 writeProtectionEnabled = enableWriteProtection.Value; 217 }) 218 ; 219 220 Registers.WriteProtectionStatus.Define(this) 221 .WithFlag(0, out writeViolationStatus, FieldMode.ReadToClear, name: "WPVS") 222 .WithReservedBits(1, 7) 223 .WithValueField(8, 8, out writeViolationSource, FieldMode.ReadToClear, name: "WPVSRC") 224 .WithReservedBits(16, 16) 225 ; 226 } 227 228 private bool writeProtectionEnabled; 229 230 private IFlagRegisterField wordMode; 231 private IValueRegisterField userSelectedChannel; 232 private IFlagRegisterField tagSelectionMode; 233 234 private IFlagRegisterField[] channelEnabled; 235 236 private IFlagRegisterField transmitReadyInterruptEnabled; 237 private IFlagRegisterField endOfConversionInterruptEnabled; 238 239 private IFlagRegisterField endOfConversionInterruptPending; 240 241 private IFlagRegisterField writeViolationStatus; 242 private IValueRegisterField writeViolationSource; 243 244 private readonly ulong[] writeProtectedOffsets = new ulong[] 245 { 246 (ulong)Registers.Mode, 247 (ulong)Registers.ChannelEnable, 248 (ulong)Registers.ChannelDisable, 249 (ulong)Registers.AnalogCurrent, 250 }; 251 252 private readonly ulong[] channelValue = new ulong[NumberOfChannels]; 253 254 private const int NumberOfChannels = 2; 255 private const uint WriteProtectionKey = 0x444143; 256 private const uint MaximumChannelValue = 0xFFF; 257 258 public enum Registers 259 { 260 Control = 0x00, 261 Mode = 0x04, 262 ChannelEnable = 0x10, 263 ChannelDisable = 0x14, 264 ChannelStatus = 0x18, 265 ConversionData = 0x20, 266 InterruptEnable = 0x24, 267 InterruptDisable = 0x28, 268 InterruptMask = 0x2C, 269 InterruptStatus = 0x30, 270 AnalogCurrent = 0x94, 271 WriteProtectionMode = 0xE4, 272 WriteProtectionStatus = 0xE8 273 } 274 } 275 } 276