Designing modular and reusable RISC-V SoCs with Topwrap and Guineveer

Published:

Topics: Open FPGA, Open hardware, Open source tools

Designing a System-on-Chip (SoC) typically involves a lot of reuse, as CPU cores and I/O peripherals are rarely built completely from scratch, but rather gradually enhanced and integrated into different configurations.

In an article from last year Antmicro introduced its versatile, extendible and user-friendly framework for digital design aggregation called Topwrap, which lets you assemble ASIC designs from building blocks. Since that time, Topwrap’s capabilities to develop System-on-Chip designs was vastly improved, with a number of features supporting module-based flows and managing interconnects.

In this article, we take a look at how you can use Topwrap together with the Guineveer reference SoC (which includes CHIPS Alliance’s VeeR EL2 RISC-V Core) as a base to generate, expand, and test your own custom SoC, with the help of integrated open source tools, such as Renode and Verilator.

Guineveer sample SoC design - main

Guineveer: a RISC-V reference SoC design

Guineveer is a simple and extensible reference design based on the RISC-V VeeR EL2 core, meant to serve as a baseline for creating custom SoCs.

VeeR EL2 is a 32-bit RISC-V core maintained by Antmicro and hosted by CHIPS Alliance, a fund of the Linux Foundation focused on open source silicon IP and tools. It’s a low-power, in-order, and highly configurable core with optional features, such as closely coupled memories, CPU instruction cache, branch prediction, physical memory protection, user mode, and Dual-Core Lockstep.

On top of VeeR itself, Guineveer also integrates several open source cores developed by Antmicro, lowRISC/OpenTitan, and ETH Zurich’s PULP project, in a sample SoC design that can be modified and expanded with new peripherals. It currently consists of the following components:

Guineveer - SoC design in Pipeline Manager

Guineveer’s design is available in Topwrap’s YAML format, which describes used modules, their parameters, and how they are connected to each other. Topwrap enables you to modify the design rapidly by editing the parameters of existing components, or by easily adding and connecting new ones. You can, of course, also modify the SoC design as usual, by editing the YAML description file.

The Guineveer SoC architecture uses the following YAML description:

design:
  name: guineveer
  parameters:
    # ...
  interfaces:
    axi_bridge:
      axi: [axi_interconnect, uart]
    axi_interconnect:
      veer_ifu: [rvtop_wrapper, ifu_axi]
      veer_lsu: [rvtop_wrapper, lsu_axi]
    uart_core:
      ahb: [axi_bridge, ahb]
    lmem:
      s_axi_sram: [axi_interconnect, mem]
    rvtop_wrapper:
      el2_mem_export: [rvtop_wrapper, el2_icache_export]
  ports:
    i_axi_cdc_lsu:
      src_clk_i: clk_i
      src_rst_ni: rst_ni
   # ...

For FPGA emulation, Guineveer currently targets mid-range Artix-7 FPGAs from AMD, including the Nexys Video and Arty A7-100T boards. In its current form, the Guineveer FPGA design takes up about 40% of the available logic cells on Arty A7-100T.

Guineveer - board targets

Testing the new SoC design

All the components, taken individually, have their own test suites. However, in a broader scope, a new SoC design requires testing in its entirety.

Guineveer implements a regression test suite, together with unit tests for individual IP cores and Integration Tests (System-Level Tests) that verify the complete SoC functionality (e.g., booting an operating system, running benchmark applications). The framework supports running tests in both Renode and Verilator and on actual FPGA hardware, to validate real-world behavior.

Additionally, you can conveniently plan tests for your own SoC designs with the open source Testplanner which is being continuously extended to support more advanced DV tracking flows.

Renode tests and co-simulation

Guineveer includes platform description logic for simulations in Renode, which facilitates the testing of software written for the platform you are designing. Below, you can find a simple implementation of a print command, based on the OpenTitan UART, which you can use to perform a baseline test.

memory0: Memory.MappedMemory @ sysbus 0x80000000
    size: 0x10000000

cpu0: CPU.VeeR_EL2 @ sysbus
    hartId: 0

memory1: Memory.MappedMemory @ sysbus 0x90000000
    size: 0x10000000

uart: UART.OpenTitan_UART @ sysbus 0x30000000

i3c: CoSimulated.CoSimulatedPeripheral @ sysbus \
  <0x30001000, +0x1000>
    limitBuffer: 50000
    timeout: 500000
    address: "127.0.0.1"
    cosimToRenodeSignalRange: <0, +5>

Using Renode allows you to quickly develop new tests for your platform, as the functional simulation executes significantly faster than RTL-based simulation.

There is also an option to run a Renode co-simulation setup for Guineveer, where a specific Renode model of a peripheral can be replaced with a from-RTL instance of the actual core simulated via Verilator, for testing of enhancements in specific IP blocks.

Guineveer tests log

Example test suites

