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