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.
- Renode
- SystemC dynamic libraries
- C++ compiler (tested with Clang, GCC)
- CMake
The repository follows the standard CMake build process, that is:
$ mkdir build
$ cd build
$ cmake .. -DUSER_RENODE_DIR=<absolute path to Renode>
$ makeWhen 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).
To compile the examples with custom SystemC builds, use -DUSER_SYSTEMC_LIB_DIR to specify
the directory with SystemC binaries and -DUSER_SYSTEMC_INCLUDE_DIR to provide the path to headers.
If these are missing, the build script will attempt to use a system-wide SystemC installation.
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=14This will build renode_bridge and the examples using the chosen
standard instead of the default C++17.
Contains 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
Contains tests for specific scenarios, including regression tests. They are not very illustrative and less interesting to users looking for reference. Other than that, the structure is identical to that of examples.
To run my_example from the repository root, use:
$ renode-test examples/my_example/my_example.robotTo run all examples:
$ pushd examples
$ renode-test -t all_examples.yaml
$ popdLocated 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.