xref: /netbsd-src/external/gpl3/gdb.old/dist/sim/microblaze/interp.c (revision 32d1c65c71fbdb65a012e8392a62a757dd6853e9)
1 /* Simulator for Xilinx MicroBlaze processor
2    Copyright 2009-2023 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 <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include "bfd.h"
27 #include "sim/callback.h"
28 #include "libiberty.h"
29 #include "sim/sim.h"
30 
31 #include "sim-main.h"
32 #include "sim-options.h"
33 #include "sim-signal.h"
34 #include "sim-syscall.h"
35 
36 #include "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   long space;
101 
102   /* Set up machine just out of reset.  */
103   PC = 0;
104   MSR = 0;
105 
106   /* Clean out the GPRs */
107   for (i = 0; i < 32; i++)
108     CPU.regs[i] = 0;
109   CPU.insts = 0;
110   CPU.cycles = 0;
111   CPU.imm_enable = 0;
112 }
113 
114 static int tracing = 0;
115 
116 void
117 sim_engine_run (SIM_DESC sd,
118 		int next_cpu_nr, /* ignore  */
119 		int nr_cpus, /* ignore  */
120 		int siggnal) /* ignore  */
121 {
122   SIM_CPU *cpu = STATE_CPU (sd, 0);
123   int needfetch;
124   signed_4 inst;
125   enum microblaze_instr op;
126   int memops;
127   int bonus_cycles;
128   int insts;
129   int w;
130   int cycs;
131   signed_4 WLhash;
132   unsigned_1 carry;
133   bool imm_unsigned;
134   short ra, rb, rd;
135   long immword;
136   unsigned_4 oldpc, newpc;
137   short delay_slot_enable;
138   short branch_taken;
139   short num_delay_slot; /* UNUSED except as reqd parameter */
140   enum microblaze_instr_type insn_type;
141 
142   memops = 0;
143   bonus_cycles = 0;
144   insts = 0;
145 
146   while (1)
147     {
148       /* Fetch the initial instructions that we'll decode. */
149       inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
150 
151       op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
152 				&num_delay_slot);
153 
154       if (op == invalid_inst)
155 	fprintf (stderr, "Unknown instruction 0x%04x", inst);
156 
157       if (tracing)
158 	fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
159 
160       rd = GET_RD;
161       rb = GET_RB;
162       ra = GET_RA;
163       /*      immword = IMM_W; */
164 
165       oldpc = PC;
166       delay_slot_enable = 0;
167       branch_taken = 0;
168       if (op == microblaze_brk)
169 	sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
170       else if (inst == MICROBLAZE_HALT_INST)
171 	{
172 	  insts += 1;
173 	  bonus_cycles++;
174 	  TRACE_INSN (cpu, "HALT (%i)", RETREG);
175 	  sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG);
176 	}
177       else
178 	{
179 	  switch(op)
180 	    {
181 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION)		\
182 	    case NAME:					\
183 	      TRACE_INSN (cpu, #NAME);			\
184 	      ACTION;					\
185 	      break;
186 #include "microblaze.isa"
187 #undef INSTRUCTION
188 
189 	    default:
190 	      sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
191 			       SIM_SIGILL);
192 	      fprintf (stderr, "ERROR: Unknown opcode\n");
193 	    }
194 	  /* Make R0 consistent */
195 	  CPU.regs[0] = 0;
196 
197 	  /* Check for imm instr */
198 	  if (op == imm)
199 	    IMM_ENABLE = 1;
200 	  else
201 	    IMM_ENABLE = 0;
202 
203 	  /* Update cycle counts */
204 	  insts ++;
205 	  if (insn_type == memory_store_inst || insn_type == memory_load_inst)
206 	    memops++;
207 	  if (insn_type == mult_inst)
208 	    bonus_cycles++;
209 	  if (insn_type == barrel_shift_inst)
210 	    bonus_cycles++;
211 	  if (insn_type == anyware_inst)
212 	    bonus_cycles++;
213 	  if (insn_type == div_inst)
214 	    bonus_cycles += 33;
215 
216 	  if ((insn_type == branch_inst || insn_type == return_inst)
217 	      && branch_taken)
218 	    {
219 	      /* Add an extra cycle for taken branches */
220 	      bonus_cycles++;
221 	      /* For branch instructions handle the instruction in the delay slot */
222 	      if (delay_slot_enable)
223 	        {
224 	          newpc = PC;
225 	          PC = oldpc + INST_SIZE;
226 	          inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
227 	          op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
228 					    &num_delay_slot);
229 	          if (op == invalid_inst)
230 		    fprintf (stderr, "Unknown instruction 0x%04x", inst);
231 	          if (tracing)
232 		    fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
233 	          rd = GET_RD;
234 	          rb = GET_RB;
235 	          ra = GET_RA;
236 	          /*	      immword = IMM_W; */
237 	          if (op == microblaze_brk)
238 		    {
239 		      if (STATE_VERBOSE_P (sd))
240 		        fprintf (stderr, "Breakpoint set in delay slot "
241 			         "(at address 0x%x) will not be honored\n", PC);
242 		      /* ignore the breakpoint */
243 		    }
244 	          else if (insn_type == branch_inst || insn_type == return_inst)
245 		    {
246 		      if (STATE_VERBOSE_P (sd))
247 		        fprintf (stderr, "Cannot have branch or return instructions "
248 			         "in delay slot (at address 0x%x)\n", PC);
249 		      sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
250 				       SIM_SIGILL);
251 		    }
252 	          else
253 		    {
254 		      switch(op)
255 		        {
256 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION)		\
257 		        case NAME:			\
258 			  ACTION;			\
259 			  break;
260 #include "microblaze.isa"
261 #undef INSTRUCTION
262 
263 		        default:
264 		          sim_engine_halt (sd, NULL, NULL, NULL_CIA,
265 					   sim_signalled, SIM_SIGILL);
266 		          fprintf (stderr, "ERROR: Unknown opcode at 0x%x\n", PC);
267 		        }
268 		      /* Update cycle counts */
269 		      insts++;
270 		      if (insn_type == memory_store_inst
271 		          || insn_type == memory_load_inst)
272 		        memops++;
273 		      if (insn_type == mult_inst)
274 		        bonus_cycles++;
275 		      if (insn_type == barrel_shift_inst)
276 		        bonus_cycles++;
277 		      if (insn_type == anyware_inst)
278 		        bonus_cycles++;
279 		      if (insn_type == div_inst)
280 		        bonus_cycles += 33;
281 		    }
282 	          /* Restore the PC */
283 	          PC = newpc;
284 	          /* Make R0 consistent */
285 	          CPU.regs[0] = 0;
286 	          /* Check for imm instr */
287 	          if (op == imm)
288 		    IMM_ENABLE = 1;
289 	          else
290 		    IMM_ENABLE = 0;
291 	        }
292 	      else
293 		{
294 		  if (op == brki && IMM == 8)
295 		    {
296 		      RETREG = sim_syscall (cpu, CPU.regs[12], CPU.regs[5],
297 					    CPU.regs[6], CPU.regs[7],
298 					    CPU.regs[8]);
299 		      PC = RD + INST_SIZE;
300 		    }
301 
302 		  /* no delay slot: increment cycle count */
303 		  bonus_cycles++;
304 		}
305 	    }
306 	}
307 
308       if (tracing)
309 	fprintf (stderr, "\n");
310 
311       if (sim_events_tick (sd))
312 	sim_events_process (sd);
313     }
314 
315   /* Hide away the things we've cached while executing.  */
316   /*  CPU.pc = pc; */
317   CPU.insts += insts;		/* instructions done ... */
318   CPU.cycles += insts;		/* and each takes a cycle */
319   CPU.cycles += bonus_cycles;	/* and extra cycles for branches */
320   CPU.cycles += memops; 	/* and memop cycle delays */
321 }
322 
323 static int
324 microblaze_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length)
325 {
326   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
327     {
328       if (length == 4)
329 	{
330 	  /* misalignment safe */
331 	  long ival = microblaze_extract_unsigned_integer (memory, 4);
332 	  if (rn < NUM_REGS)
333 	    CPU.regs[rn] = ival;
334 	  else
335 	    CPU.spregs[rn-NUM_REGS] = ival;
336 	  return 4;
337 	}
338       else
339 	return 0;
340     }
341   else
342     return 0;
343 }
344 
345 static int
346 microblaze_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length)
347 {
348   long ival;
349 
350   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
351     {
352       if (length == 4)
353 	{
354 	  if (rn < NUM_REGS)
355 	    ival = CPU.regs[rn];
356 	  else
357 	    ival = CPU.spregs[rn-NUM_REGS];
358 
359 	  /* misalignment-safe */
360 	  microblaze_store_unsigned_integer (memory, 4, ival);
361 	  return 4;
362 	}
363       else
364 	return 0;
365     }
366   else
367     return 0;
368 }
369 
370 void
371 sim_info (SIM_DESC sd, int verbose)
372 {
373   SIM_CPU *cpu = STATE_CPU (sd, 0);
374   host_callback *callback = STATE_CALLBACK (sd);
375 
376   callback->printf_filtered (callback, "\n\n# instructions executed  %10d\n",
377 			     CPU.insts);
378   callback->printf_filtered (callback, "# cycles                 %10d\n",
379 			     (CPU.cycles) ? CPU.cycles+2 : 0);
380 }
381 
382 static sim_cia
383 microblaze_pc_get (sim_cpu *cpu)
384 {
385   return cpu->microblaze_cpu.spregs[0];
386 }
387 
388 static void
389 microblaze_pc_set (sim_cpu *cpu, sim_cia pc)
390 {
391   cpu->microblaze_cpu.spregs[0] = pc;
392 }
393 
394 static void
395 free_state (SIM_DESC sd)
396 {
397   if (STATE_MODULES (sd) != NULL)
398     sim_module_uninstall (sd);
399   sim_cpu_free_all (sd);
400   sim_state_free (sd);
401 }
402 
403 SIM_DESC
404 sim_open (SIM_OPEN_KIND kind, host_callback *cb,
405 	  struct bfd *abfd, char * const *argv)
406 {
407   int i;
408   SIM_DESC sd = sim_state_alloc (kind, cb);
409   SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
410 
411   /* The cpu data is kept in a separately allocated chunk of memory.  */
412   if (sim_cpu_alloc_all (sd, 1) != SIM_RC_OK)
413     {
414       free_state (sd);
415       return 0;
416     }
417 
418   if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
419     {
420       free_state (sd);
421       return 0;
422     }
423 
424   /* The parser will print an error message for us, so we silently return.  */
425   if (sim_parse_args (sd, argv) != SIM_RC_OK)
426     {
427       free_state (sd);
428       return 0;
429     }
430 
431   /* Check for/establish the a reference program image.  */
432   if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK)
433     {
434       free_state (sd);
435       return 0;
436     }
437 
438   /* Configure/verify the target byte order and other runtime
439      configuration options.  */
440   if (sim_config (sd) != SIM_RC_OK)
441     {
442       sim_module_uninstall (sd);
443       return 0;
444     }
445 
446   if (sim_post_argv_init (sd) != SIM_RC_OK)
447     {
448       /* Uninstall the modules to avoid memory leaks,
449 	 file descriptor leaks, etc.  */
450       sim_module_uninstall (sd);
451       return 0;
452     }
453 
454   /* CPU specific initialization.  */
455   for (i = 0; i < MAX_NR_PROCESSORS; ++i)
456     {
457       SIM_CPU *cpu = STATE_CPU (sd, i);
458 
459       CPU_REG_FETCH (cpu) = microblaze_reg_fetch;
460       CPU_REG_STORE (cpu) = microblaze_reg_store;
461       CPU_PC_FETCH (cpu) = microblaze_pc_get;
462       CPU_PC_STORE (cpu) = microblaze_pc_set;
463 
464       set_initial_gprs (cpu);
465     }
466 
467   /* Default to a 8 Mbyte (== 2^23) memory space.  */
468   sim_do_commandf (sd, "memory-size 0x800000");
469 
470   return sd;
471 }
472 
473 SIM_RC
474 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
475 		     char * const *argv, char * const *env)
476 {
477   SIM_CPU *cpu = STATE_CPU (sd, 0);
478 
479   PC = bfd_get_start_address (prog_bfd);
480 
481   return SIM_RC_OK;
482 }
483