123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- This is a loose collection of notes for people hacking on simulators.
- If this document gets big enough it can be prettied up then.
- Contents
- - The "common" directory
- - Common Makefile Support
- - TAGS support
- - Generating "configure" files
- - tconfig.h
- - C Language Assumptions
- - "dump" commands under gdb
- The "common" directory
- ======================
- The common directory contains:
- - common documentation files (e.g. run.1, and maybe in time .texi files)
- - common source files (e.g. run.c)
- - common Makefile fragment and configury (e.g. Make-common.in, aclocal.m4).
- In addition "common" contains portions of the system call support
- (e.g. callback.c, nltvals.def).
- Even though no files are built in this directory, it is still configured
- so support for regenerating nltvals.def is present.
- Common Makefile Support
- =======================
- A common configuration framework is available for simulators that want
- to use it. The common framework exists to remove a lot of duplication
- in configure.ac and Makefile.in, and it also provides a foundation for
- enhancing the simulators uniformly (e.g. the more they share in common
- the easier a feature added to one is added to all).
- The configure.ac of a simulator using the common framework should look like:
- --- snip ---
- dnl Process this file with autoconf to produce a configure script.
- sinclude(../common/aclocal.m4)
- AC_PREREQ(2.5)dnl
- AC_INIT(Makefile.in)
- SIM_AC_COMMON
- ... target specific additions ...
- SIM_AC_OUTPUT
- --- snip ---
- SIM_AC_COMMON:
- - invokes the autoconf macros most often used by the simulators
- - defines --enable/--with options usable by all simulators
- - initializes sim_link_files/sim_link_links as the set of symbolic links
- to set up
- SIM_AC_OUTPUT:
- - creates the symbolic links defined in sim_link_{files,links}
- - creates config.h
- - creates the Makefile
- The Makefile.in of a simulator using the common framework should look like:
- --- snip ---
- # Makefile for blah ...
- # Copyright blah ...
- ## COMMON_PRE_CONFIG_FRAG
- # These variables are given default values in COMMON_PRE_CONFIG_FRAG.
- # We override the ones we need to here.
- # Not all of these need to be mentioned, only the necessary ones.
- # In fact it is better to *not* mention ones if the value is the default.
- # List of object files, less common parts.
- SIM_OBJS =
- # List of extra dependencies.
- # Generally this consists of simulator specific files included by sim-main.h.
- SIM_EXTRA_DEPS =
- # List of flags to always pass to $(CC).
- SIM_EXTRA_CFLAGS =
- # List of extra libraries to link with.
- SIM_EXTRA_LIBS =
- # List of extra program dependencies.
- SIM_EXTRA_LIBDEPS =
- # List of main object files for `run'.
- SIM_RUN_OBJS = run.o
- # Dependency of `all' to build any extra files.
- SIM_EXTRA_ALL =
- # Dependency of `install' to install any extra files.
- SIM_EXTRA_INSTALL =
- # Dependency of `clean' to clean any extra files.
- SIM_EXTRA_CLEAN =
- ## COMMON_POST_CONFIG_FRAG
- # Rules need to build $(SIM_OBJS), plus whatever else the target wants.
- ... target specific rules ...
- --- snip ---
- COMMON_{PRE,POST}_CONFIG_FRAG are markers for SIM_AC_OUTPUT to tell it
- where to insert the two pieces of common/Make-common.in.
- The resulting Makefile is created by doing autoconf substitions on
- both the target's Makefile.in and Make-common.in, and inserting
- the two pieces of Make-common.in into the target's Makefile.in at
- COMMON_{PRE,POST}_CONFIG_FRAG.
- Note that SIM_EXTRA_{INSTALL,CLEAN} could be removed and "::" targets
- could be used instead. However, it's not clear yet whether "::" targets
- are portable enough.
- TAGS support
- ============
- Many files generate program symbols at compile time.
- Such symbols can't be found with grep nor do they normally appear in
- the TAGS file. To get around this, source files can add the comment
- /* TAGS: foo1 foo2 */
- where foo1, foo2 are program symbols. Symbols found in such comments
- are greppable and appear in the TAGS file.
- Generating "configure" files
- ============================
- For targets using the common framework, "configure" can be generated
- by running `autoconf'.
- To regenerate the configure files for all targets using the common framework:
- $ cd devo/sim
- $ make -f Makefile.in SHELL=/bin/sh autoconf-common
- To add a change-log entry to the ChangeLog file for each updated
- directory (WARNING - check the modified new-ChangeLog files before
- renaming):
- $ make -f Makefile.in SHELL=/bin/sh autoconf-changelog
- $ more */new-ChangeLog
- $ make -f Makefile.in SHELL=/bin/sh autoconf-install
- In a similar vein, both the configure and config.in files can be
- updated using the sequence:
- $ cd devo/sim
- $ make -f Makefile.in SHELL=/bin/sh autoheader-common
- $ make -f Makefile.in SHELL=/bin/sh autoheader-changelog
- $ more */new-ChangeLog
- $ make -f Makefile.in SHELL=/bin/sh autoheader-install
- To add the entries to an alternative ChangeLog file, use:
- $ make ChangeLog=MyChangeLog ....
- tconfig.h
- ==========
- File tconfig.h defines one or more target configuration macros
- (e.g. a tm.h file). There are very few that need defining.
- For a list of all of them, see common/tconfig.h.
- It contains them all, commented out.
- The intent is that a new port can just copy this file and
- define the ones it needs.
- C Language Assumptions
- ======================
- The programmer may assume that the simulator is being built using an
- ANSI C compiler that supports a 64 bit data type. Consequently:
- o prototypes can be used
- o If sim-types.h is included, the two
- types signed64 and unsigned64 are
- available.
- o The type `unsigned' is valid.
- However, the user should be aware of the following:
- o GCC's `<number>LL' is NOT acceptable.
- Microsoft-C doesn't reconize it.
- o MSC's `<number>i64' is NOT acceptable.
- GCC doesn't reconize it.
- o GCC's `long long' MSC's `_int64' can
- NOT be used to define 64 bit integer data
- types.
- o An empty array (eg int a[0]) is not valid.
- When building with GCC it is effectivly a requirement that
- --enable-build-warnings=,-Werror be specified during configuration.
- "dump" commands under gdb
- =========================
- gdbinit.in contains the following
- define dump
- set sim_debug_dump ()
- end
- Simulators that define the sim_debug_dump function can then have their
- internal state pretty printed from gdb.
- FIXME: This can obviously be made more elaborate. As needed it will be.
- Rebuilding nltvals.def
- ======================
- Checkout a copy of the SIM and LIBGLOSS modules (Unless you've already
- got one to hand):
- $ mkdir /tmp/$$
- $ cd /tmp/$$
- $ cvs checkout sim-no-testsuite libgloss-no-testsuite newlib-no-testsuite
- Configure things for an arbitrary simulator target (I've d10v for
- convenience):
- $ mkdir /tmp/$$/build
- $ cd /tmp/$$/build
- $ /tmp/$$/devo/configure --target=d10v-elf
- In the sim/common directory rebuild the headers:
- $ cd sim/common
- $ make headers
- To add a new target:
- devo/sim/common/gennltvals.sh
- Add your new processor target (you'll need to grub
- around to find where your syscall.h lives).
- devo/sim/<processor>/Makefile.in
- Add the definition:
- ``NL_TARGET = -DNL_TARGET_d10v''
- just before the line COMMON_POST_CONFIG_FRAG.
- devo/sim/<processor>/*.[ch]
- Include targ-vals.h instead of syscall.h.
- Tracing
- =======
- For ports based on CGEN, tracing instrumentation should largely be for free,
- so we will cover the basic non-CGEN setup here. The assumption is that your
- target is using the common autoconf macros and so the build system already
- includes the sim-trace configure flag.
- The full tracing API is covered in sim-trace.h, so this section is an overview.
- Before calling any trace function, you should make a call to the trace_prefix()
- function. This is usually done in the main sim_engine_run() loop before
- simulating the next instruction. You should make this call before every
- simulated insn. You can probably copy & paste this:
- if (TRACE_ANY_P (cpu))
- trace_prefix (sd, cpu, NULL_CIA, oldpc, TRACE_LINENUM_P (cpu), NULL, 0, "");
- You will then need to instrument your simulator code with calls to the
- trace_generic() function with the appropriate trace index. Typically, this
- will take a form similar to the above snippet. So to trace instructions, you
- would use something like:
- if (TRACE_INSN_P (cpu))
- trace_generic (sd, cpu, TRACE_INSN_IDX, "NOP;");
- The exact output format is up to you. See the trace index enum in sim-trace.h
- to see the different tracing info available.
- To utilize the tracing features at runtime, simply use the --trace-xxx flags.
- run --trace-insn ./some-program
- Profiling
- =========
- Similar to the tracing section, this is merely an overview for non-CGEN based
- ports. The full API may be found in sim-profile.h. Its API is also similar
- to the tracing API.
- Note that unlike the tracing command line options, in addition to the profile
- flags, you have to use the --verbose option to view the summary report after
- execution. Tracing output is displayed on the fly, but the profile output is
- only summarized.
- To profile core accesses (such as data reads/writes and insn fetches), add
- calls to PROFILE_COUNT_CORE() to your read/write functions. So in your data
- fetch function, you'd use something like:
- PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_read);
- Then in your data write function:
- PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_write);
- And in your insn fetcher:
- PROFILE_COUNT_CORE (cpu, target_addr, size_in_bytes, map_exec);
- To use the PC profiling code, you simply have to tell the system where to find
- your simulator's PC and its size. So in your sim_open() function:
- STATE_WATCHPOINTS (sd)->pc = address_of_cpu0_pc;
- STATE_WATCHPOINTS (sd)->sizeof_pc = number_of_bytes_for_pc_storage;
- In a typical 32bit system, the sizeof_pc will be 4 bytes.
- To profile branches, in every location where a branch insn is executed, call
- one of the related helpers:
- PROFILE_BRANCH_TAKEN (cpu);
- PROFILE_BRANCH_UNTAKEN (cpu);
- If you have stall information, you can utilize the other helpers too.
- Environment Simulation
- ======================
- The simplest simulator doesn't include environment support -- it merely
- simulates the Instruction Set Architecture (ISA). Once you're ready to move
- on to the next level, call the common macro in your configure.ac:
- SIM_AC_OPTION_ENVIRONMENT
- This will support for the user, virtual, and operating environments. See the
- sim-config.h header for a more detailed description of them. The former are
- pretty straight forward as things like exceptions (making system calls) are
- handled in the simulator. Which is to say, an exception does not trigger an
- exception handler in the simulator target -- that is what the operating env
- is about. See the following userspace section for more information.
- Userspace System Calls
- ======================
- By default, the libgloss userspace is simulated. That means the system call
- numbers and calling convention matches that of libgloss. Simulating other
- userspaces (such as Linux) is pretty straightforward, but let's first focus
- on the basics. The basic API is covered in include/gdb/callback.h.
- When an instruction is simulated that invokes the system call method (such as
- forcing a hardware trap or exception), your simulator code should set up the
- CB_SYSCALL data structure before calling the common cb_syscall() function.
- For example:
- static int
- syscall_read_mem (host_callback *cb, struct cb_syscall *sc,
- unsigned long taddr, char *buf, int bytes)
- {
- SIM_DESC sd = (SIM_DESC) sc->p1;
- SIM_CPU *cpu = (SIM_CPU *) sc->p2;
- return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes);
- }
- static int
- syscall_write_mem (host_callback *cb, struct cb_syscall *sc,
- unsigned long taddr, const char *buf, int bytes)
- {
- SIM_DESC sd = (SIM_DESC) sc->p1;
- SIM_CPU *cpu = (SIM_CPU *) sc->p2;
- return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes);
- }
- void target_sim_syscall (SIM_CPU *cpu)
- {
- SIM_DESC sd = CPU_STATE (cpu);
- host_callback *cb = STATE_CALLBACK (sd);
- CB_SYSCALL sc;
- CB_SYSCALL_INIT (&sc);
- sc.func = <fetch system call number>;
- sc.arg1 = <fetch first system call argument>;
- sc.arg2 = <fetch second system call argument>;
- sc.arg3 = <fetch third system call argument>;
- sc.arg4 = <fetch fourth system call argument>;
- sc.p1 = (PTR) sd;
- sc.p2 = (PTR) cpu;
- sc.read_mem = syscall_read_mem;
- sc.write_mem = syscall_write_mem;
- cb_syscall (cb, &sc);
- <store system call result from sc.result>;
- <store system call error from sc.errcode>;
- }
- Some targets store the result and error code in different places, while others
- only store the error code when the result is an error.
- Keep in mind that the CB_SYS_xxx defines are normalized values with no real
- meaning with respect to the target. They provide a unique map on the host so
- that it can parse things sanely. For libgloss, the common/nltvals.def file
- creates the target's system call numbers to the CB_SYS_xxx values.
- To simulate other userspace targets, you really only need to update the maps
- pointers that are part of the callback interface. So create CB_TARGET_DEFS_MAP
- arrays for each set (system calls, errnos, open bits, etc...) and in a place
- you find useful, do something like:
- ...
- static CB_TARGET_DEFS_MAP cb_linux_syscall_map[] = {
- # define TARGET_LINUX_SYS_open 5
- { CB_SYS_open, TARGET_LINUX_SYS_open },
- ...
- { -1, -1 },
- };
- ...
- host_callback *cb = STATE_CALLBACK (sd);
- cb->syscall_map = cb_linux_syscall_map;
- cb->errno_map = cb_linux_errno_map;
- cb->open_map = cb_linux_open_map;
- cb->signal_map = cb_linux_signal_map;
- cb->stat_map = cb_linux_stat_map;
- ...
- Each of these cb_linux_*_map's are manually declared by the arch target.
- The target_sim_syscall() example above will then work unchanged (ignoring the
- system call convention) because all of the callback functions go through these
- mapping arrays.
- Events
- ======
- Events are scheduled and executed on behalf of either a cpu or hardware devices.
- The API is pretty much the same and can be found in common/sim-events.h and
- common/hw-events.h.
- For simulator targets, you really just have to worry about the schedule and
- deschedule functions.
- Device Trees
- ============
- The device tree model is based on the OpenBoot specification. Since this is
- largely inherited from the psim code, consult the existing psim documentation
- for some in-depth details.
- http://sourceware.org/psim/manual/
- Hardware Devices
- ================
- The simplest simulator doesn't include hardware device support. Once you're
- ready to move on to the next level, call the common macro in your configure.ac:
- SIM_AC_OPTION_HARDWARE(yes,,devone devtwo devthree)
- The basic hardware API is documented in common/hw-device.h.
- Each device has to have a matching file name with a "dv-" prefix. So there has
- to be a dv-devone.c, dv-devtwo.c, and dv-devthree.c files. Further, each file
- has to have a matching hw_descriptor structure. So the dv-devone.c file has to
- have something like:
- const struct hw_descriptor dv_devone_descriptor[] = {
- {"devone", devone_finish,},
- {NULL, NULL},
- };
- The "devone" string as well as the "devone_finish" function are not hard
- requirements, just common conventions. The structure name is a hard
- requirement.
- The devone_finish() callback function is used to instantiate this device by
- parsing the corresponding properties in the device tree.
- Hardware devices typically attach address ranges to themselves. Then when
- accesses to those addresses are made, the hardware will have its callback
- invoked. The exact callback could be a normal I/O read/write access, as
- well as a DMA access. This makes it easy to simulate memory mapped registers.
- Keep in mind that like a proper device driver, it may be instantiated many
- times over. So any device state it needs to be maintained should be allocated
- during the finish callback and attached to the hardware device via set_hw_data.
- Any hardware functions can access this private data via the hw_data function.
- Ports (Interrupts / IRQs)
- =========================
- First, a note on terminology. A "port" is an aspect of a hardware device that
- accepts or generates interrupts. So devices with input ports may be the target
- of an interrupt (accept it), and/or they have output ports so that they may be
- the source of an interrupt (generate it).
- Each port has a symbolic name and a unique number. These are used to identify
- the port in different contexts. The output port name has no hard relationship
- to the input port name (same for the unique number). The callback that accepts
- the interrupt uses the name/id of its input port, while the generator function
- uses the name/id of its output port.
- The device tree is used to connect the output port of a device to the input
- port of another device. There are no limits on the number of inputs connected
- to an output, or outputs to an input, or the devices attached to the ports.
- In other words, the input port and output port could be the same device.
- The basics are:
- - each hardware device declares an array of ports (hw_port_descriptor).
- any mix of input and output ports is allowed.
- - when setting up the device, attach the array (set_hw_ports).
- - if the device accepts interrupts, it will have to attach a port callback
- function (set_hw_port_event)
- - connect ports with the device tree
- - handle incoming interrupts with the callback
- - generate outgoing interrupts with hw_port_event
|