Copyright (c) 2024 Antmicro
This repository contains tools for creating Renode simulations using components written in SystemC and a range of examples.
Renode and SystemC simulations run as separate processes, communicating
through reusable interface components - a memory-mapped SystemCPeripheral
peripheral in Renode and a renode_bridge
SystemC module in SystemC.
From the perspective of Renode, a SystemC model can be interacted with like any other peripheral. From the perspective of SystemC, Renode is represented by a module connected by standard TLM sockets.
The repository follows the standard CMake build process, that is:
$ mkdir build
$ cd build
$ cmake .. -DUSER_RENODE_DIR=<absolute path to Renode>
$ make
When run from the repository root, the above commands will build all the examples.
Individual examples are built by running the above in the directory for
a particular example (e.g. examples/lt
).
SystemC libraries are compiled against a particular version of the C++
standard. The build process in this repository assumes C++ 17 by default, which
should be the case for all SystemC packages installed on reasonably
up-to-date systems. If this is not the case, however, the builds may fail with
undefined reference to sc_core::sc_api_version_2_3_4_cxx2017...
linker
errors. In that case, either install an up-to-date SystemC distribution
or pass an appropriate -DCMAKE_CXX_STANDARD
argument to the relevant cmake
call, for example:
$ cmake .. -DUSER_RENODE_DIR=<absolute path to Renode> -DCMAKE_CXX_STANDARD=14
This will build renode_bridge
and the examples using the chosen
standard instead of the default C++17.
examples
directoryContains one subdirectory for each available example, with the following structure:
my_example
├── CMakeLists.txt CMake configuration building the SystemC executable (bin/my_example)
├── bin
│ ├── my_example SystemC executable
│ └── my_example.elf Zephyr binary, if the example uses it and is built locally
├── renode
│ ├── my_example.repl Description of the Renode platform on which the example is run
│ └── my_example.resc Renode script for initialization of the example
├── systemc
│ ├── include C++/SystemC header files for the example
│ └── src C++/SystemC implementation files for the example
├── zephyr Zephyr RTOS source code, if the example uses a Zephyr executable
└── my_example.robot Robot Framework file with test logic
To run my_example
from the repository root, use:
$ renode-test examples/my_example/my_example.robot
To run all examples:
$ pushd examples
$ renode-test -t all_examples.yaml
$ popd
Located in the /examples/lt folder, lt
illustrates the integration of Renode with a non-trivial SystemC
model. It is an adaptation of the loosely-timed
example, which is part of the
official SystemC-2.3.4 distribution.
The SystemC model consists of two initiator
modules that generate traffic, two
memory
modules that implement simple memories and a bus module that connects
them.
The above has been extended so that the Renode simulation serves as a third
"initiator" module, reading and writing to the memories modelled in the
SystemC process via sysbus Read/Write ...
Renode commands.
Located in the /examples/timesync folder, timesync
illustrates virtual time synchronization between Renode and
SystemC. It is also simulated within a more complex platform (the STM32 Mini
F401),
extended with the SystemC peripheral. The SystemC part is a simple timer that
returns SystemC virtual time in seconds when read from. The Renode simulation
reads it and outputs the obtained time to the UART. It can then be compared with
Renode virtual time to confirm that they are synchronized.
Located in the /examples/transaction-delay folder, transaction-delay
is very similar to timesync
, but additionally the
read transaction from the SystemC peripheral is delayed through the delay
parameter in the TLM b_transport
call. The result is that time value read
from timekeeper
arrives with a one second delay, which can be confirmed by
looking at the UART output.
Located in the /examples/interrupts folder, interrupts
shows how to model interrupt requests sent through the Renode -
SystemC interface.
The SystemC part contains two interrupter
modules, each sending IRQs to Renode.
Interrupters
are connected by a simple bus module, routing the requests
to one or the other based on the address.
Interrupter modules connect their interrupt lines directly to renode_bridge
through SystemC ports bound to sc_signal<bool>
channels.
The two interrupter modules raise interrupts in fixed periods. The interrupt signals are cleared when any value is written to the interrupters. The Renode side of the simulation sets up two interrupt handlers - one for each interrupter. When the Renode-simulated CPU receives an interrupt, an output message is written to the UART and the interrupt signal is cleared by writing to the appropriate interrupter memory address.
Located in the /examples/dma folder, dma
shows a rudimentary DMA controller (DMAC) peripheral implemented in
SystemC and connected to Renode. It illustrates SystemC-to-Renode bus
communication and handling of Renode-initiated GPIO signals.
The Renode script sets up the DMAC by setting the appropriate source, destination and data length registers. It then starts the memory transfer by writing to the DMAC control register. Next, DMAC is informed that the bus is free by Renode raising a "bus free" signal on one of the GPIO pins on the SystemC peripheral. The DMAC then performs a memory-to-memory transfer according to the setup.
Located in the /examples/direct-connection folder, direct-connection
illustrates a case where there are multiple SystemC peripherals.
It also demonstrates the use of the direct connection
functionality, allowing
peripherals to communicate using memory-mapped transfers that are not going through the
Renode system bus. This, for example, allows them to communicate using a
different address space than the system bus address space.
The peripherals hold a single value, which can be changed by writing to the peripheral. When read from, the peripheral will read the value not from itself, but from the other peripheral, using the direct connection. This is transparent from the point of view of the Renode system bus. The example works by writing values to the two peripherals, and illustrating that reading from one of them returns the value saved in the other.
┌─────────────────┐ ┌────────────┐
│ │◄────────────────►│Peripheral 0│
│ │ └─────┬──────┘
│Renode system bus│ │ Direct connection
│ │ ┌─────┴──────┐
│ │◄────────────────►│Peripheral 1│
└─────────────────┘ └────────────┘
Located in the /examples/tlm-non-blocking folder, tlm-non-blocking
is almost identical to direct-connection
. The most important
difference is that the peripherals use a non-blocking TLM interface. This is just
a matter of syntax, as the integration still handles the transactions in a
single phase.
Located in the /examples/multiple-peripherals folder, multiple-peripherals
illustrates a possible translation of a pure SystemC simulation to
a co-simulation with Renode, with use of most integration features. The README in
the example directory provides more details.
Located in the /examples/gpio-connection folder, this basic example illustrates a SystemCPeripheral connected to an output GPIO of another peripheral.
The SystemC model updates its internal state after receiving a GPIO event, sent by a MiV_CoreGPIO
peripheral modelled in Renode. The state is then read
through the system bus to verify that the communication finished successfully.