Guineveer includes two extendible test suites to verify your SoC design. There is a standard SystemVerilog testbench and a Cocotb testbench written in Python. These testbenches capture logs printed over the (emulated) UART, manage timeouts, and use a mechanism for the CPU to notify the testbench that a test is over. Such a flow enables you to receive a test log in a regular console, in the same way the log would be produced by the program running on the real hardware, for example:

VerilatorTB: Start of sim

mem_mailbox = 80f80000
[UART MONITOR]: Hello UART

Finished : minstret = 7119, mcycle = 31087
See "exec.log" for execution trace with register updates..

VerilatorTB: End of sim

Extending Guineveer to include an I3C core

Basing on Topwrap makes it very easy to extend Guineveer with additional I/O cores for interfaces such as I3C (which is now included by default). The open source I3C core included in Guineveer implements the I3C target functionality of the I3C bus protocol (laid out by the MIPI Alliance), and is actively developed by Antmicro and partners within the CHIPS Alliance. The core supports Open Compute Project (OCP)’s recovery flow.

Intended to be an upgrade over I2C, I3C offers many improvements while maintaining some backward compatibility with existing I2C targets. Examples of I3C use cases include DDR5 memory modules and PCI Express device management. It’s also used in CHIPS’ Caliptra Root-of-Trust project, where Antmicro collaborates with project partners AMD, Google, Microsoft, and Nvidia, as well as other Caliptra adopters.

Including I3C in the Guineveer SoC reference design required instantiating the core, specifying the interfaces, and making connections that include clk and rst. In a traditional development flow, the I3C core, in combination with the AXI interface, requires a lot of interconnects to be designed manually, which can be quite challenging and time-consuming.

The improved Topwrap capabilities (which will be described in a separate blog article) automates this process by parsing the SystemVerilog source files to recognize the I3C core, with its signals and interfaces, allowing to reference it conveniently in the design description file, without having to describe the core manually. For example, multiple signals from the AXI interface are substituted with a single connection in Topwrap. As mentioned before, Topwrap also prevents errors, invalidating any erroneous connections it detects.

Besides editing the YAML description manually or via the API-based CLI, you can also simply load the Guineveer YAML file into the Topwrap GUI interfaces, then use the blocks and parameters available in the GUI to modify the SoC to your purpose.

VSD iframe

(For an interactive version of the diagram, visit the desktop version of the website)

Here is how Topwrap modifies the design description (YAML file) to include the I3C core on the AXI interface:

design:
  name: guineveer
  parameters:
    # ...
  interfaces:
    i_axi_cdc_lsu:
      s_axi_src: [axi_interconnect, i3c]
    axi_bridge:
      axi: [axi_interconnect, uart]
    axi_interconnect:
      veer_ifu: [rvtop_wrapper, ifu_axi]
      veer_lsu: [rvtop_wrapper, lsu_axi]
    i3c_core:
      axi: [i_axi_cdc_lsu, m_axi_dst]
    uart_core:
      ahb: [axi_bridge, ahb]
    lmem:
      s_axi_sram: [axi_interconnect, mem]
    rvtop_wrapper:
      el2_mem_export: [rvtop_wrapper, el2_icache_export]
  ports:
    i_axi_cdc_lsu:
      src_clk_i: clk_i
      src_rst_ni: rst_ni
      dst_clk_i: i3c_clk_i
      dst_rst_ni: i3c_rst_ni
   # ...

As mentioned, you can produce the same result by modifying the YAML files manually or via the console/programmatic API, but the GUI is a convenient way to get a graphical overview of all the connections.

Adding I3C tests: OCP recovery boot flow

OCP has created a standard describing the recovery process for a failed or compromised device. In this standard, I3C can be used as a bus to implement the recovery flow.

One of the supported boot flows is streaming boot, during which a recovery agent sends a firmware image to be booted to the device. The tests for the recovery flow are run in a simulation, where Cocotb is used again, similar to other tests involving I3C communication. In this scenario, an implementation of the recovery interface protocol is provided by Antmicro’s Cocotbext-i3c extension, and the test implements a recovery agent.

The Cocotb recovery agent test waits for the I3C core to enter recovery mode, then streams the image through the I3C core to the bootloader running on the main CPU, the Veer EL2 core, to complete the recovery. The Cocotb test then checks UART for the message that confirms that the new image works and recovery has been completed.

Testing the I3C streaming boot on an FPGA requires additional hardware to act as the recovery agent. To that end, we have used an NXP i.MX RT685 evaluation board, which implements the recovery agent that uses the I3C core’s recovery boot flow by running Antmicro’s custom Zephyr application.

Guineveer tests log

Automated design of custom SoCs with Antmicro

Thanks to the improvements in Antmicro’s open source digital design flow based around Topwrap and a readily-available example of Guineveer, there’s an easy entry point to designing a custom, RISC-V-based SoC, including a complex bus system and multiple interconnected IP blocks.

If you would like Antmicro to help you adopt Topwrap to design a custom SoC and automate the generation of interconnects, as well as develop extensive tests of your new SoC design, contact us at contact@antmicro.com to discuss your requirements.

See Also: