Emulation files

Essentially, the emulation can be created from scratch either as a C# project or assembled by hand using the Monitor. These ways use the Emul8 API directly.

A simpler and recommended way is to use helper files, found in the scripts/ and platforms/ directories:

  • JSON platform configuration files
  • batch scripts building the emulation using the same commands as would normally be issued from the Monitor

These two formats will be briefly described below.

JSON Platform configuration files

Platforms are described using the JSON format in ordinary text files. Available platform configurations can be found in the platforms/ directory and are used to build up an emulated platform from appropriate models. JSON as a format has the advantage of being human-readable and easy to parse. Some minor deviation from strict JSON are that:

  • multiline comments are allowed in the format of /* Comment text */
  • a comma is allowed after the last element of a list or array
  • hexadecimal numbers are allowed
{
  "foo1":["bar", "baz",], /* Comment
                                 text */
  "foo2":
  {
    "foo3": 0xabcdef
  },
}

Reserved keywords have the “_” character as a suffix. Currently existing keywords are:

  • "_type" - Peripheral class name
  • "_connection" - Connections to other peripheral objects
  • "_irq" or "_gpio" - GPIO outgoing connections
  • "_irqFrom" or "_gpioFrom" - GPIO incoming connections

A configuration file consists of a list of nodes. The root node is called sysbus and is always present and does not have to be described in the configuration file. Under the root node are first level nodes that describe peripheral objects. A peripheral object node can have subnodes that further describe properties of the peripheral object or its connection to other peripheral objects.

{
  "Peripheral_object_A": /* Level 1 node */
  {
    "property1":"value", /* Object property */
    "property2":         /* Object property */
    {
    }
  },
  "Peripheral_object_B": /* Level 1 node */
  {
    "property1":"value", /* Object property */
    "_connection":         /* Object property */
    {
    }
  }
}

Each peripheral object must have a defined name and type. The name of the object will be the string that is visible when running the command peripherals. Type is directly related to a peripheral’s class.

This example is a UART peripheral based on the PL011 peripheral class.

{
  "uart0":
  {
    "_type":"UART.PL011",
  }
}

This example is a Memory peripheral based on the Memory peripheral class.

{
  "Memory":  /* Peripheral name */
  {
    "_type":"Memory",   /* Peripheral type */
  }
}

Object specific parameters can be defined and will map towards available arguments in the constructor of the peripheral class.

{
   "Memory":  /* Peripheral name */
  {
    "_type":"Memory",   /* Peripheral type */
    "size": 0x0100000   /* Memory size in bytes */
  }
}

The reserved keyword "_connection" describes how a peripheral interconnects with other peripherals. In many cases a peripheral is connected to the root node sysbus.

To connect to another peripheral object (sysbus in this example), use the name of the object inside the "_connection" node. Depending on the class of the object there are different object properties to follow.

{
  "Memory":  /* Peripheral name */
  {
    "_type":"Memory",   /* Peripheral type */
    "size": 0x0100000   /* Memory size in bytes */
    "_connection":
    {
       "sysbus":
       [
        {"address":0},
        {"address":0xC0000000}
       ]
    }
  }
}

Note

A peripheral object A that is connected to another peripheral object B can be declared out of order within the same configuration file. If they are declared in separate files then the file declaring object B must be loaded before the file declaring object A. The sysbus are always present and does not need to be declared beforehand.
{
  "Peripheral_object_A": /* Level 1 node */
  {
    "_connection":
    {
      "Peripheral_object_B"
    }
  },
  "Peripheral_object_B": /* Level 1 node */
  {
  }
}

Emulation scripts

Emulation scripts consist of sequences of commands that could be manually typed into the Monitor. Scripts are read by the Monitor using the include command. They can be easily reused by including more general scripts in the more specific ones.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
:name: versatile linux (console)
#
# DEMO SCRIPT
#
# platform: versatile linux
#

using sysbus
createPlatform Versatile
sysbus Redirect 0xC0000000 0x0 0x10000000

### create externals ###

showAnalyzer sysbus.uart0

emulation CreateSwitch "switch"
emulation CreateTap "tap0" "tap"
connector Connect host.tap switch
connector Connect smc91x switch

machine CFIFlashFromFile @https://path.to/server/flash_versatile-[...]
                                                        0x34000000 "flash"

macro reset
"""
    ### set registers ###

    sysbus.cpu SetRegisterUnsafe 0 0x0
    sysbus.cpu SetRegisterUnsafe 1 0x183     # board id
    sysbus.cpu SetRegisterUnsafe 2 0x100     # atags

    ### load binaries ###

    sysbus LoadELF @https://path.to/server/versatile-kernel-[...] false

    sysbus LoadAtags "console=ttyAMA0,115200 noinitrd root=/dev/mtdblock0 rw
        rootfstype=jffs2 mtdparts=armflash.0:64m@0x0 earlyprintk mem=256M"
        0x10000000 0x100

    cpu PC 0x8000
"""

runMacro $reset
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
:Name: Versatile
:Icon: ARM
:HasFlash: true
:HasPendrive: true

#
# versatile board
#

include @platforms/cpus/versatile

machine LoadPeripheralsFromJSONFile @platforms/boards/versatile-externals.json

Note

Please note that the provided demos rely on binaries available on the internet. To run these scripts for the first time you have to be on-line, but these are subsequently cached and available locally.