1 This is a loose collection of notes for people hacking on simulators.
2 If this document gets big enough it can be prettied up then.
3
4 Contents
5
6 - The "common" directory
7 - Common Makefile Support
8 - TAGS support
9 - Generating "configure" files
10 - C Language Assumptions
11 - "dump" commands under gdb
12
14 The "common" directory
15 ======================
16
17 The common directory contains:
18
19 - common documentation files (e.g. run.1, and maybe in time .texi files)
20 - common source files (e.g. run.c)
21 - common Makefile fragment and configury (e.g. common/local.mk)
22
23 In addition "common" contains portions of the system call support
24 (e.g. callback.c, target-newlib-*.c).
25
27 TAGS support
28 ============
29
30 Many files generate program symbols at compile time.
31 Such symbols can't be found with grep nor do they normally appear in
32 the TAGS file. To get around this, source files can add the comment
33
34 /* TAGS: foo1 foo2 */
35
36 where foo1, foo2 are program symbols. Symbols found in such comments
37 are greppable and appear in the TAGS file.
38
40 Generating "configure" files
41 ============================
42
43 "configure" can be generated by running `autoreconf'.
44
46 C Language Assumptions
47 ======================
48
49 An ISO C11 compiler is required, as is an ISO C standard library.
50
52 "dump" commands under gdb
53 =========================
54
55 gdbinit.in contains the following
56
57 define dump
58 set sim_debug_dump ()
59 end
60
61 Simulators that define the sim_debug_dump function can then have their
62 internal state pretty printed from gdb.
63
64 FIXME: This can obviously be made more elaborate. As needed it will be.
65
67 Rebuilding target-newlib-* files
68 ================================
69
70 Checkout a copy of the SIM and LIBGLOSS modules (Unless you've already
71 got one to hand):
72
73 $ mkdir /tmp/$$
74 $ cd /tmp/$$
75 $ cvs checkout sim-no-testsuite libgloss-no-testsuite newlib-no-testsuite
76
77 Configure things for an arbitrary simulator target (d10v is used here for
78 convenience):
79
80 $ mkdir /tmp/$$/build
81 $ cd /tmp/$$/build
82 $ /tmp/$$/devo/configure --target=d10v-elf
83
84 In the sim/ directory rebuild the headers:
85
86 $ cd sim/
87 $ make nltvals
88
89 If the target uses the common syscall table (libgloss/syscall.h), then you're
90 all set! If the target has a custom syscall table, you need to declare it:
91
92 devo/sim/common/gennltvals.py
93
94 Add your new processor target (you'll need to grub
95 around to find where your syscall.h lives).
96
97 devo/sim/<processor>/*.[ch]
98
99 Include target-newlib-syscall.h instead of syscall.h.
100
102 Tracing
103 =======
104
105 For ports based on CGEN, tracing instrumentation should largely be for free,
106 so we will cover the basic non-CGEN setup here. The assumption is that your
107 target is using the common autoconf macros and so the build system already
108 includes the sim-trace configure flag.
109
110 The full tracing API is covered in sim-trace.h, so this section is an overview.
111
112 Before calling any trace function, you should make a call to the trace_prefix()
113 function. This is usually done in the main sim_engine_run() loop before
114 simulating the next instruction. You should make this call before every
115 simulated insn. You can probably copy & paste this:
116 if (TRACE_ANY_P (cpu))
117 trace_prefix (sd, cpu, NULL_CIA, oldpc, TRACE_LINENUM_P (cpu), NULL, 0, "");
118
119 You will then need to instrument your simulator code with calls to the
120 trace_generic() function with the appropriate trace index. Typically, this
121 will take a form similar to the above snippet. So to trace instructions, you
122 would use something like:
123 if (TRACE_INSN_P (cpu))
124 trace_generic (sd, cpu, TRACE_INSN_IDX, "NOP;");
125
126 The exact output format is up to you. See the trace index enum in sim-trace.h
127 to see the different tracing info available.
128
129 To utilize the tracing features at runtime, simply use the --trace-xxx flags.
130 run --trace-insn ./some-program
131
133 Profiling
134 =========
135
136 Similar to the tracing section, this is merely an overview for non-CGEN based
137 ports. The full API may be found in sim-profile.h. Its API is also similar
138 to the tracing API.
139
140 Note that unlike the tracing command line options, in addition to the profile
141 flags, you have to use the --verbose option to view the summary report after
142 execution. Tracing output is displayed on the fly, but the profile output is
143 only summarized.
144
145 To profile core accesses (such as data reads/writes and insn fetches), add
146 calls to PROFILE_COUNT_CORE() to your read/write functions. So in your data
147 fetch function, you'd use something like:
148 PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_read);
149 Then in your data write function:
150 PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_write);
151 And in your insn fetcher:
152 PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_exec);
153
154 To use the PC profiling code, you simply have to tell the system where to find
155 your simulator's PC. So in your model initialization function:
156 CPU_PC_FETCH (cpu) = function_that_fetches_the_pc;
157
158 To profile branches, in every location where a branch insn is executed, call
159 one of the related helpers:
160 PROFILE_BRANCH_TAKEN (cpu);
161 PROFILE_BRANCH_UNTAKEN (cpu);
162 If you have stall information, you can utilize the other helpers too.
163
165 Environment Simulation
166 ======================
167
168 The simplest simulator doesn't include environment support -- it merely
169 simulates the Instruction Set Architecture (ISA). Once you're ready to move
170 on to the next level, it's time to start handling the --env option. It's
171 enabled by default for all ports already.
172
173 This will support for the user, virtual, and operating environments. See the
174 sim-config.h header for a more detailed description of them. The former are
175 pretty straight forward as things like exceptions (making system calls) are
176 handled in the simulator. Which is to say, an exception does not trigger an
177 exception handler in the simulator target -- that is what the operating env
178 is about. See the following userspace section for more information.
179
181 Userspace System Calls
182 ======================
183
184 By default, the libgloss userspace is simulated. That means the system call
185 numbers and calling convention matches that of libgloss. Simulating other
186 userspaces (such as Linux) is pretty straightforward, but let's first focus
187 on the basics. The basic API is covered in include/sim/callback.h.
188
189 When an instruction is simulated that invokes the system call method (such as
190 forcing a hardware trap or exception), your simulator code should set up the
191 CB_SYSCALL data structure before calling the common cb_syscall() function.
192 For example:
193 static int
194 syscall_read_mem (host_callback *cb, struct cb_syscall *sc,
195 unsigned long taddr, char *buf, int bytes)
196 {
197 SIM_DESC sd = (SIM_DESC) sc->p1;
198 SIM_CPU *cpu = (SIM_CPU *) sc->p2;
199 return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes);
200 }
201 static int
202 syscall_write_mem (host_callback *cb, struct cb_syscall *sc,
203 unsigned long taddr, const char *buf, int bytes)
204 {
205 SIM_DESC sd = (SIM_DESC) sc->p1;
206 SIM_CPU *cpu = (SIM_CPU *) sc->p2;
207 return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes);
208 }
209 void target_sim_syscall (SIM_CPU *cpu)
210 {
211 SIM_DESC sd = CPU_STATE (cpu);
212 host_callback *cb = STATE_CALLBACK (sd);
213 CB_SYSCALL sc;
214
215 CB_SYSCALL_INIT (&sc);
216
217 sc.func = <fetch system call number>;
218 sc.arg1 = <fetch first system call argument>;
219 sc.arg2 = <fetch second system call argument>;
220 sc.arg3 = <fetch third system call argument>;
221 sc.arg4 = <fetch fourth system call argument>;
222 sc.p1 = (PTR) sd;
223 sc.p2 = (PTR) cpu;
224 sc.read_mem = syscall_read_mem;
225 sc.write_mem = syscall_write_mem;
226
227 cb_syscall (cb, &sc);
228
229 <store system call result from sc.result>;
230 <store system call error from sc.errcode>;
231 }
232 Some targets store the result and error code in different places, while others
233 only store the error code when the result is an error.
234
235 Keep in mind that the CB_SYS_xxx defines are normalized values with no real
236 meaning with respect to the target. They provide a unique map on the host so
237 that it can parse things sanely. For libgloss, the common/target-newlib-syscall
238 file contains the target's system call numbers to the CB_SYS_xxx values.
239
240 To simulate other userspace targets, you really only need to update the maps
241 pointers that are part of the callback interface. So create CB_TARGET_DEFS_MAP
242 arrays for each set (system calls, errnos, open bits, etc...) and in a place
243 you find useful, do something like:
244
245 ...
246 static CB_TARGET_DEFS_MAP cb_linux_syscall_map[] = {
247 # define TARGET_LINUX_SYS_open 5
248 { CB_SYS_open, TARGET_LINUX_SYS_open },
249 ...
250 { -1, -1 },
251 };
252 ...
253 host_callback *cb = STATE_CALLBACK (sd);
254 cb->syscall_map = cb_linux_syscall_map;
255 cb->errno_map = cb_linux_errno_map;
256 cb->open_map = cb_linux_open_map;
257 cb->signal_map = cb_linux_signal_map;
258 cb->stat_map = cb_linux_stat_map;
259 ...
260
261 Each of these cb_linux_*_map's are manually declared by the arch target.
262
263 The target_sim_syscall() example above will then work unchanged (ignoring the
264 system call convention) because all of the callback functions go through these
265 mapping arrays.
266
268 Events
269 ======
270
271 Events are scheduled and executed on behalf of either a cpu or hardware devices.
272 The API is pretty much the same and can be found in common/sim-events.h and
273 common/hw-events.h.
274
275 For simulator targets, you really just have to worry about the schedule and
276 deschedule functions.
277
279 Device Trees
280 ============
281
282 The device tree model is based on the OpenBoot specification. Since this is
283 largely inherited from the psim code, consult the existing psim documentation
284 for some in-depth details.
285 http://sourceware.org/psim/manual/
286
288 Hardware Devices
289 ================
290
291 The simplest simulator doesn't include hardware device support. Once you're
292 ready to move on to the next level, declare in your Makefile.in:
293 SIM_EXTRA_HW_DEVICES = devone devtwo devthree
294
295 The basic hardware API is documented in common/hw-device.h.
296
297 Each device has to have a matching file name with a "dv-" prefix. So there has
298 to be a dv-devone.c, dv-devtwo.c, and dv-devthree.c files. Further, each file
299 has to have a matching hw_descriptor structure. So the dv-devone.c file has to
300 have something like:
301 const struct hw_descriptor dv_devone_descriptor[] = {
302 {"devone", devone_finish,},
303 {NULL, NULL},
304 };
305
306 The "devone" string as well as the "devone_finish" function are not hard
307 requirements, just common conventions. The structure name is a hard
308 requirement.
309
310 The devone_finish() callback function is used to instantiate this device by
311 parsing the corresponding properties in the device tree.
312
313 Hardware devices typically attach address ranges to themselves. Then when
314 accesses to those addresses are made, the hardware will have its callback
315 invoked. The exact callback could be a normal I/O read/write access, as
316 well as a DMA access. This makes it easy to simulate memory mapped registers.
317
318 Keep in mind that like a proper device driver, it may be instantiated many
319 times over. So any device state it needs to be maintained should be allocated
320 during the finish callback and attached to the hardware device via set_hw_data.
321 Any hardware functions can access this private data via the hw_data function.
322
324 Ports (Interrupts / IRQs)
325 =========================
326
327 First, a note on terminology. A "port" is an aspect of a hardware device that
328 accepts or generates interrupts. So devices with input ports may be the target
329 of an interrupt (accept it), and/or they have output ports so that they may be
330 the source of an interrupt (generate it).
331
332 Each port has a symbolic name and a unique number. These are used to identify
333 the port in different contexts. The output port name has no hard relationship
334 to the input port name (same for the unique number). The callback that accepts
335 the interrupt uses the name/id of its input port, while the generator function
336 uses the name/id of its output port.
337
338 The device tree is used to connect the output port of a device to the input
339 port of another device. There are no limits on the number of inputs connected
340 to an output, or outputs to an input, or the devices attached to the ports.
341 In other words, the input port and output port could be the same device.
342
343 The basics are:
344 - each hardware device declares an array of ports (hw_port_descriptor).
345 any mix of input and output ports is allowed.
346 - when setting up the device, attach the array (set_hw_ports).
347 - if the device accepts interrupts, it will have to attach a port callback
348 function (set_hw_port_event)
349 - connect ports with the device tree
350 - handle incoming interrupts with the callback
351 - generate outgoing interrupts with hw_port_event
352