Renode

I find it a useful tool. Maybe you will, too!

About Me: I Do Weird Hardware

  • Simmel: Contact Tracing with Audio
  • Chibitronics: Programming Stickers with Audio
  • Novena: Open Source Laptop
  • Senoko: Open Source Power Board for Novena

Hardware with Embedded Software

  • Software needs to be written
  • Software needs to be tested
  • Software needs to be debugged

About Renode

  • Whole-System Emulator
  • Supports concurrent emulation
  • Extensible with C# and Python
  • Windows, Mac, Linux
  • MIT Licensed

About This Talk

  • Overview of Emulators
  • Oevrview of Weird Hardware
  • Cool things you can do

Who will find this interesting?

  • Creators: Those making new boards or hardware
  • Integrators: Running CI on firmware files
  • Reverse Engineers: Understanding new hardware and firmware

Creators: Making New Things!

  • Reusing an existing platform
  • Reusing an existing microcontroller
  • New microcontroller fron existing family

Integrators: Making Sure Nothing Broke!

  • Hardware testing incompatible with cloud
    • ...it sure is effective, though
  • Hardware crunch makes it difficult to get hardware
  • Downloading software is much cheaper than shipping
  • Can run tests on every code push

Reverse Engineers: What Is This Blob Doing?

  • Staring at code flow is enlightening, but time-consuming
  • What is it doing and how does it get there?
  • How can we make it do $x?

What is an Emulator?

What is an Emulator?

What is an Emulator?

Whole-System Emulator

Whole-System Emulator

Transparent Emulator

  • WSL2/Docker
  • qemu on Linux
  • Rosetta on Mac

Renode Is Many of These

  • Console: Able to present an interactive environment
  • Transparent: Can run in CI via Robot commands
  • Debugger: Has a GDB server built in

What is a Computer?

What is a Computer?

  • A system of devices
  • One or more processors
  • One or more buses
  • One or more blocks of memory
  • Some I/O

What is a Computer?

What is a Computer?

What is a Computer?

What is a Computer?

What is a Computer?

What is a Computer?

What is a Computer?

What is a Computer?

Defining a Computer in Renode


                        flash: Memory.MappedMemory @ sysbus 0x00000000
                            size: 0x00008000

                        sram: Memory.MappedMemory @ sysbus 0x20000000
                            size: 0x00001000
    
                        nvic: IRQControllers.NVIC @ sysbus 0xE000E000
                            IRQ -> cpu@0

                        cpu: CPU.CortexM @ sysbus
                            nvic: nvic
                            cpuType: "cortex-m0+"
                            PerformanceInMips: 24
                    

That's Nice, but What About...

  1. Loading firmware?
  2. Adding peripherals?

What is "Firmware"?

Firmware is a series of instructions executed by the CPU in order to accomplish a task
Firmware is Memory

Loading Firmware in Renode


                        sysbus LoadELF @firmware.elf
                    

                        sysbus LoadBinary @rom.bin 0x20000000
                    

                        sysbus LoadSymbolsFrom @rom.elf
                    

How does Renode Interact With $VENDOR_TOOL?

  • Hopefully your vendor tool produces ELF files
  • At the end of the day, it's all bytes. Just use LoadBinary!

What About Boot ROMs?

  1. Initialize peripherals
  2. Check for boot override
  3. Check for low-power state
  4. Load firmware into RAM
  5. Validate firmware
  6. Jump to loaded program

                        sysbus.cpu VectorTableOffset 0x20000000
                        sysbus.cpu PC 0x20000c00
                    

What about New Peripherals?

It's All About Small Victories

  • Serial ports are super rewarding
  • They're also usually simple!
  • They are easy to script

What is a Register?

What is a Register?

What is a Register?

Reuse an Existing Block!


                        flash: Memory.MappedMemory @ sysbus 0x00000000
                            size: 0x00008000

                        sram: Memory.MappedMemory @ sysbus 0x20000000
                            size: 0x00001000
                            
                        nvic: IRQControllers.NVIC @ sysbus 0xE000E000
                            IRQ -> cpu@0

                        // 👇 Add a UART with IRQ #10 at address 0x40300000
                        uart: UART.PL011 @ sysbus 0x40300000
                            -> nvic@10
                    
                        cpu: CPU.CortexM @ sysbus
                            nvic: nvic
                            cpuType: "cortex-m0+"
                            PerformanceInMips: 24
                    

Output Success!

Always Check for Block Reuse

Blocks are frequently reused across designs, and can save you from having to reimplement everything from scratch!

What if we need to write it ourselves?

