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 #ifndef Cpu_h
9 #define Cpu_h
10 
11 #include "../renode.h"
12 #include "../renode_bus.h"
13 #include "cpu-interface.h"
14 
15 class CpuAgent : public RenodeAgent
16 {
17 public:
18     using RenodeAgent::RenodeAgent;
19 
addCPU(DebuggableCPU * cpu)20     void addCPU(DebuggableCPU *cpu)
21     {
22         this->cpu = cpu;
23     }
24 
tick(bool countEnable,uint64_t steps)25     void tick(bool countEnable, uint64_t steps) override
26     {
27         for (size_t i = 0; i < steps; i++)
28         {
29             for (auto &bus : initatorInterfaces)
30             {
31                 bus->readHandler();
32                 bus->writeHandler();
33             }
34             cpu->clkHigh();
35             cpu->evaluateModel();
36             cpu->clkLow();
37             cpu->evaluateModel();
38 
39             for (auto &bus : initatorInterfaces)
40                 bus->clearSignals();
41         }
42         if (countEnable)
43             tickCounter += steps;
44     }
45 
handleRequest(Protocol * message)46     void handleRequest(Protocol *message) override
47     {
48         switch (message->actionId)
49         {
50         case interrupt:
51             cpu->onGPIO(message->addr, message->value);
52             break;
53 
54         case registerGet:
55             communicationChannel->sendSender(Protocol(registerGet, message->addr, getRegister(message->addr)));
56             break;
57 
58         case registerSet:
59             setRegister(message->addr, message->value);
60             communicationChannel->sendSender(Protocol(registerSet, 0, 0));
61             break;
62 
63         case singleStepMode:
64             if (message->value)
65             {
66                 if (!inSingleStepMode)
67                     enterSingleStepMode();
68             }
69             else
70             {
71                 if (inSingleStepMode)
72                     exitSingleStepMode();
73             }
74             communicationChannel->sendSender(Protocol(singleStepMode, 0, 0));
75             break;
76 
77         case tickClock:
78         {
79             int64_t ticks = 0;
80             if (!inSingleStepMode)
81             {
82                 ticks = message->value - tickCounter;
83                 if (ticks < 0)
84                     tickCounter -= message->value;
85                 else
86                 {
87                     tick(false, ticks);
88                     tickCounter = 0;
89                 }
90 
91                 bool halted = cpu->isHalted();
92                 if (wasHalted != halted)
93                 {
94                     communicationChannel->sendSender(Protocol(isHalted, 0, halted));
95                     wasHalted = halted;
96                 }
97             }
98             ticks = ticks > 0 ? ticks : 0;
99             communicationChannel->sendSender(Protocol(tickClock, 0, ticks));
100         }
101         break;
102 
103         case step:
104         {
105             int64_t ticks = 0;
106             if (inSingleStepMode)
107             {
108                 ticks = message->value - tickCounter;
109                 if (ticks < 0)
110                     tickCounter -= message->value;
111                 else
112                 {
113                     for (int64_t i = 0; i < ticks; i++)
114                     {
115                         waitForNonDebugProgramInstruction();
116                         waitForFirstDebugProgramInstruction();
117                     }
118                     tickCounter = 0;
119                 }
120 
121                 ticks = ticks > 0 ? ticks : 0;
122                 communicationChannel->sendSender(Protocol(step, 0, ticks));
123             }
124         }
125         break;
126 
127         default:
128             RenodeAgent::handleRequest(message);
129             break;
130         }
131     }
132 
reset()133     void reset() override
134     {
135         cpu->reset();
136     }
137 
getRegister(uint64_t id)138     uint64_t getRegister(uint64_t id)
139     {
140         log(LOG_LEVEL_DEBUG, "Start getRegister");
141         debugProgram = cpu->getRegisterGetProgram(id);
142 
143         cpu->debugRequest(true);
144         waitForFirstDebugProgramInstruction();
145         if (!inSingleStepMode)
146             cpu->debugRequest(false);
147         runDebugProgram(true);
148 
149         log(LOG_LEVEL_DEBUG, "End getRegister");
150         return debugProgramReturnValue;
151     }
152 
setRegister(uint64_t id,uint64_t value)153     void setRegister(uint64_t id, uint64_t value)
154     {
155         log(LOG_LEVEL_DEBUG, "Start setRegister");
156         debugProgram = cpu->getRegisterSetProgram(id, value);
157 
158         cpu->debugRequest(true);
159         waitForFirstDebugProgramInstruction();
160         if (!inSingleStepMode)
161             cpu->debugRequest(false);
162         runDebugProgram(false);
163 
164         log(LOG_LEVEL_DEBUG, "End setRegister");
165     }
166 
enterSingleStepMode()167     void enterSingleStepMode()
168     {
169         log(LOG_LEVEL_DEBUG, "Start enterSingleStepMode");
170         debugProgram = cpu->getEnterSingleStepModeProgram();
171 
172         cpu->debugRequest(true);
173         waitForFirstDebugProgramInstruction();
174         cpu->debugRequest(false);
175         runDebugProgram(false);
176 
177         log(LOG_LEVEL_DEBUG, "End enterSingleStepMode");
178         inSingleStepMode = true;
179         debugProgram = cpu->getSingleStepModeProgram();
180     }
181 
exitSingleStepMode()182     void exitSingleStepMode()
183     {
184         log(LOG_LEVEL_DEBUG, "Start exitSingleStepMode");
185         inSingleStepMode = false;
186         debugProgram = cpu->getExitSingleStepModeProgram();
187 
188         cpu->debugRequest(true);
189         waitForFirstDebugProgramInstruction();
190         cpu->debugRequest(false);
191         runDebugProgram(false);
192 
193         log(LOG_LEVEL_DEBUG, "End exitSingleStepMode");
194         waitForNonDebugProgramInstruction();
195         debugProgram = {};
196     }
197 
pushByteToAgent(uint64_t addr,uint8_t value)198     void pushByteToAgent(uint64_t addr, uint8_t value) override
199     {
200         if (!inDebugMode)
201             RenodeAgent::pushByteToAgent(addr, value);
202         else
203             debugProgramReturn(addr, value);
204     }
205 
pushWordToAgent(uint64_t addr,uint16_t value)206     void pushWordToAgent(uint64_t addr, uint16_t value) override
207     {
208         if (!inDebugMode)
209             RenodeAgent::pushWordToAgent(addr, value);
210         else
211             debugProgramReturn(addr, value);
212     }
213 
pushDoubleWordToAgent(uint64_t addr,uint32_t value)214     void pushDoubleWordToAgent(uint64_t addr, uint32_t value) override
215     {
216         if (!inDebugMode)
217             RenodeAgent::pushDoubleWordToAgent(addr, value);
218         else
219             debugProgramReturn(addr, value);
220     }
221 
222 
requestDoubleWordFromAgent(uint64_t addr)223     uint64_t requestDoubleWordFromAgent(uint64_t addr) override
224     {
225         if (!inDebugMode)
226         {
227             debugProgramOrPrefetch = debugProgramOrPrefetch && (lastRequestAddress + 4 == addr);
228             lastRequestAddress = addr;
229 
230             if (inSingleStepMode && inDebugProgramRange(addr))
231             {
232                 debugProgramOrPrefetch = true;
233                 return debugProgram.memory[(addr - debugProgram.address) / 4];
234             }
235             else
236             {
237                 if (debugProgramOrPrefetch)
238                     return debugProgram.memory.back(); // CPU is prefetching debug program
239                 else
240                     return RenodeAgent::requestDoubleWordFromAgent(addr);
241             }
242         }
243         else
244         {
245             debugProgramOrPrefetch = true;
246             lastRequestAddress = addr;
247 
248             if (inDebugProgramRange(addr))
249             {
250                 debugProgramReadCount++;
251                 uint64_t idx = (addr - debugProgram.address) / 4;
252                 if (idx + 1 == debugProgram.memory.size())
253                     debugProgramReadLastInstruction = true;
254                 return debugProgram.memory[idx];
255             }
256             else
257                 return debugProgram.memory.back(); // CPU is probably prefetching, last program instruction should be debug return instruction
258         }
259     }
260 
261 private:
waitForFirstDebugProgramInstruction()262     void waitForFirstDebugProgramInstruction()
263     {
264         bool adressSpecified = false;
265 
266         while (true)
267         {
268             for (auto &bus : initatorInterfaces)
269             {
270                 log(LOG_LEVEL_DEBUG, "Waiting for first debug program instruction access");
271                 if (bus->hasSpecifiedAdress() && bus->getSpecifiedAdress() == debugProgram.address)
272                 {
273                     log(LOG_LEVEL_DEBUG, "Finished waiting");
274                     adressSpecified = true;
275                     break;
276                 }
277             }
278             if (adressSpecified)
279                 break;
280             tick(false, 1);
281             tickCounter++;
282         }
283     }
284 
waitForNonDebugProgramInstruction()285     void waitForNonDebugProgramInstruction()
286     {
287         bool adressSpecified = false;
288 
289         while (true)
290         {
291             for (auto &bus : initatorInterfaces)
292             {
293                 log(LOG_LEVEL_DEBUG, "Waiting for non debug program instruction access");
294                 if (bus->hasSpecifiedAdress() && !inDebugProgramRange(bus->getSpecifiedAdress()) && !debugProgramOrPrefetch)
295                 {
296                     log(LOG_LEVEL_DEBUG, "Finished waiting");
297                     adressSpecified = true;
298                     break;
299                 }
300             }
301             if (adressSpecified)
302                 break;
303             tick(false, 1);
304             tickCounter++;
305         }
306     }
307 
runDebugProgram(bool withReturnSuccess)308     void runDebugProgram(bool withReturnSuccess)
309     {
310         if (inSingleStepMode) // Re-enter debug mode after running program
311             cpu->debugRequest(true);
312 
313         inDebugMode = true;
314 
315         debugProgramReadCount = 0;
316         debugProgramReturnSuccess = false;
317         debugProgramReadLastInstruction = false;
318 
319         while (debugProgramReadCount < debugProgram.readCount || (!debugProgramReturnSuccess && withReturnSuccess) || !debugProgramReadLastInstruction)
320         {
321             log(LOG_LEVEL_DEBUG, "runDebugProgram tick start");
322             tick(false, 1);
323             log(LOG_LEVEL_DEBUG, "runDebugProgram tick end");
324         }
325 
326         inDebugMode = false;
327 
328         if (inSingleStepMode)
329         {
330             waitForFirstDebugProgramInstruction();
331             cpu->debugRequest(false);
332             debugProgram = cpu->getSingleStepModeProgram();
333         }
334     }
335 
debugProgramReturn(uint64_t addr,uint64_t value)336     void debugProgramReturn(uint64_t addr, uint64_t value)
337     {
338         if (addr != 0)
339                 throw "debug program writes to non 0 address";
340             if (debugProgramReturnSuccess)
341                 throw "debug program have already written return value";
342 
343             debugProgramReturnValue = value;
344             debugProgramReturnSuccess = true;
345     }
346 
inDebugProgramRange(uint64_t addr)347     bool inDebugProgramRange(uint64_t addr)
348     {
349         return addr >= debugProgram.address && addr < debugProgram.address + debugProgram.memory.size() * 4;
350     }
351 
352     DebuggableCPU *cpu = nullptr;
353     DebuggableCPU::DebugProgram debugProgram;
354     uint64_t debugProgramReadCount = 0;
355     uint64_t debugProgramReturnValue;
356     int64_t tickCounter = 0;
357     uint64_t lastRequestAddress = 0;
358     bool debugProgramReturnSuccess = false;
359     bool debugProgramReadLastInstruction = false;
360     bool wasHalted = false;
361 
362     bool inDebugMode = false;
363     bool inSingleStepMode = false;
364 
365     bool debugProgramOrPrefetch = false;
366 };
367 
368 #endif
369