--- trunk/doc/technical.html 2007/10/08 16:18:27 10 +++ trunk/doc/technical.html 2007/10/08 16:22:32 42 @@ -1,18 +1,18 @@ -GXemul documentation: Technical details +Gavare's eXperimental Emulator:   Technical details
-GXemul documentation:        +Gavare's eXperimental Emulator:
Technical details

+ Back to the index


@@ -52,7 +53,6 @@

  • Speed and emulation modes
  • Networking
  • Emulation of hardware devices -
  • Regression tests @@ -64,18 +64,20 @@

    Speed and emulation modes

    -So, how fast is GXemul? There is no good answer to this. There is +So, how fast is GXemul? There is no short answer to this. There is especially no answer to the question What is the slowdown factor?, because the host architecture and emulated architecture can usually not be compared just like that.

    Performance depends on several factors, including (but not limited to) -host architecture, host clock speed, which compiler and compiler flags -were used to build the emulator, what the workload is, and so on. For -example, if an emulated operating system tries to read a block from disk, -from its point of view the read was instantaneous (no waiting). So 1 MIPS -in an emulated OS might have taken more than one million instructions on a -real machine. +host architecture, target architecture, host clock speed, which compiler +and compiler flags were used to build the emulator, what the workload is, +what additional runtime flags are given to the emulator, and so on. + +

    Devices are generally not timing-accurate: for example, if an emulated +operating system tries to read a block from disk, from its point of view +the read was instantaneous (no waiting). So 1 MIPS in an emulated OS might +have taken more than one million instructions on a real machine.

    Also, if the emulator says it has executed 1 million instructions, and the CPU family in question was capable of scalar execution (i.e. one cycle @@ -85,48 +87,16 @@

    Because of these issues, it is in my opinion best to measure performance as the actual (real-world) time it takes to perform a task -with the emulator. Typical examples would be "How long does it take to -install NetBSD?", or "How long does it take to compile XYZ inside NetBSD -in the emulator?". - -

    The emulation technique used varies depending on which processor type -is being emulated. (One of my main goals with GXemul is to experiment with -different kinds of emulation, so these might change in the future.) +with the emulator, e.g.:

    +

    So, how fast is it? :-)   Answer: it varies. + @@ -137,8 +107,7 @@

    Networking

    -NOTE/TODO: This section is very old and a bit -out of date. +NOTE/TODO: This section is very old.

    Running an entire operating system under emulation is very interesting in itself, but for several reasons, running a modern OS without access to @@ -330,9 +299,6 @@ files in both directions, but then you should be aware of the fragmentation issue mentioned above. -

    TODO: Write a section on how to connect multiple emulator instances. -(Using the local_port and add_remote configuration file -commands.) @@ -343,14 +309,13 @@

    Emulation of hardware devices

    -Each file in the device/ directory is responsible for one -hardware device. These are used from src/machine.c, when -initializing which hardware a particular machine model will be using, or -when adding devices to a machine using the device() command in -configuration files. - -

    NOTE: The device registry subsystem is currently -in a state of flux, as it is being redesigned. +Each file called dev_*.c in the +src/devices/ directory is +responsible for one hardware device. These are used from +src/machines/machine_*.c, +when initializing which hardware a particular machine model will be using, +or when adding devices to a machine using the device() command in +configuration files.

    (I'll be using the name "foo" as the name of the device in all these examples. This is pseudo code, it might need some modification to @@ -363,30 +328,26 @@

  • A devinit function in src/devices/dev_foo.c. It would typically look something like this:
    -	/*
    -	 *  devinit_foo():
    -	 */
    -	int devinit_foo(struct devinit *devinit)
    +	DEVINIT(foo)
     	{
    -	        struct foo_data *d = malloc(sizeof(struct foo_data));
    +	        struct foo_data *d;
     
    -	        if (d == NULL) {
    -	                fprintf(stderr, "out of memory\n");
    -	                exit(1);
    -	        }
    -	        memset(d, 0, sizeof(struct foon_data));
    +		CHECK_ALLOCATION(d = malloc(sizeof(struct foo_data)));
    +	        memset(d, 0, sizeof(struct foo_data));
     
     		/*
     		 *  Set up stuff here, for example fill d with useful
    -		 *  data. devinit contains settings like address, irq_nr,
    +		 *  data. devinit contains settings like address, irq path,
     		 *  and other things.
     		 *
     		 *  ...
     		 */
    +
    +		INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
             
     	        memory_device_register(devinit->machine->memory, devinit->name,
     	            devinit->addr, DEV_FOO_LENGTH,
    -	            dev_foo_access, (void *)d, MEM_DEFAULT, NULL);
    +	            dev_foo_access, (void *)d, DM_DEFAULT, NULL);
             
     		/*  This should only be here if the device
     		    has a tick function:  */
    @@ -398,46 +359,76 @@
     	}       
     

    +

    DEVINIT(foo) is defined as int devinit_foo(struct devinit *devinit), + and the devinit argument contains everything that the device driver's + initialization function needs. + +

  • At the top of dev_foo.c, the foo_data struct should be defined.
     	struct foo_data {
    -		int	irq_nr;
    +		struct interrupt	irq;
     		/*  ...  */
     	}
     

    - + (There is an exception to this rule; some legacy code and other + ugly hacks have their device structs defined in + src/include/devices.h instead of dev_foo.c. + New code should not add stuff to devices.h.) +

  • If foo has a tick function (that is, something that needs to be run at regular intervals) then FOO_TICKSHIFT and a tick function need to be defined as well:
    -	#define FOO_TICKSHIFT		10
    +	#define FOO_TICKSHIFT		14
     
    -	void dev_foo_tick(struct cpu *cpu, void *extra)
    +	DEVICE_TICK(foo)
     	{
    -		struct foo_data *d = (struct foo_data *) extra;
    +		struct foo_data *d = extra;
     
     		if (.....)
    -			cpu_interrupt(cpu, d->irq_nr);
    +			INTERRUPT_ASSERT(d->irq);
     		else
    -			cpu_interrupt_ack(cpu, d->irq_nr);
    +			INTERRUPT_DEASSERT(d->irq);
     	}
     

    +
  • Does this device belong to a standard bus? + +

  • And last but not least, the device should have an access function. The access function is called whenever there is a load or store - to an address which is in the device' memory mapped region. -
    -	int dev_foo_access(struct cpu *cpu, struct memory *mem,
    +	to an address which is in the device' memory mapped region. To
    +	simplify things a little, a macro DEVICE_ACCESS(x)
    +	is expanded into
    +	int dev_x_access(struct cpu *cpu, struct memory *mem,
     	    uint64_t relative_addr, unsigned char *data, size_t len,
     	    int writeflag, void *extra)
    +
    The access function can look like this: +
    +	DEVICE_ACCESS(foo)
     	{
     		struct foo_data *d = extra;
     		uint64_t idata = 0, odata = 0;
     
    -		idata = memory_readmax64(cpu, data, len);
    +		if (writeflag == MEM_WRITE)
    +			idata = memory_readmax64(cpu, data, len);
    +
     		switch (relative_addr) {
    -		/* .... */
    +
    +		/*  Handle accesses to individual addresses within
    +		    the device here.  */
    +
    +		/*  ...  */
    +
     		}
     
     		if (writeflag == MEM_READ)
    @@ -474,76 +465,6 @@
     
     
     
    -


    - -

    Regression tests

    - -In order to make sure that the emulator actually works like it is supposed -to, it must be tested. For this purpose, there is a simple regression -testing framework in the tests/ directory. - -

    -NOTE: The regression testing framework is basically just a skeleton so far. -Regression tests are very good to have. However, the fact that complete -operating systems can run in the emulator indicate that the emulation is -probably not too incorrect. This makes it less of a priority to write -regression tests. - -

    -To run all the regression tests, type make regtest. Each assembly -language file matching the pattern test_*.S will be compiled and -linked into a 64-bit MIPS ELF (using a gcc cross compiler), and run in the -emulator. If everything goes well, you should see something like this: - -

    -	$ make regtest
    -	cd tests; make run_tests; cd ..
    -	gcc33 -Wall -fomit-frame-pointer -fmove-all-movables -fpeephole -O2 
    -		-mcpu=ev5 -I/usr/X11R6/include -lm -L/usr/X11R6/lib -lX11  do_tests.c
    -		-o do_tests
    -	do_tests.c: In function `main':
    -	do_tests.c:173: warning: unused variable `s'
    -	/var/tmp//ccFOupvD.o: In function `do_tests':
    -	/var/tmp//ccFOupvD.o(.text+0x3a8): warning: tmpnam() possibly used
    -		unsafely; consider using mkstemp()
    -	mips64-unknown-elf-gcc -g -O3 -fno-builtin -fschedule-insns -mips64 
    -		-mabi=64 test_common.c -c -o test_common.o
    -	./do_tests "mips64-unknown-elf-gcc -g -O3 -fno-builtin -fschedule-insns 
    -		-mips64 -mabi=64" "mips64-unknown-elf-as -mabi=64 -mips64" 
    -		"mips64-unknown-elf-ld -Ttext 0xa800000000030000 -e main 
    -		--oformat=elf64-bigmips" "../gxemul"
    -
    -	Starting tests:
    -	  test_addu.S (-a)
    -	  test_addu.S (-a -b)
    -	  test_clo_clz.S (-a)
    -	  test_clo_clz.S (-a -b)
    -	  ..
    -	  test_unaligned.S (-a)
    -	  test_unaligned.S (-a -b)
    -
    -	Done. (12 tests done)
    -	    PASS:     12
    -	    FAIL:      0
    -
    -	----------------
    -
    -	  All tests OK
    -
    -	----------------
    -
    - -

    -Each test writes output to stdout, and there is a test_*.good for -each .S file which contains the wanted output. If the actual -output matches the .good file, then the test passes, otherwise it -fails. - -

    -Read tests/README for more information. - - -