Steps to Set Up a Serial Port

  1. Enable peripheral
  2. Set up clock
  3. Mux GPIOs
  4. Calculate baud rate
  5. Write to UART TX register
  6. Read from UART RX register

Steps to Set Up a Serial Port

  • Interrupt Support
    • Handled as a GPIO within the peripheral
  • DMA
    • Handled as a different peripheral

Example Serial Port


                    //
                    // Copyright (c) 2010-2018 Antmicro
                    //
                    // This file is licensed under the MIT License.
                    // Full license text is available in 'licenses/MIT.txt'.
                    //
                    using System.Collections.Generic;
                    using Antmicro.Renode.Peripherals.Bus;
                    using Antmicro.Renode.Core.Structure.Registers;
                    using Antmicro.Renode.Core;
                    using Antmicro.Renode.Logging;
                    
                    namespace Antmicro.Renode.Peripherals.UART
                    {
                        public class LiteX_UART : UARTBase, IDoubleWordPeripheral, IBytePeripheral, IKnownSize
                        {
                            public LiteX_UART(Machine machine) : base(machine)
                            {
                                IRQ = new GPIO();
                                var registersMap = new Dictionary<long, DoubleWordRegister>
                                {
                                    {(long)Registers.RxTx, new DoubleWordRegister(this)
                                    .WithValueField(0, 8,
                                        writeCallback: (_, value) => 
                                            this.TransmitCharacter((byte)value),
                                        valueProviderCallback: _ => {
                                            if(!TryGetCharacter(out var character))
                                            {
                                                this.Log(LogLevel.Warning, "Empty Rx FIFO.");
                                            }
                                            return character;
                                        })
                                    },
                                    {(long)Registers.TxFull, new DoubleWordRegister(this)
                                        .WithFlag(0, FieldMode.Read) //tx is never full
                                    },
                                    {(long)Registers.RxEmpty, new DoubleWordRegister(this)
                                        .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => Count == 0)
                                    },
                                    {(long)Registers.EventPending, new DoubleWordRegister(this)
                                        // `txEventPending` implements `WriteOneToClear` semantics to avoid fake warnings
                                        // `txEventPending` is generated on the falling edge of TxFull; in our case it means never
                                        .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, valueProviderCallback: _ => false, name: "txEventPending")
                                        .WithFlag(1, out rxEventPending, FieldMode.Read | FieldMode.WriteOneToClear, name: "rxEventPending")
                                        .WithWriteCallback((_, __) => UpdateInterrupts())
                                    },
                                    {(long)Registers.EventEnable, new DoubleWordRegister(this)
                                        .WithFlag(0, name: "txEventEnabled")
                                        .WithFlag(1, out rxEventEnabled)
                                        .WithWriteCallback((_, __) => UpdateInterrupts())
                                    },
                                };
                    
                                registers = new DoubleWordRegisterCollection(this, registersMap);
                            }
                    
                            public uint ReadDoubleWord(long offset)
                            {
                                return registers.Read(offset);
                            }
                    
                            public byte ReadByte(long offset)
                            {
                                if(offset % 4 != 0)
                                {
                                    // in the current configuration, only the lowest byte
                                    // contains a meaningful data
                                    return 0;
                                }
                                return (byte)ReadDoubleWord(offset);
                            }
                    
                            public override void Reset()
                            {
                                base.Reset();
                                registers.Reset();
                    
                                UpdateInterrupts();
                            }
                    
                            public void WriteDoubleWord(long offset, uint value)
                            {
                                registers.Write(offset, value);
                            }
                                
                            public void WriteByte(long offset, byte value)
                            {
                                if(offset % 4 != 0)
                                {
                                    // in the current configuration, only the lowest byte
                                    // contains a meaningful data
                                    return;
                                }
                    
                                WriteDoubleWord(offset, value);
                            }
                            
                            public long Size => 0x100;
                    
                            public GPIO IRQ { get; private set; }
                    
                            public override Bits StopBits => Bits.One;
                    
                            public override Parity ParityBit => Parity.None;
                    
                            public override uint BaudRate => 115200;
                    
                            protected override void CharWritten()
                            {
                                UpdateInterrupts();
                            }
                    
                            protected override void QueueEmptied()
                            {
                                UpdateInterrupts();
                            }
                    
                            private void UpdateInterrupts()
                            {
                                // rxEventPending is latched
                                rxEventPending.Value = (Count != 0);
                    
                                // tx fifo is never full, so `txEventPending` is always false
                                var eventPending = (rxEventEnabled.Value && rxEventPending.Value);
                                IRQ.Set(eventPending);
                            }
                    
                            private IFlagRegisterField rxEventEnabled;
                            private IFlagRegisterField rxEventPending;
                            private readonly DoubleWordRegisterCollection registers;
                    
                            private enum Registers : long
                            {
                                RxTx = 0x0,
                                TxFull = 0x04,
                                RxEmpty = 0x08,
                                EventStatus = 0x0c,
                                EventPending = 0x10,
                                EventEnable = 0x14,
                            }
                        }
                    }
                    

