xref: /netbsd-src/external/gpl3/gdb/dist/sim/microblaze/interp.c (revision 9fd8799cb5ceb66c69f2eb1a6d26a1d587ba1f1e)
1 /* Simulator for Xilinx MicroBlaze processor
2    Copyright 2009-2020 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 #include "config.h"
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include "bfd.h"
25 #include "gdb/callback.h"
26 #include "libiberty.h"
27 #include "gdb/remote-sim.h"
28 
29 #include "sim-main.h"
30 #include "sim-options.h"
31 
32 #include "microblaze-dis.h"
33 
34 #define target_big_endian (CURRENT_TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
35 
36 static unsigned long
37 microblaze_extract_unsigned_integer (unsigned char *addr, int len)
38 {
39   unsigned long retval;
40   unsigned char *p;
41   unsigned char *startaddr = (unsigned char *)addr;
42   unsigned char *endaddr = startaddr + len;
43 
44   if (len > (int) sizeof (unsigned long))
45     printf ("That operation is not available on integers of more than "
46 	    "%zu bytes.", sizeof (unsigned long));
47 
48   /* Start at the most significant end of the integer, and work towards
49      the least significant.  */
50   retval = 0;
51 
52   if (!target_big_endian)
53     {
54       for (p = endaddr; p > startaddr;)
55 	retval = (retval << 8) | * -- p;
56     }
57   else
58     {
59       for (p = startaddr; p < endaddr;)
60 	retval = (retval << 8) | * p ++;
61     }
62 
63   return retval;
64 }
65 
66 static void
67 microblaze_store_unsigned_integer (unsigned char *addr, int len,
68 				   unsigned long val)
69 {
70   unsigned char *p;
71   unsigned char *startaddr = (unsigned char *)addr;
72   unsigned char *endaddr = startaddr + len;
73 
74   if (!target_big_endian)
75     {
76       for (p = startaddr; p < endaddr;)
77 	{
78 	  *p++ = val & 0xff;
79 	  val >>= 8;
80 	}
81     }
82   else
83     {
84       for (p = endaddr; p > startaddr;)
85 	{
86 	  *--p = val & 0xff;
87 	  val >>= 8;
88 	}
89     }
90 }
91 
92 static void
93 set_initial_gprs (SIM_CPU *cpu)
94 {
95   int i;
96   long space;
97 
98   /* Set up machine just out of reset.  */
99   PC = 0;
100   MSR = 0;
101 
102   /* Clean out the GPRs */
103   for (i = 0; i < 32; i++)
104     CPU.regs[i] = 0;
105   CPU.insts = 0;
106   CPU.cycles = 0;
107   CPU.imm_enable = 0;
108 }
109 
110 static int tracing = 0;
111 
112 void
113 sim_engine_run (SIM_DESC sd,
114 		int next_cpu_nr, /* ignore  */
115 		int nr_cpus, /* ignore  */
116 		int siggnal) /* ignore  */
117 {
118   SIM_CPU *cpu = STATE_CPU (sd, 0);
119   int needfetch;
120   word inst;
121   enum microblaze_instr op;
122   int memops;
123   int bonus_cycles;
124   int insts;
125   int w;
126   int cycs;
127   word WLhash;
128   ubyte carry;
129   int imm_unsigned;
130   short ra, rb, rd;
131   long immword;
132   uword oldpc, newpc;
133   short delay_slot_enable;
134   short branch_taken;
135   short num_delay_slot; /* UNUSED except as reqd parameter */
136   enum microblaze_instr_type insn_type;
137 
138   memops = 0;
139   bonus_cycles = 0;
140   insts = 0;
141 
142   while (1)
143     {
144       /* Fetch the initial instructions that we'll decode. */
145       inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
146 
147       op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
148 				&num_delay_slot);
149 
150       if (op == invalid_inst)
151 	fprintf (stderr, "Unknown instruction 0x%04x", inst);
152 
153       if (tracing)
154 	fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
155 
156       rd = GET_RD;
157       rb = GET_RB;
158       ra = GET_RA;
159       /*      immword = IMM_W; */
160 
161       oldpc = PC;
162       delay_slot_enable = 0;
163       branch_taken = 0;
164       if (op == microblaze_brk)
165 	sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
166       else if (inst == MICROBLAZE_HALT_INST)
167 	{
168 	  insts += 1;
169 	  bonus_cycles++;
170 	  sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG);
171 	}
172       else
173 	{
174 	  switch(op)
175 	    {
176 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION)		\
177 	    case 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 		/* no delay slot: increment cycle count */
288 		bonus_cycles++;
289 	    }
290 	}
291 
292       if (tracing)
293 	fprintf (stderr, "\n");
294 
295       if (sim_events_tick (sd))
296 	sim_events_process (sd);
297     }
298 
299   /* Hide away the things we've cached while executing.  */
300   /*  CPU.pc = pc; */
301   CPU.insts += insts;		/* instructions done ... */
302   CPU.cycles += insts;		/* and each takes a cycle */
303   CPU.cycles += bonus_cycles;	/* and extra cycles for branches */
304   CPU.cycles += memops; 	/* and memop cycle delays */
305 }
306 
307 static int
308 microblaze_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
309 {
310   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
311     {
312       if (length == 4)
313 	{
314 	  /* misalignment safe */
315 	  long ival = microblaze_extract_unsigned_integer (memory, 4);
316 	  if (rn < NUM_REGS)
317 	    CPU.regs[rn] = ival;
318 	  else
319 	    CPU.spregs[rn-NUM_REGS] = ival;
320 	  return 4;
321 	}
322       else
323 	return 0;
324     }
325   else
326     return 0;
327 }
328 
329 static int
330 microblaze_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
331 {
332   long ival;
333 
334   if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
335     {
336       if (length == 4)
337 	{
338 	  if (rn < NUM_REGS)
339 	    ival = CPU.regs[rn];
340 	  else
341 	    ival = CPU.spregs[rn-NUM_REGS];
342 
343 	  /* misalignment-safe */
344 	  microblaze_store_unsigned_integer (memory, 4, ival);
345 	  return 4;
346 	}
347       else
348 	return 0;
349     }
350   else
351     return 0;
352 }
353 
354 void
355 sim_info (SIM_DESC sd, int verbose)
356 {
357   SIM_CPU *cpu = STATE_CPU (sd, 0);
358   host_callback *callback = STATE_CALLBACK (sd);
359 
360   callback->printf_filtered (callback, "\n\n# instructions executed  %10d\n",
361 			     CPU.insts);
362   callback->printf_filtered (callback, "# cycles                 %10d\n",
363 			     (CPU.cycles) ? CPU.cycles+2 : 0);
364 }
365 
366 static sim_cia
367 microblaze_pc_get (sim_cpu *cpu)
368 {
369   return cpu->microblaze_cpu.spregs[0];
370 }
371 
372 static void
373 microblaze_pc_set (sim_cpu *cpu, sim_cia pc)
374 {
375   cpu->microblaze_cpu.spregs[0] = pc;
376 }
377 
378 static void
379 free_state (SIM_DESC sd)
380 {
381   if (STATE_MODULES (sd) != NULL)
382     sim_module_uninstall (sd);
383   sim_cpu_free_all (sd);
384   sim_state_free (sd);
385 }
386 
387 SIM_DESC
388 sim_open (SIM_OPEN_KIND kind, host_callback *cb,
389 	  struct bfd *abfd, char * const *argv)
390 {
391   int i;
392   SIM_DESC sd = sim_state_alloc (kind, cb);
393   SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
394 
395   /* The cpu data is kept in a separately allocated chunk of memory.  */
396   if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
397     {
398       free_state (sd);
399       return 0;
400     }
401 
402   if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
403     {
404       free_state (sd);
405       return 0;
406     }
407 
408   /* The parser will print an error message for us, so we silently return.  */
409   if (sim_parse_args (sd, argv) != SIM_RC_OK)
410     {
411       free_state (sd);
412       return 0;
413     }
414 
415   /* Check for/establish the a reference program image.  */
416   if (sim_analyze_program (sd,
417 			   (STATE_PROG_ARGV (sd) != NULL
418 			    ? *STATE_PROG_ARGV (sd)
419 			    : NULL), abfd) != SIM_RC_OK)
420     {
421       free_state (sd);
422       return 0;
423     }
424 
425   /* Configure/verify the target byte order and other runtime
426      configuration options.  */
427   if (sim_config (sd) != SIM_RC_OK)
428     {
429       sim_module_uninstall (sd);
430       return 0;
431     }
432 
433   if (sim_post_argv_init (sd) != SIM_RC_OK)
434     {
435       /* Uninstall the modules to avoid memory leaks,
436 	 file descriptor leaks, etc.  */
437       sim_module_uninstall (sd);
438       return 0;
439     }
440 
441   /* CPU specific initialization.  */
442   for (i = 0; i < MAX_NR_PROCESSORS; ++i)
443     {
444       SIM_CPU *cpu = STATE_CPU (sd, i);
445 
446       CPU_REG_FETCH (cpu) = microblaze_reg_fetch;
447       CPU_REG_STORE (cpu) = microblaze_reg_store;
448       CPU_PC_FETCH (cpu) = microblaze_pc_get;
449       CPU_PC_STORE (cpu) = microblaze_pc_set;
450 
451       set_initial_gprs (cpu);
452     }
453 
454   /* Default to a 8 Mbyte (== 2^23) memory space.  */
455   sim_do_commandf (sd, "memory-size 0x800000");
456 
457   return sd;
458 }
459 
460 SIM_RC
461 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
462 		     char * const *argv, char * const *env)
463 {
464   SIM_CPU *cpu = STATE_CPU (sd, 0);
465 
466   PC = bfd_get_start_address (prog_bfd);
467 
468   return SIM_RC_OK;
469 }
470