SystemC是一个C++库,用于系统级建模、设计和验证。它扩展了C++,增加了硬件描述语言的功能。
SystemC程序的基本结构包括:
#include <systemc>
// 使用SystemC命名空间
using namespace sc_core;
// 定义模块
SC_MODULE(ModuleName) {
// 端口声明
sc_in<bool> input1;
sc_out<int> output1;
// 内部信号
sc_signal<bool> internal_signal;
// 子模块
SubModule* submodule;
// 构造函数
SC_CTOR(ModuleName) {
// 注册进程
SC_METHOD(process_method);
sensitive << input1; // 敏感列表
// 实例化子模块
submodule = new SubModule("submodule_instance");
// 连接子模块
submodule->port1(signal1);
}
// 进程定义
void process_method() {
// 进程行为
}
};
// 主函数
int sc_main(int argc, char* argv[]) {
// 实例化顶层模块
ModuleName top("top_instance");
// 开始仿真
sc_start(100, SC_NS);
return 0;
}模块是SystemC的基本构建块,类似于硬件设计中的组件:
SC_MODULE(Counter) {
// 模块定义
// ...
};SystemC提供多种端口类型:
sc_in<T>: 输入端口sc_out<T>: 输出端口sc_inout<T>: 双向端口sc_port<IF>: 接口端口
sc_in<bool> clock;
sc_out<sc_uint<8>> data_out;SystemC提供三种不同的进程类型,每种都有其特定的用途和优势。选择正确的进程类型对于准确建模硬件行为至关重要。
特点:
- 不消耗仿真时间
- 每次触发时完整执行,不能暂停
- 执行完后立即返回
- 每次敏感事件发生时重新执行
- 不保存内部执行状态
语法:
SC_MODULE(Example) {
sc_in<bool> clock;
SC_CTOR(Example) {
SC_METHOD(process);
sensitive << clock.pos(); // 对时钟上升沿敏感
}
void process() {
// 执行逻辑代码,不能包含wait()语句
}
};适用场景:
- 组合逻辑电路:如多路复用器、解码器、ALU
- 简单的时序逻辑:如D触发器、寄存器(需要检查时钟边沿)
- 信号监控:任何需要响应信号变化的逻辑
- 高性能仿真:因为SC_METHOD开销最小
例子:在实验中的应用
-
ALU实现:纯组合逻辑,根据操作码产生结果
SC_METHOD(alu_process); sensitive << A << B << op; // 任何输入变化都会触发计算
-
寄存器写入:时序逻辑,在时钟边沿写入数据
SC_METHOD(write_process); sensitive << clk.pos(); // 仅在时钟上升沿触发 void write_process() { if (clk.posedge() && wr_en.read()) { // 需要额外检查是否是上升沿 // 写入数据 } }
特点:
- 可以消耗仿真时间(通过wait()语句)
- 执行过程中可以暂停和恢复
- 仿真开始时只启动一次
- 保留内部执行状态和程序计数器
- wait()之后的代码会在下一次唤醒时继续执行
语法:
SC_MODULE(Example) {
sc_in<bool> clock;
SC_CTOR(Example) {
SC_THREAD(process);
sensitive << clock.pos(); // 初始敏感列表
}
void process() {
// 初始化代码(只执行一次)
while(true) {
// 处理逻辑
wait(); // 暂停执行,等待敏感事件
// wait()后的代码在下次敏感事件时继续执行
}
// 或者使用多种wait形式
wait(10, SC_NS); // 等待特定时间
wait(event); // 等待特定事件
wait(clock.posedge_event()); // 等待时钟上升沿
wait(condition_expr); // 等待条件满足
}
};适用场景:
- 测试平台(Testbench):生成测试向量、验证结果
- 复杂的时序协议:如总线协议、握手机制
- 状态机:具有多个状态和复杂转换
- 行为级建模:描述高层次算法流程
- 异步系统:具有不规则时序行为的系统
例子:在实验中的应用
-
测试平台的激励生成:按顺序生成输入并等待响应
SC_THREAD(test_process); // 不需要初始敏感列表,通过wait()控制 void test_process() { // 初始化 for(int i=0; i<10; i++) { input.write(i); // 设置输入 wait(10, SC_NS); // 等待10ns check_output(); // 检查输出 } sc_stop(); // 测试结束 }
-
复杂状态机:如UART发送器
SC_THREAD(uart_tx); sensitive << clock.pos(); void uart_tx() { while(true) { // 等待发送请求 while(!tx_start.read()) wait(); // 等待下一个时钟 // 发送起始位 tx.write(0); wait(bit_time); // 发送8位数据 for(int i=0; i<8; i++) { tx.write(data.read() & (1<<i)); wait(bit_time); } // 发送停止位 tx.write(1); wait(bit_time); } }
特点:
- SC_THREAD的特殊变种,专为时钟驱动设计
- 仅对指定时钟的特定边沿敏感
- 每个wait()语句自动对应一个时钟周期
- 无需指定敏感列表,直接与时钟边沿绑定
- 最接近硬件描述语言(HDL)的同步设计风格
语法:
SC_MODULE(Example) {
sc_in<bool> clock;
sc_in<bool> reset;
SC_CTOR(Example) {
SC_CTHREAD(process, clock.pos()); // 直接指定时钟边沿
reset_signal_is(reset, true); // 指定复位信号和极性
}
void process() {
// 复位逻辑
variable = 0;
while(true) {
// 处理单个时钟周期的逻辑
output.write(variable);
variable = input.read();
wait(); // 等待下一个时钟周期
}
}
};适用场景:
- 时序电路的RTL级建模:最接近实际硬件设计
- 流水线设计:每个wait()代表一个流水线阶段
- 有限状态机(FSM):时钟同步的状态转换
- 同步顺序电路:寄存器、计数器、移位寄存器
- 综合目标代码:最易转换为可综合的RTL代码
例子:在实验中的应用
-
计数器:时钟驱动的简单计数器
SC_CTHREAD(counter_proc, clock.pos()); reset_signal_is(reset, true); void counter_proc() { // 复位逻辑 count = 0; while(true) { // 输出当前值 count_out.write(count); // 更新计数器 if(enable.read()) count++; wait(); // 等待下一个时钟周期 } }
-
简单CPU执行阶段:每个周期执行一条指令
SC_CTHREAD(execute, clock.pos()); reset_signal_is(reset, true); void execute() { // 复位状态 pc = 0; while(true) { // 取指令 instruction = memory[pc]; // 解码和执行 switch(instruction & 0xF0) { case 0x10: // ADD reg[instruction & 0x0F]++; break; case 0x20: // SUB reg[instruction & 0x0F]--; break; // 更多指令... } // 更新程序计数器 pc++; wait(); // 执行下一条指令 } }
| 特性 | SC_METHOD | SC_THREAD | SC_CTHREAD |
|---|---|---|---|
| 可暂停执行 | 否 | 是 | 是 |
| 支持wait() | 否 | 是 | 是(简化) |
| 执行频率 | 每次敏感事件 | 只启动一次 | 只启动一次 |
| 敏感列表 | 显式指定 | 可动态修改 | 隐式(时钟边沿) |
| 时钟关联 | 手动处理 | 手动处理 | 自动绑定 |
| 状态保存 | 无 | 完整保存 | 完整保存 |
| 资源消耗 | 最低 | 较高 | 较高 |
| 代码复杂性 | 可能高(分散状态) | 中等 | 最低 |
| 典型应用 | 组合逻辑 | 测试平台/协议 | RTL设计 |
| 对应HDL | always_comb | initial块/fork-join | always_ff |
在进行SystemC设计时,关于使用哪种进程类型,可以遵循以下准则:
-
使用SC_METHOD当:
- 建模纯组合逻辑
- 需要对多个信号变化立即响应
- 进程内部不需要保存状态
- 追求最高的仿真性能
-
使用SC_THREAD当:
- 创建测试平台或激励生成器
- 实现具有复杂时序的协议
- 需要灵活控制等待时间或事件
- 行为需要使用wait()暂停和继续
-
使用SC_CTHREAD当:
- 实现时钟驱动的同步设计
- 创建可能需要综合的RTL代码
- 建模流水线或多阶段逻辑
- 设计与时钟紧密绑定的状态机
选择适当的进程类型不仅影响代码清晰度,还直接影响仿真性能和模型的准确性.
SystemC提供多种特殊数据类型:
sc_bit: 单比特sc_bv<W>: 比特向量sc_int<W>,sc_uint<W>: 有符号和无符号整数sc_bigint<W>,sc_biguint<W>: 大整数类型sc_fixed<W,I>,sc_ufixed<W,I>: 定点数
sc_uint<8> counter; // 8位无符号整数
sc_int<16> value; // 16位有符号整数
sc_bv<32> bus; // 32位比特向量信号用于模块间通信:
sc_signal<bool> control_signal;
sc_signal<sc_uint<8>> data_bus;// 读信号
bool value = signal.read();
// 写信号
signal.write(new_value);// 运行特定时间
sc_start(100, SC_NS);
// 无限运行直到停止
sc_start();sc_stop(); // 停止仿真sc_time current_time = sc_time_stamp(); // 获取当前仿真时间
sc_time delay(10, SC_NS); // 10纳秒延迟sc_trace_file* tf = sc_create_vcd_trace_file("wave");
sc_trace(tf, signal, "signal_name"); // 跟踪信号SC_REPORT_INFO("ID", "Information message");
SC_REPORT_WARNING("ID", "Warning message");
SC_REPORT_ERROR("ID", "Error message");
SC_REPORT_FATAL("ID", "Fatal error message");对于熟悉Verilog/SystemVerilog的工程师,以下对照表可帮助理解SystemC概念。
| 概念 | SystemC | Verilog/SystemVerilog |
|---|---|---|
| 模块定义 | SC_MODULE(ModuleName) { ... } |
module ModuleName(...); ... endmodule |
| 端口声明 | sc_in<bool> clk; |
input logic clk; |
sc_out<sc_uint<8>> data; |
output logic [7:0] data; |
|
sc_inout<bool> io_pin; |
inout logic io_pin; |
|
| 信号/线网 | sc_signal<bool> sig; |
logic sig; 或 wire sig; |
| 参数/常量 | static const int WIDTH = 8; |
parameter WIDTH = 8; |
| 敏感列表 | sensitive << clk.pos(); |
@(posedge clk) |
| 模块实例化 | SubModule* sub = new SubModule("sub"); |
SubModule sub(...); |
| 连线 | sub->in(sig); |
.in(sig) |
| 时间单位 | SC_NS, SC_PS |
ns, ps |
| 主函数 | int sc_main(int argc, char* argv[]) { ... } |
(没有直接对应概念,仿真器自动执行) |
| SystemC | SystemVerilog |
|---|---|
bool |
logic |
sc_bit |
bit |
sc_bv<N> |
bit [N-1:0] |
sc_uint<N> |
logic unsigned [N-1:0] |
sc_int<N> |
logic signed [N-1:0] |
sc_bigint<N>, sc_biguint<N> |
(超过64位的大整数,没有直接对应) |
sc_fixed<W,I> |
(需要使用实数和转换函数模拟) |
| 功能 | SystemC | SystemVerilog |
|---|---|---|
| 组合逻辑 | SC_METHOD(process); sensitive << in1 << in2; |
always_comb begin ... end |
| 时序逻辑 | SC_METHOD(process); sensitive << clk.pos(); |
always_ff @(posedge clk) begin ... end |
| 过程延迟 | wait(10, SC_NS); |
#10; |
| 条件等待 | wait(condition_event); |
wait(condition); |
| 变量赋值 | var = value; |
var = value; |
| 信号赋值 | signal.write(value); |
signal <= value; 或 assign signal = value; |
| 信号读取 | value = signal.read(); |
value = signal; |
| SystemC | SystemVerilog |
|---|---|
SC_METHOD(process) |
always_comb 或 always @(*) |
SC_CTHREAD(process, clk.pos()) |
always_ff @(posedge clk) |
SC_THREAD(process) |
使用initial begin ... end和fork...join的组合 |
| 概念 | SystemC | SystemVerilog |
|---|---|---|
| 并发执行 | 多个SC_METHOD进程并行执行 |
多个always块并行执行 |
| 同步控制 | wait(), notify(), event |
wait fork, disable fork |
| 事件同步 | sc_event evt; evt.notify(); |
没有直接对应,使用信号变化 |
| 延时 | wait(10, SC_NS); |
#10; |
| 概念 | SystemC | SystemVerilog |
|---|---|---|
| 基本接口 | 端口和信号 | 端口和线网 |
| 分层通信 | 通道、接口、端口 | 接口、通道、任务/函数 |
| FIFO | sc_fifo<T> |
需自行实现或使用类 |
| 回调 | 事件通知和事件查找器 | 任务调用、fork-join |
| 功能 | SystemC | SystemVerilog |
|---|---|---|
| 启动仿真 | sc_start(); |
(由仿真器控制) |
| 结束仿真 | sc_stop(); |
$finish; |
| 打印调试 | std::cout << msg; |
$display("%s", msg); |
| 转储波形 | sc_trace(file, signal, name); |
$dumpvars; |
| 时间控制 | sc_time_stamp() |
$time |
| 概念 | SystemC | SystemVerilog |
|---|---|---|
| 测试台 | 自定义C++类 | module testbench |
| 激励生成 | C++函数或类方法 | 任务和函数 |
| 检查结果 | C++ if/else 或断言 |
assert 语句 |
| 覆盖率 | 需额外库支持 | 内置覆盖率指令 |
| 随机化 | C++标准库或自定义 | 内置约束随机化 |
-
抽象级别:
- SystemC:更高级,面向对象,适合系统级和算法级建模
- SystemVerilog:更贴近硬件,适合RTL和门级建模
-
语法风格:
- SystemC:C++语法,使用
.和->访问成员 - SystemVerilog:HDL语法,使用
.访问成员
- SystemC:C++语法,使用
-
编译与执行:
- SystemC:需要C++编译器,生成可执行文件
- SystemVerilog:需要HDL仿真器直接解释执行
-
扩展能力:
- SystemC:可以利用全部C++特性
- SystemVerilog:语言扩展受限于HDL规范
例1:简单计数器
SystemC:
SC_MODULE(Counter) {
sc_in<bool> clk;
sc_in<bool> rst;
sc_out<sc_uint<8>> count;
sc_uint<8> counter;
SC_CTOR(Counter) {
SC_METHOD(process);
sensitive << clk.pos() << rst;
counter = 0;
}
void process() {
if (rst.read()) {
counter = 0;
} else if (clk.pos()) {
counter++;
}
count.write(counter);
}
};SystemVerilog:
module Counter(
input logic clk,
input logic rst,
output logic [7:0] count
);
always_ff @(posedge clk or posedge rst) begin
if (rst)
count <= 8'd0;
else
count <= count + 1'b1;
end
endmodule例2:简单状态机
SystemC:
SC_MODULE(FSM) {
sc_in<bool> clk;
sc_in<bool> rst;
sc_in<bool> input;
sc_out<bool> output;
enum State { S0, S1, S2 };
State state;
SC_CTOR(FSM) {
SC_METHOD(state_logic);
sensitive << clk.pos() << rst;
state = S0;
}
void state_logic() {
if (rst.read()) {
state = S0;
output.write(false);
} else if (clk.pos()) {
switch (state) {
case S0:
state = input.read() ? S1 : S0;
output.write(false);
break;
case S1:
state = input.read() ? S2 : S0;
output.write(false);
break;
case S2:
state = input.read() ? S2 : S0;
output.write(true);
break;
}
}
}
};SystemVerilog:
module FSM (
input logic clk,
input logic rst,
input logic input_signal,
output logic output_signal
);
typedef enum logic [1:0] {S0, S1, S2} state_t;
state_t state;
always_ff @(posedge clk or posedge rst) begin
if (rst) begin
state <= S0;
output_signal <= 1'b0;
} else begin
case (state)
S0: begin
state <= input_signal ? S1 : S0;
output_signal <= 1'b0;
end
S1: begin
state <= input_signal ? S2 : S0;
output_signal <= 1'b0;
end
S2: begin
state <= input_signal ? S2 : S0;
output_signal <= 1'b1;
end
endcase
end
end
endmodule