Steps to Set Up a Serial Port

What about Missing Definitions?

  • Most registers are unused
    • Start/Stop bits
    • One-wire mode
    • Infrared mode
  • Most writes can be ignored

Advantages of Emulation

Advantages of Emulation

Robot Framework: Running Tests in CI


                            *** Settings ***
                                Suite Setup                   Setup
                                Suite Teardown                Teardown
                                Test Setup                    Reset Emulation
                                Test Teardown                 Test Teardown
                                Resource                      ${RENODEKEYWORDS}
                                
                            *** Variables ***
                                ${UART}   sysbus.uart0
                                ${URI}    @https://dl.antmicro.com/projects/renode
                                
                                ${LIS2DS12}=     SEPARATOR=
                                ...  """                                                 ${\n}
                                ...  using "platforms/cpus/nrf52840.repl"                ${\n}
                                ...                                                      ${\n}
                                ...  lis2ds12: Sensors.LIS2DS12 @ twi1 0x1c              ${\n}
                                ...  ${SPACE*4}IRQ -> gpio0@28                           ${\n}
                                ...  """

                            *** Keywords ***
                            Create Machine
                                Execute Command  mach create
                                Execute Command  machine 
                                    ... LoadPlatformDescriptionFromString ${LIS2DS12}
                                Execute Command  sysbus LoadELF 
                                    ... ${URI}/nrf52840--zephyr_lis2dh.elf-s_747800-163b7e7cc986d4b1115f06b5f3df44ed0defc1fa

                            *** Test Cases ***
                            Should Read Acceleration
                                Create Machine
                                Create Terminal Tester    ${UART}
                            
                                Execute Command sysbus.twi1.lis2ds12 AccelerationX 10
                                Execute Command sysbus.twi1.lis2ds12 AccelerationY 5
                                Execute Command sysbus.twi1.lis2ds12 AccelerationZ -5
                            
                                Start Emulation
                            
                                Wait For Line On Uart  
                                    ... x 9.997213 , y 4.997410 , z -4.999803
                        

Renode for Reverse Engineering

SVD: Standard Chip Documentation


                        
                        
                            STMicroelectronics
                            ST
                            BlueNRG2
                            BlueNRG1
                            1.0.0
                            ARM 32-bit Cortex-M0 Microcontroller based device, CPU clock up to 32MHz
                            License
                            
                                CM0
                                r0p0
                                little
                                false
                                false
                                2
                                false
                            
                            8
                            32
                            32
                            read-write
                            0x00000000
                            0xFFFFFFFF
                            
                                
                                    RNG
                                    1.0
                                    RNG
                                    RNG
                                    0xB0000000
                                    32
                                    read-write
                                    
                                        0
                                        0x1000
                                        registers
                                    
                                    
                                        
                                            CR
                                            RNG configuration register
                                            0x00
                                            32
                                            read-write
                                            0x00000000
                                            0x0000FFFF
                                            
                                                
                                                    DIS
                                                    Set the state of the random number generator.<ul><li>0: RNG is enable.</li><li>1: RNG is disabled. The internal free-running oscillators are put in power-down mode and the RNG clock is stopped at the input of the block.</li></ul>
                                                    2
                                                    1
                                                    read-write
                                                
                                            
                                        
                                        
                                            SR
                                            RNG status register
                                            0x04
                                            32
                                            read-only
                                            0x00000000
                                            0x0000FFFF
                                            
                                                
                                                    RDY
                                                    New random value ready.<ul><li>0: The RNG_VAL register value is not yet valid. If performing a read access to VAL, the host will be put on hold (by wait-states insertion on the AHB bus) until a random value is available.</li><li>1: The VAL register contains a valid random number.</li></ul>This bit remains at 0 when the RNG is disabled (RNGDIS bit = 1b in CR)
                                                    0
                                                    1
                                                    read-only
                                                
                                            
                                        
                                        
                                            VAL
                                            RNG 16 bit random value
                                            0x08
                                            32
                                            read-only
                                            0x00000000
                                            0x0000FFFF
                                        
                                        
                                
                            
                        
                        

SVD: Using with Renode


                        sysbus ApplySVD @BlueNRG2.svd
                    

Logging Memory Accesses

Debugging with GDB

Creating ELF Files

Multi-System Emulation

Multi-System Emulation