corosim is a C++20 coroutine-based co-simulation engine for Verilator. It maps co_await to delta-cycle and timed-event scheduling, letting you write concurrent hardware testbenches without manual state machines or eval loops.
- Coroutine concurrency —
co_await posedge(),co_await delay(),co_await any() - Non-blocking signals —
sig.next(val)with delta-cycle scheduling - Trigger-based processes —
sim.always()for repeating triggers,sim.instance()for one-shot coroutines - Phase-aligned I/O —
sim.sample()/sim.drive()for BFM-style transactions - Software signals — cross-process sync without DUT binding
# macOS (with Homebrew)
brew install verilator
# Ubuntu/Debian
sudo apt-get install verilator g++ makegit clone https://github.com/dozecat/corosim.git
cd corosim/examples/async_fifo/sim/tb
make # Compile and run simulationgtkwave waveform.vcdcorosim/
├── src/ # Library source
│ ├── corosim.hpp # Umbrella header
│ ├── core/ # Kernel, Sim<TOP>, Dut
│ ├── signal/ # Signal<T>, wide, helpers
│ ├── trigger/ # Edge, delay, any waiters
│ ├── process/ # Task, Process, ProcessManager
│ └── scheduler/ # Delta-cycle + timed event engine
└── examples/
├── async_fifo/ # Dual-clock async FIFO (basic)
└── axis_async_fifo/ # AXI4-Stream async FIFO (advanced, with BFM)
Most testbenches interact through Sim<TOP>, which owns a Kernel and provides signal binding, process registration, and clock generation:
#include "corosim.hpp"
using namespace corosim;
Vasync_fifo top;
Sim sim(top); // wrap the Verilator top
auto& clk = sim.sig(top.clk); // bind a DUT signal
auto& rst = sim.sig<uint8_t>(); // software-only signal (no DUT)
sim.clock(clk, 10); // toggle clk every 5 time units
sim.run(500, [&](sim_time t) { tfp.dump(t); });For pointer-to-member binding, use Dut<TOP>:
Dut<TOP> dut(top, sim.signals());
auto& clk = dut.sig(&TOP::clk);
auto& rst = dut.sig(&TOP::rst);Signal<uint8_t> sig(&top->sig_field); // bind to a Verilator signal
Signal<bool> flag; // software-only (no binding)
sig.next(val); // non-blocking assignment (applied at delta end)
sig.read(); // read current value
auto v = sig; // implicit read
// Mapping from Verilator types:
Signal<uint8_t> rst(&top.rst); // 1-bit → CData
Signal<uint8_t> data(&top.data); // 8-bit → CData
Signal<uint16_t> wide(&top.wide); // 16-bit → SDataFor wide vectors (VlWide<N>), use Signal<VlWide<N>> — see signal/wide.hpp.
| Trigger | Type constraint | sim.always() |
co_await |
|---|---|---|---|
posedge(sig) |
sizeof(T) <= 1 |
✓ | ✓ |
negedge(sig) |
sizeof(T) <= 1 |
✓ | ✓ |
change(sig) |
any Signal<T> |
✓ | ✓ |
delay(n) |
— | ✓ | ✓ |
posedge/negedge require 1-byte signal types; change works with any signal width.
// Auto-repeat on trigger
sim.always(posedge(clk), [&] { cnt.next(cnt.read() + 1); });
sim.always(delay(5), [&] { clk.next(!clk.read()); });
// One-shot coroutine (lambda)
sim.instance([&]() -> Task {
co_await delay(30);
go.next(true);
});
// One-shot coroutine (standalone function)
sim.instance(reset, &rst, &wr_clk);Coroutine functions support co_await for synchronization:
Task reset(Signal<uint8_t>* rst, Signal<uint8_t>* clk) {
rst->next(1);
for (int i = 0; i < 5; i++) co_await posedge(*clk);
rst->next(0);
}
co_await delay(30); // wait 30 time units
co_await posedge(clk); // wait for rising edge
// Multi-condition wait — returns winning trigger index
int w = co_await any(posedge(clk), delay(200));
// w == 0: posedge(clk) fired first
// w == 1: delay(200) fired firstFor phase-aligned I/O, sample runs before DUT eval and drive runs after:
sim.sample(posedge(clk), [&] { bfm.sample_inputs(); });
sim.drive(posedge(clk), [&] { bfm.drive_outputs(); });Dual-clock asynchronous FIFO testbench demonstrating basic triggers, software signals, coroutine processes, and VCD dump.
cd examples/async_fifo/sim/tb
makeAXI4-Stream async FIFO testbench using BFM with sample/drive phase hooks. Frame-fifo mode only (aux FIFO stores per-frame side-channel signals; bad frames are detected and discarded).
cd examples/axis_async_fifo/sim/tb
make- C++20 (coroutines)
- Verilator 5.x
- Make
MIT License — see LICENSE.
corosim 是一个基于 C++20 协程的 Verilator 协同仿真引擎。它将 co_await 映射为 delta 周期和定时事件调度,用于编写并发硬件测试台。
- 协程并发 —
co_await posedge()、co_await delay()、co_await any() - 非阻塞信号 —
sig.next(val),delta 周期调度 - 触发进程 —
sim.always()重复触发,sim.instance()一次性协程 - 相位 I/O —
sim.sample()/sim.drive()用于 BFM 总线事务 - 软件信号 — 无需 DUT 绑定的跨进程同步
# macOS (使用 Homebrew)
brew install verilator
# Ubuntu/Debian
sudo apt-get install verilator g++ makegit clone https://github.com/dozecat/corosim.git
cd corosim/examples/async_fifo/sim/tb
make # 编译并运行仿真gtkwave waveform.vcdcorosim/
├── src/ # 库源码
│ ├── corosim.hpp # 总头文件
│ ├── core/ # 引擎核心、Sim<TOP>、Dut
│ ├── signal/ # Signal<T>、wide、helpers
│ ├── trigger/ # 边沿、延时、多条件等待
│ ├── process/ # Task、Process、ProcessManager
│ └── scheduler/ # delta 周期 + 定时事件调度
└── examples/
├── async_fifo/ # 双时钟异步 FIFO(基础)
└── axis_async_fifo/ # AXI4-Stream 异步 FIFO(高级,带 BFM)
大部分测试台通过 Sim<TOP> 与引擎交互:
#include "corosim.hpp"
using namespace corosim;
Vasync_fifo top;
Sim sim(top);
auto& clk = sim.sig(top.clk); // 绑定 DUT 信号
auto& rst = sim.sig<uint8_t>(); // 纯软件信号
sim.clock(clk, 10); // 每 5 时间单位翻转 clk
sim.run(500, [&](sim_time t) { tfp.dump(t); });使用 Dut<TOP> 进行成员指针绑定:
Dut<TOP> dut(top, sim.signals());
auto& clk = dut.sig(&TOP::clk);
auto& rst = dut.sig(&TOP::rst);Signal<uint8_t> sig(&top->sig_field); // 绑定 Verilator 信号
Signal<bool> flag; // 纯软件信号
sig.next(val); // 非阻塞赋值(delta 结束时提交)
sig.read(); // 读当前值
auto v = sig; // 隐式读取
// Verilator 类型映射:
Signal<uint8_t> rst(&top.rst); // 1-bit → CData
Signal<uint8_t> data(&top.data); // 8-bit → CData
Signal<uint16_t> wide(&top.wide); // 16-bit → SData宽向量(VlWide<N>)使用 Signal<VlWide<N>>。
| 触发器 | 类型限制 | sim.always() |
co_await |
|---|---|---|---|
posedge(sig) |
sizeof(T) <= 1 |
✓ | ✓ |
negedge(sig) |
sizeof(T) <= 1 |
✓ | ✓ |
change(sig) |
任意 Signal<T> |
✓ | ✓ |
delay(n) |
— | ✓ | ✓ |
posedge/negedge 仅支持 1 字节信号;change 支持任意宽度。
// 触发重复执行
sim.always(posedge(clk), [&] { cnt.next(cnt.read() + 1); });
sim.always(delay(5), [&] { clk.next(!clk.read()); });
// 一次性协程 (lambda)
sim.instance([&]() -> Task {
co_await delay(30);
go.next(true);
});
// 一次性协程 (独立函数)
sim.instance(reset, &rst, &wr_clk);协程函数中使用 co_await 同步:
Task reset(Signal<uint8_t>* rst, Signal<uint8_t>* clk) {
rst->next(1);
for (int i = 0; i < 5; i++) co_await posedge(*clk);
rst->next(0);
}
co_await delay(30);
co_await posedge(clk);
// 多条件等待 — 返回先触发的索引
int w = co_await any(posedge(clk), delay(200));
// w == 0: posedge(clk) 先到
// w == 1: delay(200) 先到sample 在 DUT eval 前执行,drive 在 eval 后执行:
sim.sample(posedge(clk), [&] { bfm.sample_inputs(); });
sim.drive(posedge(clk), [&] { bfm.drive_outputs(); });双时钟异步 FIFO 测试,演示基本触发、软件信号、协程进程和 VCD 波形。
cd examples/async_fifo/sim/tb
makeAXI4-Stream 异步 FIFO 测试,使用 BFM + sample/drive 相位钩子。仅 frame-fifo 模式(aux FIFO 存储帧级 side-channel 信号,检测并丢弃坏帧)。
cd examples/axis_async_fifo/sim/tb
make- C++20(协程支持)
- Verilator 5.x
- Make
MIT License — 见 LICENSE。