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