0% found this document useful (0 votes)
24 views49 pages

Gu 04 WG Debugging and Peripherals

The document discusses debugging embedded firmware using Renode and PlatformIO. It describes how to debug firmware using gdb, PlatformIO, and Renode. It also provides instructions on integrating Renode and PlatformIO for debugging.

Uploaded by

ewfkahq3vw
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
24 views49 pages

Gu 04 WG Debugging and Peripherals

The document discusses debugging embedded firmware using Renode and PlatformIO. It describes how to debug firmware using gdb, PlatformIO, and Renode. It also provides instructions on integrating Renode and PlatformIO for debugging.

Uploaded by

ewfkahq3vw
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 49

Debugging with Renode and Platformio

Peripheral modeling with Renode

System On Chip Architecture


2022 - 2023
Outline

● Debugging with renode and platformIO


○ Debugging with gdb
○ Debug with PlatformIO and HW
○ Debug with Renode
○ Integrate Renode and PlatformIO
Outline

● Debugging with renode and platformIO


○ Debugging with gdb
○ Debug with PlatformIO and HW
○ Debug with Renode
○ Integrate Renode and PlatformIO
Debugging with gdb: A very simplified list

• Compile your firmware for debugging


• Debug symbols
• Not optimized
• Load the firmware on the target
• Start a gdb server on the target
• Connect to the gdb server on the host
• Start execution
Outline

● Debugging with renode and platformIO


○ Debugging with gdb
○ Debug with PlatformIO and HW
○ Debug with Renode
○ Integrate Renode and PlatformIO
How to debug using platformio IDE and HW
If your board is supported by platformio, it is enough to start the debugging
with VsCode.
https://docs.platformio.org/en/latest/plus/debugging.html

Platformio will take care of all the required steps:


• Compile the debug version of your firmware
• Setting up the gdb toolchain and start the gdb communication
• Loading the firmware and start the execution (using gdb)

Check in your .vscode/launch.json configuration which toolchain is used for


your platform. For the K64F, we use gcc-arm-non-eebi

"toolchainBinDir": "/home/walter/.platformio/packages/toolchain-gccarmnoneeabi/bin"

(Inside this folder you can find the gdb that we will use in the next steps)
How to debug using platformio IDE and HW
If you have multiple environments, before starting debugging you need to choose one.

From our previous session, we had two environments


● one for running in hardware
● one for running in renode

To debug in hardware, simply choose the correct environment, using the PlatformIO: switch
project environment command:

You can also access this command from the status bar:
How to debug using platformio IDE and HW
To start the debugging, simply start it as you would do for a local program with vscode.
Choose the PIO Debug configuration that platformIO generated at project creation.

Set a breakpoint somewhere in your code and hit the green arrow to start debugging.
You will get all the usual functionalities gdb offers:
● Breakpoints
● Debug console
● Step by step execution
● Variables examiner
● Peripherals, Registers, memory and disassembly
How to debug using platformio IDE and HW
How to debug using platformio IDE and HW
Outline

● Debugging with renode and platformIO


○ Debugging with gdb
○ Debug with PlatformIO and HW
○ Debug with Renode
○ Integrate Renode and PlatformIO
How to debug using renode

We will have to take care of some of the steps.


https://renode.readthedocs.io/en/latest/debugging/gdb.html

• Open renode monitor, create a machine and load the platform


• Start a gdb server on renode
• Open gdb on the host
• Connect to the server
• Load the debug firmware
• start simulation
Loading the firmware
This can be done in two different ways:

● With the usual renode command sysbus LoadELF


● From the gdb client, using the load command.

(The second way is how platformIO does it by default)


Unfortunately the second way does not work with the K64F platform. See issue
https://github.com/renode/renode/issues/398

So we will work with the first approach. The commands to execute are:

1 - Open renode monitor, create a machine and load the platform (using k64f.resc script)
2 - start a gdb server on renode, with machine StartGdbServer 3333
3 - Open gdb with the debug firmware ./arm-none-eabi-gdb firmware.elf
4 - Connect gdb in the host to the server with (gdb) target remote :3333
5 - Load your debug .elf firmware from renode using sysbus LoadELF @firmware.elf
6 - start simulation with (gdb) monitor start and (gdb) continue &
Loading the firmware
(monitor) include @scripts/single-node/k64f.resc
(K64F) machine StartGdbServer 3333
(K64F) showAnalyzer uart0
(K64F) sysbus LoadELF @.pio/build/frdm_k64f_renode_debug/firmware.elf

