From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9794 invoked by alias); 12 Apr 2010 14:56:46 -0000 Received: (qmail 9313 invoked by uid 22791); 12 Apr 2010 14:56:42 -0000 X-SWARE-Spam-Status: No, hits=0.8 required=5.0 tests=BAYES_50 X-Spam-Check-By: sourceware.org Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 12 Apr 2010 14:56:35 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id CF0602BAB6D; Mon, 12 Apr 2010 10:56:33 -0400 (EDT) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id rS6Gj64IIk91; Mon, 12 Apr 2010 10:56:33 -0400 (EDT) Received: from joel.gnat.com (localhost.localdomain [127.0.0.1]) by rock.gnat.com (Postfix) with ESMTP id 61A5D2BAB5A; Mon, 12 Apr 2010 10:56:33 -0400 (EDT) Received: by joel.gnat.com (Postfix, from userid 1000) id 25D08F58C2; Mon, 12 Apr 2010 07:56:19 -0700 (PDT) Date: Mon, 12 Apr 2010 14:56:00 -0000 From: Joel Brobecker To: Mike Frysinger , eliz@gnu.org Cc: gdb-patches@sourceware.org Subject: Re: [PATCH] sim: add more hacking notes Message-ID: <20100412145619.GT19194@adacore.com> References: <1270925910-23834-1-git-send-email-vapier@gentoo.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1270925910-23834-1-git-send-email-vapier@gentoo.org> User-Agent: Mutt/1.5.20 (2009-06-14) Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-04/txt/msg00356.txt.bz2 > 2010-04-10 Mike Frysinger > > * README-HACKING: Add more sections. > This one might be more in Eli's department than anyone else. If not, I'll be happy to review.... > 1 files changed, 245 insertions(+), 0 deletions(-) > > diff --git a/sim/README-HACKING b/sim/README-HACKING > index bee504a..5219b4e 100644 > --- a/sim/README-HACKING > +++ b/sim/README-HACKING > @@ -256,4 +256,249 @@ To add a new target: > devo/sim//*.[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 = ; > + sc.arg1 = ; > + sc.arg2 = ; > + sc.arg3 = ; > + sc.arg4 = ; > + sc.p1 = (PTR) sd; > + sc.p2 = (PTR) cpu; > + sc.read_mem = syscall_read_mem; > + sc.write_mem = syscall_write_mem; > + > + cb_syscall (cb, &sc); > + > + ; > + ; > +} > +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 > +a 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 are 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 > -- > 1.7.0.2 -- Joel