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 using System; 8 using System.Collections.Generic; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Timers; 14 using Antmicro.Renode.Peripherals.UART; 15 using Antmicro.Renode.Utilities.Binding; 16 using Endianess = ELFSharp.ELF.Endianess; 17 18 namespace Antmicro.Renode.Peripherals.CPU 19 { 20 public partial class Xtensa : TranslationCPU, IPeripheralRegister<SemihostingUart, NullRegistrationPoint> 21 { Xtensa(string cpuType, IMachine machine, uint cpuId = 0, long frequency = 10000000)22 public Xtensa(string cpuType, IMachine machine, uint cpuId = 0, long frequency = 10000000) 23 : base(cpuId, cpuType, machine, Endianess.LittleEndian) 24 { 25 innerTimers = new ComparingTimer[InnerTimersCount]; 26 for(var i = 0; i < innerTimers.Length; i++) 27 { 28 var j = i; 29 innerTimers[i] = new ComparingTimer(machine.ClockSource, frequency, this, "", enabled: true, eventEnabled: true); 30 innerTimers[i].CompareReached += () => HandleCompareReached(j) ; 31 } 32 Reset(); 33 } 34 OnGPIO(int number, bool value)35 public override void OnGPIO(int number, bool value) 36 { 37 TlibSetIrqPendingBit((uint)number, value ? 1u : 0u); 38 base.OnGPIO(number, value); 39 } 40 Register(SemihostingUart peripheral, NullRegistrationPoint registrationPoint)41 public void Register(SemihostingUart peripheral, NullRegistrationPoint registrationPoint) 42 { 43 if(semihostingUart != null) 44 { 45 throw new RegistrationException("A semihosting uart is already registered."); 46 } 47 semihostingUart = peripheral; 48 machine.RegisterAsAChildOf(this, peripheral, registrationPoint); 49 } 50 Reset()51 public override void Reset() 52 { 53 base.Reset(); 54 ShouldEnterDebugMode = true; 55 } 56 Unregister(SemihostingUart peripheral)57 public void Unregister(SemihostingUart peripheral) 58 { 59 semihostingUart = null; 60 machine.UnregisterAsAChildOf(this, peripheral); 61 } 62 63 public override string Architecture { get { return "xtensa"; } } 64 65 public override ExecutionMode ExecutionMode 66 { 67 get 68 { 69 return base.ExecutionMode; 70 } 71 72 set 73 { 74 base.ExecutionMode = value; 75 TlibSetSingleStep(IsSingleStepMode ? 1u : 0u); 76 } 77 } 78 79 public override string GDBArchitecture { get { return "xtensa"; } } 80 81 public override List<GDBFeatureDescriptor> GDBFeatures => new List<GDBFeatureDescriptor>(); 82 DecodeInterrupt(int number)83 protected override Interrupt DecodeInterrupt(int number) 84 { 85 return Interrupt.Hard; 86 } 87 88 [Export] 89 /* AKA SIMCALL. After the simcall: "a return code will be stored to a2 and an error number to a3." */ DoSemihosting()90 private void DoSemihosting() 91 { 92 uint op = A[2]; 93 94 switch((XtensaSimcallOperation)op){ 95 case XtensaSimcallOperation.Write: 96 uint fd = A[3]; 97 uint vaddr = A[4]; 98 uint len = A[5]; 99 this.Log(LogLevel.Debug, "WRITE SIMCALL: fd={0}; vaddr=0x{1:X}; len={2}", fd, vaddr, len); 100 101 if(semihostingUart == null) 102 { 103 this.Log(LogLevel.Warning, "WRITE SIMCALL: Semihosting UART not available!"); 104 break; 105 } 106 if(fd != 1 && fd != 2) 107 { 108 this.Log(LogLevel.Warning, "WRITE SIMCALL: Only writing to fd=1 or fd=2 is supported!", fd); 109 break; 110 } 111 112 var buf = Bus.ReadBytes(vaddr, (int)len); 113 var bufString = System.Text.Encoding.ASCII.GetString(buf); 114 using(ObtainGenericPauseGuard()) 115 { 116 semihostingUart.SemihostingWriteString(bufString); 117 } 118 119 A[2] = bufString.Length; 120 A[3] = 0; // errno 121 return; 122 default: 123 var opType = typeof(XtensaSimcallOperation); 124 this.Log(LogLevel.Warning, "Unimplemented simcall op={0} ({1})!", op, 125 Enum.IsDefined(opType, op) ? Enum.GetName(opType, op) : "UNKNOWN"); 126 break; 127 } 128 A[2] = uint.MaxValue; 129 A[3] = 88; // ENOSYS 130 } 131 132 [Export] GetCPUTime()133 private ulong GetCPUTime() 134 { 135 SyncTime(); 136 return innerTimers[0].Value; 137 } 138 HandleCompareReached(int id)139 private void HandleCompareReached(int id) 140 { 141 // this is a mapping for sample_controller 142 var intMap = new uint[] { 6, 10, 13 }; 143 144 // this is a mapping for baytrail 145 // var intMap = new uint[] { 1, 5, 7 }; 146 TlibSetIrqPendingBit(intMap[id], 1u); 147 } 148 149 [Export] TimerMod(uint id, ulong value)150 private void TimerMod(uint id, ulong value) 151 { 152 if(id >= InnerTimersCount) 153 { 154 throw new Exception($"Unsupported compare #{id}"); 155 } 156 157 innerTimers[id].Compare = value; 158 } 159 160 private readonly ComparingTimer[] innerTimers; 161 private SemihostingUart semihostingUart = null; 162 163 private const int InnerTimersCount = 3; 164 165 // 649: Field '...' is never assigned to, and will always have its default value null 166 #pragma warning disable 649 167 [Import] 168 private Action<uint, uint> TlibSetIrqPendingBit; 169 170 [Import] 171 private Action<uint> TlibSetSingleStep; 172 #pragma warning restore 649 173 174 private enum XtensaSimcallOperation : uint 175 { 176 Exit = 1, 177 Read = 3, 178 Write = 4, 179 Open = 5, 180 Close = 6, 181 Lseek = 19, 182 SelectOne = 29, 183 ReadArgc = 1000, 184 ReadArgvSize = 1001, 185 ReadArgv = 1002, 186 Memset = 1004, 187 } 188 } 189 } 190 191