xref: /netbsd-src/external/gpl3/gdb/dist/sim/microblaze/interp.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1 /* Simulator for Xilinx MicroBlaze processor
2    Copyright 2009-2024 Free Software Foundation, Inc.
3 
4    This file is part of GDB, the GNU debugger.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
18 
19 /* This must come before any other includes.  */
20 #include "defs.h"
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include "bfd.h"
26 #include "sim/callback.h"
27 #include "libiberty.h"
28 #include "sim/sim.h"
29 
30 #include "sim-main.h"
31 #include "sim-options.h"
32 #include "sim-signal.h"
33 #include "sim-syscall.h"
34 
35 #include "microblaze-sim.h"
36 #include "opcodes/microblaze-dis.h"
37 
38 #define target_big_endian (CURRENT_TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
39 
40 static unsigned long
41 microblaze_extract_unsigned_integer (const unsigned char *addr, int len)
42 {
43   unsigned long retval;
44   unsigned char *p;
45   unsigned char *startaddr = (unsigned char *)addr;
46   unsigned char *endaddr = startaddr + len;
47 
48   if (len > (int) sizeof (unsigned long))
49     printf ("That operation is not available on integers of more than "
50 	    "%zu bytes.", sizeof (unsigned long));
51 
52   /* Start at the most significant end of the integer, and work towards
53      the least significant.  */
54   retval = 0;
55 
56   if (!target_big_endian)
57     {
58       for (p = endaddr; p > startaddr;)
59 	retval = (retval << 8) | * -- p;
60     }
61   else
62     {
63       for (p = startaddr; p < endaddr;)
64 	retval = (retval << 8) | * p ++;
65     }
66 
67   return retval;
68 }
69 
70 static void
71 microblaze_store_unsigned_integer (unsigned char *addr, int len,
72 				   unsigned long val)
73 {
74   unsigned char *p;
75   unsigned char *startaddr = (unsigned char *)addr;
76   unsigned char *endaddr = startaddr + len;
77 
78   if (!target_big_endian)
79     {
80       for (p = startaddr; p < endaddr;)
81 	{
82 	  *p++ = val & 0xff;
83 	  val >>= 8;
84 	}
85     }
86   else
87     {
88       for (p = endaddr; p > startaddr;)
89 	{
90 	  *--p = val & 0xff;
91 	  val >>= 8;
92 	}
93     }
94 }
95 
96 static void
97 set_initial_gprs (SIM_CPU *cpu)
98 {
99   int i;
100 
101   /* Set up machine just out of reset.  */
102   PC = 0;
103   MSR = 0;
104 
105   /* Clean out the GPRs */
106   for (i = 0; i < 32; i++)
107     CPU.regs[i] = 0;
108   CPU.insts = 0;
109   CPU.cycles = 0;
110   CPU.imm_enable = 0;
111 }
112 
113 static int tracing = 0;
114 
115 void
116 sim_engine_run (SIM_DESC sd,
117 		int next_cpu_nr, /* ignore  */
118 		int nr_cpus, /* ignore  */
119 		int siggnal) /* ignore  */
120 {
121   SIM_CPU *cpu = STATE_CPU (sd, 0);
122   signed_4 inst;
123   enum microblaze_instr op;
124   int memops;
125   int bonus_cycles;
126   int insts;
127   unsigned_1 carry;
128   bool imm_unsigned;
129   short ra, rb, rd;
130   unsigned_4 oldpc, newpc;
131   short delay_slot_enable;
132   short branch_taken;
133   short num_delay_slot; /* UNUSED except as reqd parameter */
134   enum microblaze_instr_type insn_type;
135 
136   memops = 0;
137   bonus_cycles = 0;
138   insts = 0;
139 
140   while (1)
141     {
142       /* Fetch the initial instructions that we'll decode. */
143       inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
144 
145       op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
146 				&num_delay_slot);
147 
148       if (op == invalid_inst)
149 	fprintf (stderr, "Unknown instruction 0x%04x", inst);
150 
151       if (tracing)
152 	fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
153 
154       rd = GET_RD;
155       rb = GET_RB;
156       ra = GET_RA;
157       /*      immword = IMM_W; */
158 
159       oldpc = PC;
160       delay_slot_enable = 0;
161       branch_taken = 0;
162       if (op == microblaze_brk)
163 	sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
164       else if (inst == MICROBLAZE_HALT_INST)
165 	{
166 	  insts += 1;
167 	  bonus_cycles++;
168 	  TRACE_INSN (cpu, "HALT (%i)", RETREG);
169 	  sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG);
170 	}
171       else
172 	{
173 	  switch(op)
174 	    {
175 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION)		\
176 	    case NAME:					\
177 	      TRACE_INSN (cpu, #NAME);			\
178 	      ACTION;					\
179 	      break;
180 #include "microblaze.isa"
181 #undef INSTRUCTION
182 
183 	    default:
184 	      sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
185 			       SIM_SIGILL);
186 	      fprintf (stderr, "ERROR: Unknown opcode\n");
187 	    }
188 	  /* Make R0 consistent */
189 	  CPU.regs[0] = 0;
190 
191 	  /* Check for imm instr */
192 	  if (op == imm)
193 	    IMM_ENABLE = 1;
194 	  else
195 	    IMM_ENABLE = 0;
196 
197 	  /* Update cycle counts */
198 	  insts ++;
199 	  if (insn_type == memory_store_inst || insn_type == memory_load_inst)
200 	    memops++;
201 	  if (insn_type == mult_inst)
202 	    bonus_cycles++;
203 	  if (insn_type == barrel_shift_inst)
204 	    bonus_cycles++;
205 	  if (insn_type == anyware_inst)
206 	    bonus_cycles++;
207 	  if (insn_type == div_inst)
208 	    bonus_cycles += 33;
209 
210 	  if ((insn_type == branch_inst || insn_type == return_inst)
211 	      && branch_taken)
212 	    {
213 	      /* Add an extra cycle for taken branches */
214 	      bonus_cycles++;
215 	      /* For branch instructions handle the instruction in the delay slot */
216 	      if (delay_slot_enable)
217 	        {
218 	          newpc = PC;
219 	          PC = oldpc + INST_SIZE;
220 	          inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
221 	          op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
222 					    &num_delay_slot);
223 	          if (op == invalid_inst)
224 		    fprintf (stderr, "Unknown instruction 0x%04x", inst);
225 	          if (tracing)
226 		    fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
227 	          rd = GET_RD;
228 	          rb = GET_RB;
229 	          ra = GET_RA;
230 	          /*	      immword = IMM_W; */
231 	          if (op == microblaze_brk)
232 		    {
233 		      if (STATE_VERBOSE_P (sd))
234 		        fprintf (stderr, "Breakpoint set in delay slot "
235 			         "(at address 0x%x) will not be honored\n", PC);
236 		      /* ignore the breakpoint */
237 		    }
238 	          else if (insn_type == branch_inst || insn_type == return_inst)
239 		    {
240 		      if (STATE_VERBOSE_P (sd))
241 		        fprintf (stderr, "Cannot have branch or return instructions "
242 			         "in delay slot (at address 0x%x)\n", PC);
243 		      sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
244 				       SIM_SIGILL);
245 		    }
246 	          else
247 		    {
248 		      switch(op)
249 		        {
250 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION)		\
251 		        case NAME:			\
252 			  ACTION;			\
253 			  break;
254 #include "microblaze.isa"
255 #undef INSTRUCTION
256 
257 		        default:
258 		          sim_engine_halt (sd, NULL, NULL, NULL_CIA,
259 					   sim_signalled, SIM_SIGILL);
260 		          fprintf (stderr, "ERROR: Unknown opcode at 0x%x\n", PC);
261 		        }
262 		      /* Update cycle counts */
263 		      insts++;
264 		      if (insn_type == memory_store_inst
265 		          || insn_type == memory_load_inst)
266 		        memops++;
267 		      if (insn_type == mult_inst)
268 		        bonus_cycles++;
269 		      if (insn_type == barrel_shift_inst)
270 		        bonus_cycles++;
271 		      if (insn_type == anyware_inst)
272 		        bonus_cycles++;
273 		      if (insn_type == div_inst)
274 		        bonus_cycles += 33;
275 		    }
276 	          /* Restore the PC */
277 	          PC = newpc;
278 	          /* Make R0 consistent */
279 	          CPU.regs[0] = 0;
280 	          /* Check for imm instr */
281 	          if (op == imm)
282 		    IMM_ENABLE = 1;
283 	          else
284 		    IMM_ENABLE = 0;
285 	        }
286 	      else
287 		{
288 		  if (op == brki && IMM == 8)
289 		    {
290 		      RETREG = sim_syscall (cpu, CPU.regs[12], CPU.regs[5],
291 					    CPU.regs[6], CPU.regs[7],
292 					    CPU.regs[8]);
293 		      PC = RD + INST_SIZE;
294 		    }
295 
296 		  /* no delay slot: increment cycle count */
297 		  bonus_cycles++;
298 		}
299 	    }
300 	}
301 
302       if (tracing)
303 	fprintf (stderr, "\n");
304 
305       if (sim_events_tick (sd))
306 	sim_events_process (sd);
307     }
308 
309   /* Hide away the things we've cached while executing.  */
310   /*  CPU.pc = pc; */
311   CPU.insts += insts;		/* instructions done ... */
312   CPU.cycles += insts;		/* and each takes a cycle */
313   CPU.cycles += bonus_cycles;	/* and extra cycles for branches */
314   CPU.cycles += memops; 	/* and memop cycle delays */
315 }
316 
317 static int
318 microblaze_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length)
319 {
320   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
321     {
322       if (length == 4)
323 	{
324 	  /* misalignment safe */
325 	  long ival = microblaze_extract_unsigned_integer (memory, 4);
326 	  if (rn < NUM_REGS)
327 	    CPU.regs[rn] = ival;
328 	  else
329 	    CPU.spregs[rn-NUM_REGS] = ival;
330 	  return 4;
331 	}
332       else
333 	return 0;
334     }
335   else
336     return 0;
337 }
338 
339 static int
340 microblaze_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length)
341 {
342   long ival;
343 
344   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
345     {
346       if (length == 4)
347 	{
348 	  if (rn < NUM_REGS)
349 	    ival = CPU.regs[rn];
350 	  else
351 	    ival = CPU.spregs[rn-NUM_REGS];
352 
353 	  /* misalignment-safe */
354 	  microblaze_store_unsigned_integer (memory, 4, ival);
355 	  return 4;
356 	}
357       else
358 	return 0;
359     }
360   else
361     return 0;
362 }
363 
364 void
365 sim_info (SIM_DESC sd, bool verbose)
366 {
367   SIM_CPU *cpu = STATE_CPU (sd, 0);
368   host_callback *callback = STATE_CALLBACK (sd);
369 
370   callback->printf_filtered (callback, "\n\n# instructions executed  %10d\n",
371 			     CPU.insts);
372   callback->printf_filtered (callback, "# cycles                 %10d\n",
373 			     (CPU.cycles) ? CPU.cycles+2 : 0);
374 }
375 
376 static sim_cia
377 microblaze_pc_get (sim_cpu *cpu)
378 {
379   return MICROBLAZE_SIM_CPU (cpu)->spregs[0];
380 }
381 
382 static void
383 microblaze_pc_set (sim_cpu *cpu, sim_cia pc)
384 {
385   MICROBLAZE_SIM_CPU (cpu)->spregs[0] = pc;
386 }
387 
388 static void
389 free_state (SIM_DESC sd)
390 {
391   if (STATE_MODULES (sd) != NULL)
392     sim_module_uninstall (sd);
393   sim_cpu_free_all (sd);
394   sim_state_free (sd);
395 }
396 
397 SIM_DESC
398 sim_open (SIM_OPEN_KIND kind, host_callback *cb,
399 	  struct bfd *abfd, char * const *argv)
400 {
401   int i;
402   SIM_DESC sd = sim_state_alloc (kind, cb);
403   SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
404 
405   /* The cpu data is kept in a separately allocated chunk of memory.  */
406   if (sim_cpu_alloc_all_extra (sd, 0, sizeof (struct microblaze_regset))
407       != SIM_RC_OK)
408     {
409       free_state (sd);
410       return 0;
411     }
412 
413   if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
414     {
415       free_state (sd);
416       return 0;
417     }
418 
419   /* The parser will print an error message for us, so we silently return.  */
420   if (sim_parse_args (sd, argv) != SIM_RC_OK)
421     {
422       free_state (sd);
423       return 0;
424     }
425 
426   /* Check for/establish the a reference program image.  */
427   if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK)
428     {
429       free_state (sd);
430       return 0;
431     }
432 
433   /* Configure/verify the target byte order and other runtime
434      configuration options.  */
435   if (sim_config (sd) != SIM_RC_OK)
436     {
437       sim_module_uninstall (sd);
438       return 0;
439     }
440 
441   if (sim_post_argv_init (sd) != SIM_RC_OK)
442     {
443       /* Uninstall the modules to avoid memory leaks,
444 	 file descriptor leaks, etc.  */
445       sim_module_uninstall (sd);
446       return 0;
447     }
448 
449   /* CPU specific initialization.  */
450   for (i = 0; i < MAX_NR_PROCESSORS; ++i)
451     {
452       SIM_CPU *cpu = STATE_CPU (sd, i);
453 
454       CPU_REG_FETCH (cpu) = microblaze_reg_fetch;
455       CPU_REG_STORE (cpu) = microblaze_reg_store;
456       CPU_PC_FETCH (cpu) = microblaze_pc_get;
457       CPU_PC_STORE (cpu) = microblaze_pc_set;
458 
459       set_initial_gprs (cpu);
460     }
461 
462   /* Default to a 8 Mbyte (== 2^23) memory space.  */
463   sim_do_commandf (sd, "memory-size 0x800000");
464 
465   return sd;
466 }
467 
468 SIM_RC
469 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
470 		     char * const *argv, char * const *env)
471 {
472   SIM_CPU *cpu = STATE_CPU (sd, 0);
473 
474   PC = bfd_get_start_address (prog_bfd);
475 
476   return SIM_RC_OK;
477 }
478