CPUlator Simulator Documentation

CPUlator is a debugger and simulator of a Nios II or ARMv7 computer system and I/O devices. Simulation allows running and debugging programs without needing to use a hardware board. When you load an assembly program or ELF executable on the simulator, the program will behave (almost) identically to the same program running on real hardware. The simulated systems are based on the computer systems from the Altera University Program.

The primary advantage of using a simulator is the ability to do lab assignments without needing access to the hardware board. Another advantage is that simulators have greater visibility into the system state, and can warn about incorrect or suspicious behaviours, such as self-modifying code or violating calling conventions. There are some limitations to software simulation: A simulator cannot interact with physical hardware devices, and is somewhat slower than hardware (by about 2–20 times).

Features

Quick Start

  1. Choose which system to simulate (Settings → System). This setting determines which instruction set and I/O devices will be included
  2. In the Simple Assembler window, use the Compile and Load button to compile your assembly source and load the executable into memory
  3. Set breakpoints, step, run, stop, and debug your program.
If you want to simulate a compiled executable instead of assembly source code, see the Compiling Code section below.

What is CPUlator?

There are four main components in a typical development flow for simple assembly programs: Source code written by the user, an assembler (or compiler for a higher-level language) to transform the source code into executable machine code, a computer system to run the machine code, and a debugger to observe the behaviour of the program running on the computer. CPUlator provides the assembler, computer, and debugger inside a web browser (sorry, we don't write your code for you).

In the Altera Monitor Program development flow, the Monitor Program provides the assembler and debugger, while the computer system is running on an FPGA development board attached to the host PC over a programming cable. (Top-most option in the figure below)

CPUlator replaces both the computer system and debugger, and optionally also the assembler. For simple programs (one source file), using the built-in assembler (GNU assembler) is the most convenient option (Above figure, middle option). For more complex programs, it is also possible to assemble or compile your project into an ELF executable using any tools you wish (e.g., Altera Monitor Program) and then simulate the executable (above figure, bottom-most option).

Compiling Code

Internally, the CPUlator simulator simulates ELF executables (it does not directly simulate assembly source code). You can compile your source code using the built-in assembler, Altera Monitor Program, or any other development tools you wish.

Using the Built-in Assembler

The built-in assembler is an interface to the GNU assembler. When Compile and Load is clicked, the contents of the Simple Assembler editor is uploaded to the server, saved as a single file, then run through the GNU assembler. The server then sends back the ELF executable and any assembler output messages, and the executable is loaded into the simulator.

Because the ELF file is generated at the server, there are limits on the size of the compiled executable (currently 12 MB). If your program is bigger than a megabyte or so (which sometimes happens if it includes images or audio as initialized data), it is usually faster to compile your program locally than to download megabytes at every compile. If your program is substantially bigger than this, be aware that the executable size causes the simulator itself to consume memory to hold the executable. Don't expect the simulator to ever work with gigabyte-sized executables.

For ARMv7: The GNU assembler uses "divided" syntax by default. If you're writing in the newer "unified" syntax, use a .syntax unified directive to change the syntax. The differences are minor, so this won't affect most programs. The CPUlator disassembler uses instruction names in unified syntax (this only affects a few infrequently-used instructions).

Compiling using Altera Monitor Program

The Altera Monitor Program can be used to compile projects without being connected to an FPGA board. However, actions that interact with the board need to be avoided.

When creating a project, specify the "DE1-SoC Computer" system as usual. This is important because the compiler makes certain assumptions (e.g., system memory size) that depend on the computer system, and should match what's being simulated here.

When prompted to select the programming cable, it's ok to leave it blank, as we won't be using it.
System Parameters settings box

When prompted, do not download the computer system to the board. Choose No.
Download system? dialog box

You can now compile your project as usual, but do not use "Load" nor "Compile & Load". Compiling the program produces a filename.elf (or filename.axf for ARM) executable file in your project directory. To load the executable into the CPUlator simulator, use Load program and choose this executable file.
Simulator toolbar

Using the Simulator

The simulator interface consists of the Toolbar, Messages window, and four movable windows: Disassembly, Memory, I/O Devices, and Settings. Move the windows around to reduce scrolling as you move between windows while debugging.

Toolbar

The toolbar along the top has the usual debugging operations: step, step over, continue, stop, restart, and reload. These are mapped to the same shortcut keys as in the Altera Monitor Program.

Step over and step are the same except when single-stepping a call instruction. Step will single-step into the first instruction of the function, while Step over will skip over the function entirely, stopping at the instruction following the call. This can be useful for skipping over big assumed-bug-free functions such as printf. Step over is implemented by tracking function calls and returns (the same mechanism used to show the call stack). If your function calls and returns are not properly paired or you use an unusual call or return method that is not recognized as a call or return, you may get strange behaviour.

The reload function reloads the executable into memory. If you are simulating an ELF executable from disk, the executable is reloaded from disk, in case the executable on disk has changed due to being recompiled. If you are compiling using the Altera Monitor Program, compiling (Ctrl-Shift-C in the Monitor Program) then reloading (F5 or click "Reload Program" in the browser) will load the new program.

Disassembly Window

This shows the disassembly of a memory region. This shows only a disassembly of the instructions in memory, not lines from your source code.

Immediately above the disassembly window, there is a "Go to address, label, or register" text input. This allows you to jump to a particular location in your program. The box will accept hexadecimal numbers (3a8), labels (_start), or registers (pc).

You can force the program to move to a different instruction by changing pc. This can be done by changing pc in the registers window, or double-clicking the target line in the disassembly (This works the same way in the Monitor Program).

Trace and Call stack

The trace and call stack show what the program has been executing. The trace shows the last 200 instructions that were executed. The call stack shows the current stack of function calls since the last processor restart. Click on the instruction in the list to highlight and scroll to the instruction in the disassembly window.

Breakpoints

This lists all of the breakpoints that have been set. Set breakpoints by clicking in the gray column in the disassembly window, next to the instruction where you want the breakpoint. The list in the breakpoint window allows you to make breakpoints conditional. A conditional breakpoint stops the program only if the specified condition evaluates to true (non-zero). For example, a condition of r3==0 would cause the breakpoint to trigger only if register r3 is zero when this instruction is reached.

Stats

This shows some performance counters, which are reset when the simulation is restarted.

Memory Window

This shows a region of memory as an array of 32-bit words. The number format and number of words per row is configurable in the Settings window.. You may edit values in memory by clicking a word to pop up a text box, entering a new hexadecimal value, then pressing Enter. Pressing ESC will cancel the edit. Above the Memory Window, the "Go to..." box works in the same way as for the Disassembly window.

When viewing the memory address ranges of memory-mapped I/O devices, the simulator will show you the values at those memory locations as if a load were performed, but without triggering any of the side effects that would normally be performed in response to a load (such as dequeueing a FIFO). This may make it slightly easier to observe the system state without disturbing it. (However, there is currently no method to intentionally trigger the side effects from the debugger.) This behaviour differs from using a debugger with real hardware.

Registers Window

Along the right is the Registers window. You may change the value of a register by clicking, entering a new number, and pressing Enter.

Devices Window

The devices window allows you to interact with the hardware devices in the computer system outside the CPU, such as LEDs and switches. Because we're limited to operating in a web browser, the interface sometimes differs from the board. For more complicated I/O devices, sometimes this interface is simplified, without full functionality, but still giving you a way to test how your program interacts with the device. In some cases, the interface also shows extra debugging information about the state of the I/O device, to help in debugging your program.

The panels in the Devices window can be reordered by dragged them around, so you can group commonly-used devices together for convenience.

Watchpoints

A watchpoint is used to pause the simulation (like a breakpoint) whenever a certain region in memory is read or written. There are four data watchpoints to allow monitoring four memory regions for loads and stores (but not instruction fetches).

Each memory region has a start and end address (in hexadecimal). The R and W checkboxes select whether the simulation should stop when a memory read or write occurs. Regardless of whether these checkboxes are enabled, the number of reads and writes into the memory region and the most recent instruction that accessed the region are recorded. The Reset button resets these counters.

Settings Window

System

Changing the system affects which CPU and I/O devices are in the system. Clicking Change will change the system and reload the simulator, which causes any changes to be lost (including in the Simple Assembler editor). See the Simulated Systems section for a description of the systems.

Display Options

This changes how numbers are displayed in various places in the simulator. For the ASCII byte option, bytes are show as a single ASCII character if it is printable, or as two hexadecimal digit if not.

Debugging Checks

The checkboxes control whether each assertion should be enforced. Turning off some of these may be useful if your program does something unusual and the assertion is too stringent. Hover over each checkbox for a description of each assertion (The descriptions use Nios II terminology and register names).

Messages Window

The messages window at the bottom shows various messages that may be generated by the simulator. Click the blue header to hide or show it. It will automatically pop open when a new message is printed.

These messages may be useful for debugging, particularly messages that warn about reading or writing to addresses that appear to be invalid.

Debug Assertions

The CPUlator simulator does more sanity checks than a real processor, to catch common errors. When it detects something suspicious, the simulator will stop executing (as if a breakpoint occurred), allowing you to examine the cause of the problem as soon as it's detected. Any of these checks can be disabled by unchecking the desired checkbox in Settings → Debugging Checks.

Simulated Systems

Processors

There are currently two supported processor instruction set architectures:

Computer Systems

DE1-SoC Nios II

This is a Nios II system with all of the FPGA-side I/O devices found in the DE1-SoC Computer, the 1 GB DDR3 memory attached to the HPS (Hard Processor System), but no other HPS-attached devices.

There are a few minor differences:

Nios II Generic

This is a Nios II CPU with 4 GB of memory and no other I/O devices.

DE1-SoC ARMv7

This is a ARMv7 system with all of the FPGA-side I/O devices found in the DE1-SoC Computer, the 1 GB DDR3 memory, Cortex-A9 private timer, and ARM GIC interrupt controller. Many of the complex HPS devices (Ethernet, USB, SDHC, etc.) are omitted.

There are a few minor differences:

ARMv7 Generic

This is a ARMv7 CPU with 4 GB of memory and no other I/O devices.

I/O Devices

All of the simulated I/O devices generally behave the same as the I/O devices in the Altera University Program's DE1-SoC Computer system. This section describes some of the differences or user-interface features in the simulator.

Timers

Timers include both the Interval Timer and Cortex-A9 private timer. In the simulator, the internal state of the timer (counter, one-shot or continuous mode, etc.) are shown. There is also a Force Timeout Now button that causes the counter to jump to 0 and trigger a timeout. This is useful when debugging with long timeouts.

UART

The JTAG UART device is attached to a terminal in the Altera Monitor Program, so it is also attached to a terminal in the simulator here. Bytes sent from the CPU to the UART will be shown. To send bytes to the UART, click on the terminal inside the UART device's sub-window to give it keyboard focus, then type. The simulated device also shows the state of the two FIFOs.

The terminal supports VT100 escape codes, like the one in the Altera Monitor Program, though with some minor differences in behaviour. The terminal uses code from https://github.com/chjj/term.js.

Carworld UART

This device is a custom device used at the University of Toronto and is not part of the DE1-SoC Computer. The rest of this paragraph assumes you know the lab assignment that this device is used with. While this device does not let you control the game (as there is no way to connect to the game), it will show you whether you are able to send and receive commands correctly to/from the game. See the Help popup on the device for more details.

VGA pixel and character buffer

The details of the VGA DMA device (including page flipping) are accurately modelled. Thus, in addition to being able to draw pixels by writing to the pixel buffer, the pixel buffer location can be changed to another region of memory, and page flipping can also be simulated. The pixel buffer is initialized to random values to discourage using the display without first clearing it, but in the DE1-SoC the pixel buffer is initialized to black (This wasn't the case with the DE2 that used an off-FPGA frame buffer SRAM).

In hardware, the character buffer is an overlay on top of the pixel buffer. This behaviour is accurately modelled here.

Audio input and output

Due to the non-realtime nature of a web browser, it was necessary for the simulated audio interface to have much larger audio sample buffers to absorb jitter. Despite this change, there is no change to the register-level interface (the bigger buffers are hidden except for timing differences). In the simulator, audio input FIFO underflows and output FIFO overflows are tracked to make sure your code isn't underflowing or overflowing the buffers. In hardware, this is harder to detect, as the only symptoms are noisy sound or sound that appears to play at an inappropriate speed. Use Reset overflow flags to reset the Has overflowed/underflowed state.

PS/2 keyboard and mouse

The simulator interface provides several methods of sending input to emulate what a PS/2 keyboard or mouse might send. The simulator generally does not accurately model the power-on negotiation sequence by keyboards or mice, but does have some basic support for responding to a few commands (such as reset). See the Help popup for more details.

Video-in

This simulator can't input a real video as TV input. However, the video input device (and its DMA controller) is correctly modelled. Instead of a user-provided video, it paints a test image (that moves) to its output buffer (defaults to the VGA pixel buffer location). This allows you to test enabling the video input device and relocation of the output buffer location.

ADC

The ADC device on the DE1-SoC is modelled, but just returns a different number for every read instead of a real signal. The ADC interface is based on version 16.0+ of the DE1-SoC Computer, which includes a valid bit at bit[16] of each channel. The interface in version 15.1 and earlier omitted the use of the valid bit.