$ /home/walter/.platformio/packages/toolchain-gccarmnoneeabi/bin/arm-none-eabi-gdb
.pio/build/frdm_k64f_renode_debug/firmware.elf

GNU gdb (GNU Tools for Arm Embedded Processors 8-2018-q4-major) 8.2.50.20181213-git
. . .

For help, type "help".


Type "apropos word" to search for commands related to "word"...
Reading symbols from .pio/build/frdm_k64f_renode_debug/firmware.elf...
(gdb) target remote :3333
Remote debugging using :3333
sys_clock_isr (arg=0x0 <sys_clock_isr>) at
/home/walter/.platformio/packages/framework-zephyr/drivers/timer/sys_clock_init.c:49
49 }
(gdb) monitor start
Starting emulation...
(gdb) continue &
Continuing.

NOTE: If you are following this slides offline, the commands above will fail because you don’t have a debug
firmware yet. Keep reading the next couple of slides.
Outline

● Debugging with renode and platformIO


○ Debugging with gdb
○ Debug with PlatformIO and HW
○ Debug with Renode
○ Integrate Renode and PlatformIO
Integrating platformio and renode for debugging
In the previous example we saw that we need a debug firmware.

We also saw that we can not rely on the (gdb) load command, and therefore we
can not rely on PlatformIO to generate a debug firmware automatically and load it.

Therefore we have to do it manually.

https://docs.platformio.org/en/latest/projectconf/build_configurations.html

https://docs.platformio.org/en/latest/projectconf/section_env_debug.html#debug
-load-mode
Integrating platformio and renode for debugging
[env:frdm_k64f_renode_debug]
; custom settings for debugging with renode.
; automatic load using gdb does not work in this platform, so we manually
; compile a debug release and load it using renode.
; see https://github.com/renode/renode/issues/398

build_type = debug
debug_tool = custom
debug_load_mode = manual ; Disable automatic loading by platformio
debug_port = localhost:3333
debug_server = renode
--hide-log ; to avoid filling the debug console with renode logs
-e include @scripts/single-node/k64f.resc
-e sysbus LoadELF @$PROG_PATH
-e machine StartGdbServer 3333

; The usual commands so that we can interact with the uart0


-e emulation CreateServerSocketTerminal 3456 "term" ; Note there is no backslash
-e connector Connect uart0 term
-e showAnalyzer uart0

debug_extra_cmds =
monitor start ; same as we did when using gdb manually, we start the simulation
Integrating platformio and renode for debugging
Integrating platformio and renode for debugging
Notes:

Platformio provides a debug_tool = renode option, but is not available for all
platforms.
https://docs.platformio.org/en/latest/plus/debug-tools/renode.html

For some platforms, the autoload from platformio (using gdb load) works, so
you don’t have to do it with renode. For this reason some examples you will
find online do not use the same procedure that we used.
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
Modeling peripherals with renode
https://renode.readthedocs.io/en/latest/advanced/writing-peripherals.html

Most of the peripherals already present and that you will implement are
written in C #.

We need a way to interface our CPU code, written (usually) in C, to our


peripherals, in C #.

Done by Renode using the TranslationCPU class.


Modeling peripherals with renode

A C # class is considered a peripheral model if it implements the IPeripheral


interface.

In order for the peripheral to be attachable to the system bus, it must


implement at least one of the interfaces
● IBytePeripheral
● IWordPeripheral
● IDoubleWordPeripheral
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
Recap of I2C communication
On a basic configuration, you will have
● A master (I2C controller)
● A few slaves (sensors)

Connected by two lines only:


SDA: Serial Data Line
SCL: Serial Clock Line

Because all devices are connected to the same line, all devices need to have an unique
address.
Recap of I2C communication

To establish a communication between the master and one of the slaves, the
master has to:
● Take control of the bus with a Start
● Send the address of the slave (usually 7 bits)
● Indicate if the operation is Read or Write
● The 8 bit data can then be transmitted (with the corresponding ACKs)
● Finally the Stop is sent.
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
I2C modeling in renode
Renode already provides several I2C implementations of:

● I2C controllers, that will be attached to the sysbus.


● I2C devices, that will be attached to an I2C controller.

You can check in the renode sources, under:

src/Infrastructure/src/Emulator/Peripherals/Peripherals/I2C

