目录
目录README.md

Unified Virtual Peripheral Interface for Simulated Processors

Objective

The current implementation of simulated peripherals in Project 一生一芯 is fragmented and suffers from lower performance. This library establishes a standardized interface for simulated peripherals that supports multiple simulators (NEMU, NPC, etc.), while natively integrating difftest and tracing capabilities. Our goals are:

  • Flexible Interaction: Enable simulated processors to switch between different user interaction methods without code modifications. For example, a virtual serial port could interface through standard I/O, pseudo-terminals (pty), or physical hardware using identical processor-side code.
  • Synchronized Difftesting: Ensure consistent peripheral behavior across multiple simulators during difftesting by guaranteeing all processors receive identical inputs simultaneously.

Core Components

The system comprises five key elements working together:

  • Virtual Bus (Interconnection backbone)
  • IO Frontend (Processor-facing interface)
  • IO Ring Buffer (Data synchronization layer)
  • IO Multiplexer (Coordination manager)
  • IO Backend (Physical/Virtual device handlers)

Input Flow

  1. Frontend Interaction: A simulated processor initiates input through its dedicated IO frontend.
  2. Multiplexer Coordination: The frontend queries the input multiplexer, which checks the shared ring buffer:
    • If data exists: Immediate return from buffer
    • If empty: Backend retrieves new input → stored in buffer → returned to frontend
  3. Synchronization: Multiple processors sharing an input source each maintain individual frontends but share the backend and buffer. The multiplexer ensures all processors receive identical input sequences simultaneously.

Output Flow

  1. Frontend Handling: Processors write outputs through their dedicated output frontend to a private ring buffer.
  2. Multiplexer Verification: The output multiplexer compares all buffers:
    • Consensus: Identical outputs across buffers trigger backend transmission
    • Divergence: Output mismatch triggers difftest error

Key Advantages

  • Decoupled Architecture: Frontend/backend separation enables runtime reconfiguration of I/O channels
  • Deterministic Testing: Input synchronization and output verification ensure reliable difftesting
  • Tracing out of Box: Ring buffers can be directly used as tracing records.

Input Frontends

There are two types of input frontends: timing input frontends and MMIO input frontends.

An MMIO input fronted accepts an MMIO read request from the simulated processor. It is designed to be used with a simulated processor whose bus peripherals are simulated by software. It is an alternative to the MMIO peripherals implementation of NEMU. If there are multiple simulated processors using the MMIO frontend, it can guarantee that each will read the input data from the bus at exactly the same time. The data from an MMIO frontend is always valid unless there is a timing frontend whose input data is not valid.

A timing input frontend generates a waveform. It is designed to be used with a simulated processor whose peripherals are implemented with RTL. It is an alternative to nvboard. If there are multiple simulated processors, only one can use the timing input frontend. The bus and peripherals of a processor using the timing frontend are implemented with RTL and cannot be controlled by libvio. However, to make a difftest possible with IO enabled, each processor core must have input data valid from the bus at exactly the same time, which cannot be guaranteed if more than one processor uses the timing input frontend.

Input Backends

There are two types of input backends: synchronous input backends and asynchronous input backends.

A synchronous input backend is invoked by the input multiplexer when the processor core requests input data from the input frontend. It will block until the requested data is ready. It is the input multiplexer that put the data into the ring buffer if the input frontend is synchronous. An asynchronous input backend, on the other hand, can be invoked by the input multiplexer or run in a separate thread. It is the backend’s responsibility to put the data into the ring buffer if it is asynchronous. The input multiplexer should not write to the ring buffer of an asynchronous input backend, and the input multiplexer should not invoke an asynchronous input backend when the backend is running in a separate thread, to prevent race conditions. The input multiplexer will handle the difference between the two backends, and the input backend need not take care of the difference.

The same type of peripheral of all the simulated processors in a difftest must share the same input backend, since they expect exactly the same input.

Input Multiplexers

When a processor core requests data from the input frontend, the input frontend will pass the request to the input multiplexer, no matter whether the core is querying the availability of an input value or reading an input value. When an input frontend requests data from the input multiplexer, the multiplexer will invoke the corresponding input backend if:

  • There is no new data in the ring buffer and the input backend is synchronous.
  • There is no new data in the ring buffer, the input backend is asynchronous, and the input frontend is not running in a separate thread.

The input multiplexer return “data not ready” if:

  • There is a timing input frontend, and the bus data of it is not valid yet.
  • The input backend is asynchronous and there’s no new data in the ring buffer.

Output Frontends

There are two types of output frontends: timing output frontends and MMIO output frontends.

An MMIO output fronted accepts an MMIO write request from the simulated processor. It is designed to be used with a simulated processor whose bus peripherals are simulated by software. It is an alternative to the MMIO peripherals implementation of NEMU. MMIO output frontends are always ready for output, unless there is a timing backend that is not ready.

A timing output frontend parses a waveform. It is designed to be used with a simulated processor whose peripherals are implemented with RTL. It is an alternative to nvboard. More than one simulated processor using the timing output frontend can coexist, since difftest do not require them to generate the exactly same output at exactly the same time.

Output Backends

There is only one type of output backend. It just reads from an output multiplexer and do the actual output.

Output Multiplexers

An output multiplexer accepts output from frontends. When a frontend writes some data to it, if this frontend is the first one to write this data, the output multiplexer will store this data in the ring buffer and forward this data to the output backend. If some other frontend has already written this data, the output multiplexer will compare the stored data with the new data. If they are different, it will report a difftest error.

Virtual Buses

A virtual bus dispatches MMIO requests from a simulated processor core to multiple MMIO IO frontends. It is the counterpart of mmio_read() and mmio_write() in NEMU.

Virtual Interrupt Controller

The architecture of the virtual interrupt controller and how it is integrated with the IO components is yet under planning.

If there is an RTL processor, all the software emulated processors will receive the same interrupt when the RTL processor receives an interrupt.

If all the processors are software emulated, the interrupts are controlled by virtual peripherals.

关于

An virtual IO interface for simulated processors, designed for difftest support.

81.0 KB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

©Copyright 2023 CCF 开源发展委员会
Powered by Trustie& IntelliDE 京ICP备13000930号