Let's get started!

To complete this tutorial, you will need to download and install Emul8 first.
Visit Github for the code and instructions.

Running a sample emulation

So, you have downloaded and installed Emul8, and run it with the emul8 command. This tutorial will guide you through some basic concepts and show you some of the possibilities in order to get you started.

When you run Emul8, you are greeted by a console, called the Monitor. It is the main control point of the whole environment.

For a comprehensive overview of available commands, see the built-in help:

(monitor) help
Available commands:
Name              | Description
===============================================================
allowPrivates     : allow private fields and properties manipulation.
analyzers         : shows available analyzers for peripheral
commandFromHistory: executes command from history.
[...]
mach              : list and manipulate machines available in the environment.
[...]

That's a lot of possibilities, but where to start?

For one thing, it's quite lonely out there at the moment, with no virtual embedded devices to manipulate. But the mach command sounds promising!

Let's create something using mach create:

(monitor) mach create
(machine-0)

Notice the change in the color and name of the prompt. This means that a new machine - think of it as your embedded device - was created and you are now executing commands within its context. The concept of context is important to remember, as to manipulate a given machine you must always enter its context with mach set first (mach create switches context automatically).
Starting from now, we will only show the prompt when something interesting happens, otherwise we will limit ourselves to the command and its output, if present.
An empty machine consists only of a system bus. To turn it into a platform, you need to load a platform description, provided in the JSON format.

Sample json file:
"uart1":
{
    "_type":"UART.STM32_UART",
    "_connection":
    {
        "sysbus":
        {
            "address":0x40011000,
            "size":0x100
        }
    },
    "_irq":
        {
           "nvic":[37]
        }
},

The format specifies the components of a platform such as CPUs, serial ports and interrupt controllers, their properties and the connections between them. You can download a sample full platform description and load it with the LoadPeripherals command (notice the use of the @ sign to denote paths, and use tab-completion when looking for the right file; all paths can be absolute or relative to the Emul8 installation directory by default):

(machine-0) machine LoadPeripherals @/path/to/stm32f4discovery-demo.json

This command builds up an entire embedded platform, in this case an STM32F4 discovery kit, from the necessary components. To inspect the results of the previous command, you can use the peripherals command and see the peripherals available in your machine:

(machine-0) peripherals
Available peripherals:
can0 (STMCAN) in sysbus at <0x40006400, 0x40006800)

cpu (CortexM) in sysbus at Slot: 0

[...]
SRAM (Memory) in sysbus at <0x20000000, 0x20040000)

sysbus (SystemBus) in machine-0

uart1 (STM32_UART) in sysbus at <0x40011000, 0x40011100)

uart2 (STM32_UART) in sysbus at <0x40004400, 0x40004500)
[...]

You can see some peripherals connected to the system bus at certain addresses. machine-0, a.k.a. your virtual STM32F4 discovery kit, is now ready to be programmed with a real binary!

We will now need a binary to run. Of course, normally that would be your own code that you'd like to develop using Emul8, but for the purpose of this tutorial, a sample application is available for download. Save it to your hard drive and use loadELF to load it into machine-0's memory, noticing again the use of @ to denote paths:

(machine-0) sysbus LoadELF @/path/to/stm32f4discovery-demo.elf

For every path you provide in Emul8, you may use an URL instead of downloading the file manually. Try that instead!

The ELF file (i.e. binary with debug symbols - there is a similar command, LoadBinary, which loads plain old symbol-less binary files) is loaded into memory and interpreted. You can always use the information about stored symbols to help in the debugging process:

((machine-0) sysbus FindSymbolAt 0x08002ab1
USART_ReceiveData

To inspect the output of your application and interact with it, open a separate window for the serial port (UART0):

(machine-0) showAnalyzer sysbus.uart4

A window will open where you will be able to read from and write to the machine's serial port, just like you would with picocom or putty. Now that the emulation environment is prepared, you can run it with the start command:

(machine-0) start

See the output on the terminal - congratulations! You have just run your first emulation.

Some Emul8 magic

Unlike in a real device, at any point the current emulation state can be quickly saved for future replay using the Save command:

(machine-0) Save @state.dat

Now state.dat contains all the information needed to re-run the emulation from where we left off. We can now quit Emul8 altogether using the quit command (or rather its shorthand, q):

(machine-0) q

Loading the state (after running Emul8 again) is just as easy:

(monitor) Load @state.dat

Since state saving stores the entire emulation and not just a single machine, by default it does not enter the context of any particular machine - to continue we just need to change the context back to the machine we were working with earlier.

If unsure of the name of the machine, we can always list the available options:

(monitor) mach
[...]
No machine selected
Available machines:
        0: machine-0
[...]

Now to get back to the context of machine-0, use:

(monitor) mach set “machine-0”
(machine-0)

Emul8 lets you control all elements of the emulation. Most objects are accessible from the Monitor by their name. For example, typing a peripheral’s name and pressing will show you all available methods and properties:

(machine-0) sysbus.uart1

Following methods are available: [...]
 - Void AddLineHook (String contains, String pythonScript)
 - Void AttachLoggingTerminal ()
 - Void DebugLog (String message)
 - Boolean HasGPIO ()
 - UInt32 ReadDoubleWord (Int64 offset)
 - Void Reset ()
 - Void WriteChar (Byte value)
 - Void WriteDoubleWord (Int64 address, UInt32 value)

Usage:
 sysbus.uart1 MethodName param1 param2 ...

There are many possibilities to interact with the machine; a simple example would be setting some registers before starting the CPU:

(machine-0) sysbus.cpu SetRegisterUnsafe 1 0x149

Now let us read out this register and see if it was properly set:

(machine-0) sysbus.cpu GetRegisterUnsafe 1
0x00000149

That is, indeed, correct. In a similar fashion, you can get and set virtually any value or property of the emulated environment - you are in full control!

Connecting via GDB

The true power of Emul8 shows when you connect to the virtual CPU with your regular development tool like GDB:

(machine-0) sysbus.cpu StartGDBServer 12345

Replace 12345 with any port of your convenience.

Use GDB to debug your code as normal - but also examine the state of other peripherals inside the Monitor at the same time, owing to the full observability offered by the virtual environment.

Further steps

This is the end of the tutorial. You can proceed to read the documentation and explore the framework on your own, but you are kindly invited to contribute on Github and get in touch with us at info@emul8.org - we would love to hear about your use cases and ideas.

You can also say “hello” using our contact form, if that is more convenient for you.

Happy Emul8ing!

Questions?

We are happy to answer!

Contact