src/Infrastructure/src/Emulator/Peripherals/Peripherals/Sensors
I2C controller example: STM32F4
src/Infrastructure/src/Emulator/Peripherals/Peripherals/I2C/STM32F4_I2C.cs

namespace Antmicro.Renode.Peripherals.I2C
{
public sealed class STM32F4_I2C : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral,
IBytePeripheral, IKnownSize
{

Note this interface implements IBytePeripheral and IDoubleWordPeripheral so it can


be attached to the sysbus.

Note the namespace.

We can therefore add this I2C controller to our platform (adding it to the sysbus).
To add the I2C to our platform we need to know:
- The address in the sysbus
- The Event and Error interrupts ID in the nvic

We can get this information from the datasheet.


I2C controller example: Getting the sysbus address
I2C controller example: Getting the NVIC position
I2C controller example: adding it to the platform:

Address: 0x40005400
Event Interrupt position: 31
Error Interrupt position: 32

In your .repl file:

i2c1: I2C.STM32F4_I2C @ sysbus 0x40005400


EventInterrupt -> nvic@31
ErrorInterrupt -> nvic@32
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
Device example: I2C temperature / humidity sensor

Model: Si7021
https://www.adafruit.com/product/3251

● How is it modeled in renode?


● How can we add it to our platform?
● How can we access it from our firmware?
Sensor Addressing
The SI7021 7 bit address is 0x40.

NOTE: Be careful with the address specification of I2C

When referring to I2C devices, the address you find on the datasheet is usually the 7 bit
address.

However, when using the device, in the I2C protocol you need to use the 8 bit address,
which is just the 7 bit address and the R/W bit:

So if you want to address the sensor in:


● Read mode: 0x40 << 1 | 0x01 = 0x81
● Write mode: 0x40 << 1 | 0x00 = 0x80
Sensor Addressing
The SI7021 7 bit address is 0x40.

This address can not be changed.

Therefore if you want to use multiple SI7021 sensors, you need to use some technique like
I2C channel multiplexer or multiple I2C ports. https://learn.adafruit.com/working-with-
multiple-i2c-devices

Some sensors allow configuration of the address. For example the TMP 102 allows you to
configure the address according to the value of the A0 PIN:
How to read data from the Si7021 sensor
From the datasheet, the required operations to take a
measurement are:

● Send command:
○ Start
○ Address
○ Write
○ Data: command

● Send read request and wait: NOTE: The sensor also


○ Start (repeated) supports the No Hold master
○ Address mode.
○ Read
○ Sensor will hold the line during measurement, so no data is actually transmitted.
○ Data: Measurement in two bytes.
How to read data from the Si7021 sensor
The most important commands we will use are:
Sensor model in renode
src/Infrastructure/src/Emulator/Peripherals/Peripherals/Sensors/SI70xx.cs

● Renode already provides a generic Interface for I2C peripherals, so we can simply inherit from it.
● Renode already provides a mechanism to list the commands our sensor can receive, we just need
to provide the handler.

namespace Antmicro.Renode.Peripherals.Sensors
{
public class SI70xx : II2CPeripheral, ITemperatureSensor,
IHumiditySensor
{
public SI70xx(Model model)
{
this.model = model;
commands = new I2CCommandManager<Action<byte[]>>();
outputBuffer = new Queue<byte>();

commands.RegisterCommand(MeasureHumidity, 0xE5);
commands.RegisterCommand(MeasureHumidity, 0xF5);
commands.RegisterCommand(MeasureTemperature, 0xE0);
Sensor model in renode: The environment
We need private members to hold the Humidity and Temperature raw values. Think of these values as the
environment value, that the sensor will measure when triggered.
We also need to provide public properties (setters and getters) that allow us to get and set human values from
the renode monitor and control the environment. (The conversion formulas are in the sensor datasheet.)

private decimal humidity;


private decimal temperature;

public decimal Humidity


{
get
{
return (humidity * 125) / 65536 - 6;
}
set
{
humidity = (value + 6) * 65536 / 125;
}
}

public decimal Temperature


{
get
{
return (temperature * 175.72m) / 65536 - 46.85m;
}
set
{
temperature = (value + 46.85m) * 65536 / 175.72m;
}
}
Sensor model in renode: Write request
We need to provide a write method that is invoked when the master request to write data.

For our sensor, when the master requests a write, it is sending the sensor a command, so
we need to handle it.

public void Write(byte[] data)


{
if(!commands.TryGetCommand(data, out var command))
{
this.Log(LogLevel.Warning, "Unknown command:...
return;
}
command(data);
}
Sensor model in renode: The handler
We need to provide the handlers for each command. Notice that when a command for measurement arrives,
we are NOT sending anything in i2c yet, we are simply “measuring” the requested variable, i.e. we get the
values from the environment and we save them in our measurement buffer.

public SI70xx(Model model)


{
commands.RegisterCommand(MeasureHumidity, 0xF5);
commands.RegisterCommand(MeasureTemperature, 0xE0);
. . .
}

private void MeasureHumidity(byte[] command)


{
outputBuffer.Enqueue((byte)((uint)humidity >> 8));
outputBuffer.Enqueue((byte)((uint)humidity & 0xFF));
}

private void MeasureTemperature(byte[] command)


{
outputBuffer.Enqueue((byte)((uint)temperature >> 8));
outputBuffer.Enqueue((byte)((uint)temperature & 0xFF));
}

private readonly Queue<byte> outputBuffer;


Sensor model in renode: Read request
Finally, we need to implement a Read method invoked when the master sends a read request.

Here we simply send the outputBuffer that we prepared in the previous step.

Notice there is only one Read method, as the variable to be read is prepared during the write
request.

public byte[] Read(int count = 1)


{
var result = outputBuffer.ToArray();
outputBuffer.Clear();
return result;
}
Sensor model in renode: recap
● Environment is saved in humidity and temperature members.
○ We can modify these members from the Renode monitor using
the public properties.
○ The environment values are “always present”
○ The sensor is NOT continuously measuring these values.

● Write request arrives


○ Decodify command
○ Execute command
■ In case of measurement command, read the requested
variable and save it in the Output Buffer
■ At this point the value in the sensor is equal to the value in
the environment.

● Read request arrives


○ Simply reply with the Output Buffer that should contain the
requested variable.
Sensor model in renode: Other types of sensors.

We have seen an example of an I2C sensor.

Renode also provides examples for SPI sensors.

Renode also provides an specialization for ST I2C sensors. See:


src/Infrastructure/src/Emulator/Peripherals/Peripherals/Sensors/LSM330_Accelerometer.cs
Sensor model in renode: add it to the platform.

Note that we do not add it directly to the sysbus (we can’t, our sensor interface does not
implement IBytePeripheral etc)

Instead we add it to the I2C controller that we had created before.

Make sure the address corresponds to the one in the datasheet (7 bit address).

i2c1: I2C.STM32F4_I2C @ sysbus 0x40005400


EventInterrupt -> nvic@31
ErrorInterrupt -> nvic@32

sensor_si7021: Sensors.SI70xx @ i2c1 0x40


model: Model.SI7021
Outline

● Modeling peripherals in renode


○ General concepts for modeling in renode
○ I2C modeling example
■ Recap of I2C
■ I2C controller in renode
■ I2C device (sensor) in renode
■ Demo
Demo: Reading data from the I2C sensor.
To read data from the sensor, we will need to send the appropriate commands,
following the I2C specification (start, addressing etc)

Thankfully:
● STM32 provides the HAL (Hardware abstraction layer) to deal with the I2C
specification.
● You can find a driver for the si7021 (and other sensors), that uses the STM32
HAL here: https://github.com/belyalov/stm32-hal-libraries

The demo shown during the lesson can be found here:


https://github.com/SoC-Arch-polito/ex22-renode-i2c-sensor
It uses the two resources mentioned above. Some of the code was generated
using STM32CUBE. (see comments in the code for more details).

● Zephyr also provides an abstraction layer and drivers for multiple sensors.
Demo: Testing.
Using renode for sensor modelling can speed up the validation of your algorithms.

For example, if you need to simulate an environmental condition in which the


variables are behaving in a certain way (for example increasing monotonically in a
certain window of time), you can do this easily with the public properties of the
sensor, and using a simple resc script:

i2c1.sensor_si7021 Temperature 10
sleep 5
i2c1.sensor_si7021 Temperature 15
sleep 5
i2c1.sensor_si7021 Temperature 20
sleep 5

You can also use python to describe more complex behaviors. Refer to:
https://renode.readthedocs.io/en/latest/basic/using-python.html

If you are working with multiple sensors measuring the same variable, you want
to be sure all are reading consistent data. Check how to use environments
https://renode.readthedocs.io/en/latest/basic/sensors.html

You might also like