快速 SystemC 之旅(一)

  • 一、前言背景
  • 二、实验环境
    • 1. 安装步骤
    • 2. 验证安装
  • 三、RTL 级硬件描述
    • 1. 初看模块
    • 2. 二输入与非门


一、前言背景

因项目需求,近期开始开展电子系统级设计(ESL)进行事务级建模(TLM),方案上选择了 NVIDIA 的开源架构 NVDLA 。
NVDLA
NVDLA 基于 GreenSocs 的 QBOX 框架,也是一种 QEMU 和 SystemC 联合仿真的解决方案。为学习该开源框架,为深入理解该开源架构,笔者开始系统学习 QEMU 与 SystemC。本文为学习 SystemC 过程中记录的笔记,一方面便于日后查阅,另一方面也希望对想要学习相关知识的朋友一些帮助。

实验环境基于 NVDLA 框架适配版本:SystemC 2.3.0,使用 C++11 标准编译。

二、实验环境

本节介绍 SystemC 2.3.0 的安装过程。SystemC 本质上是一个基于 C++ 的建模扩展库。

1. 安装步骤

使用以下命令完成 SystemC 2.3.0 的下载、编译与安装:

sudo wget -O systemc-2.3.0a.tar.gz http://www.accellera.org/images/downloads/standards/systemc/systemc-2.3.0a.tar.gz
tar -xzvf systemc-2.3.0a.tar.gz
cd systemc-2.3.0a
sudo mkdir -p /usr/local/systemc-2.3.0/
mkdir objdir
cd objdir
../configure --prefix=/usr/local/systemc-2.3.0
make
sudo make install

2. 验证安装

安装完成后,通过编写一个简单的 HelloWorld 项目验证环境配置是否成功。

项目结构

.
├── CMakeLists.txt
└── main.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.12)# === 项目信息 ===
set(CMAKE_PROJECT_NAME demo_systemc)project(${CMAKE_PROJECT_NAME} VERSION 2025.06.13 LANGUAGES C CXX
)# === 构建类型 ===
if(NOT CMAKE_BUILD_TYPE)set(CMAKE_BUILD_TYPE Debug)
endif()# === 编译配置 ===
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")# === 可执行目标 ===
add_executable(${CMAKE_PROJECT_NAME})# === 打印信息 ===
message(STATUS "[Build type]        :           ${CMAKE_BUILD_TYPE}")
message(STATUS "[C++ Standard]      :           C++${CMAKE_CXX_STANDARD}")
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_UPPER)
set(_CXXFLAGS "CMAKE_CXX_FLAGS_${BUILD_TYPE_UPPER}")
message(STATUS "[Compiler Flags]    :           ${${_CXXFLAGS}}")# === SYSTEMC库 ===
set(SYSTEM_HOME /usr/local/systemc-2.3.0)
set(SYSTEMC_INCLUDE_DIR ${SYSTEM_HOME}/include)
set(SYSTEMC_LIBRARY ${SYSTEM_HOME}/lib-linux64/libsystemc.a)# === 源文件 ===
file(GLOB SOURCES CONFIGURE_DEPENDS${PROJECT_SOURCE_DIR}/*.cpp
)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE${SOURCES}
)# === 头文件目录 ===
target_include_directories(${CMAKE_PROJECT_NAME}PRIVATE${SYSTEMC_INCLUDE_DIR}
)# === 宏定义 ===
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE# Add user defined symbols
)# === 链接库 ===
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE# Add user defined library search paths
)
target_link_libraries(${CMAKE_PROJECT_NAME}PRIVATE${SYSTEMC_LIBRARY}
)

main.cpp:

#include <systemc.h>SC_MODULE(Hello)
{SC_CTOR(Hello){SC_METHOD(run);}void run(){std::cout << "Hello SystemC" << std::endl;}
};int sc_main(int, char **)
{Hello hello("hello");sc_start();return 0;
}

编译与运行

执行以下命令完成编译与运行:

cmake . -B build/
cmake --build build/
./build/demo_systemc

运行结果如下,表明 SystemC 配置成功:

SystemC 2.3.0-ASI --- May 20 2025 10:22:36Copyright (c) 1996-2012 by all Contributors,ALL RIGHTS RESERVEDHello SystemC

三、RTL 级硬件描述

SystemC 目前能够描述包括门级在内所有更高抽象级别的数字逻辑电路和软件,在后续内容中,我们也主要讨论基于 SystemC 的 RTL 级以上的硬件描述。但笔者认为 RTL 级建模是一个非常适合作为入门的 Demo,因此本节将用 RTL 级的实例帮助大家快速了解 SystemC 的基本语法与建模风格。

1. 初看模块

回顾我们之前编写的 HelloWorld 示例,这段简洁的代码已经体现出 SystemC 作为硬件描述语言的基本特征:其核心建模单元是模块(SC_MODULE)。

SC_MODULE 是 SystemC 中的基本构建块,用于表示一个硬件组件。其本质是一个宏定义,展开后为继承自 sc_core::sc_module 的类(C++ 中,struct 的默认访问类型是 publicclass 的默认访问类型是 private):

#define SC_MODULE(user_module_name)                                           \struct user_module_name : ::sc_core::sc_module

模块的构造函数通常通过 SC_CTOR 宏定义,该宏除了定义构造函数外,还进行了一些类型声明:

#define SC_CTOR(user_module_name)                                             \typedef user_module_name SC_CURRENT_USER_MODULE;                          \user_module_name( ::sc_core::sc_module_name )

其中的 typedef 语句也可以通过单独的 SC_HAS_PROCESS 宏显式声明:

#define SC_HAS_PROCESS(user_module_name)                                      \typedef user_module_name SC_CURRENT_USER_MODULE

SC_HAS_PROCESS 用于注册 SystemC 的进程(如 SC_METHODSC_THREADSC_CTHREAD)到仿真内核。
例如,SC_METHOD(func) 用于将 func() 注册为一个敏感事件触发的仿真进程。

SystemC 提供了多个宏用于简化模块定义过程。根据模块的构造方式和进程类型,选择合适的宏可使代码更简洁:

  • 如果模块不包含仿真进程,无需使用 SC_CTORSC_HAS_PROCESS
  • 如果模块构造函数仅包含 sc_module_name,使用 SC_CTOR 即可。
  • 如果模块构造函数包含额外参数,应使用 SC_HAS_PROCESS 并自行定义构造函数。

以下是一个带额外参数和仿真进程的模块示例:

SC_MODULE(module) {const int i;SC_HAS_PROCESS(module);module(sc_module_name name, int i) : i(i) {SC_METHOD(func);}void func() {cout << name() << ", addithonal input argument" << endl;}
};

这里我们仅做简要介绍,意在带大家快速上手,后续章节我们将进一步介绍 SystemC 中模块、进程与敏感列表等关键概念。

2. 二输入与非门

以一个典型的门级电路——二输入与非门(NAND2)为例,展示 SystemC 在 RTL 级硬件建模中的基本用法。这可以更好的类比于其他的硬件描述语言,有助于大家快速建立认知。为简洁起见,一些语法细节将于后续章节展开讨论。

Nand2.h:

#ifndef NADN2_H
#define NADN2_H#include <systemc.h>SC_MODULE(Nand2)
{sc_in<bool>  input_a;    sc_in<bool>  input_b;sc_out<bool> output_x;   SC_CTOR(Nand2){SC_METHOD(do_nand);sensitive << input_a << input_b;}
private:void do_nand(){output_x.write(!(input_a.read() && input_b.read()));}
}; /* Nand2 */#endif /* NADN2_H */

该模块实现了一个二输入与非门:

  • input_ainput_bbool 型输入端口。
  • output_xbool 型输出端口。
  • 成员函数 do_nand() 作为一个 SC_METHOD 进程被注册,其敏感列表包含 input_ainput_b,即在任一输入变化时触发该进程完成对输出信号 output_x 的求值。

为验证功能正确性,我们编写一个测试模块(Testbench):

tb/TB_Nand2.h:

#ifndef TB_NADN2_H
#define TB_NADN2_H#include <systemc.h>SC_MODULE(TB_Nand2)
{sc_out<bool> output_a;sc_out<bool> output_b;sc_in<bool>  input_x;sc_in_clk    clk;SC_CTOR(TB_Nand2){SC_CTHREAD(gen_input, clk.pos());SC_METHOD(display_variable);sensitive << input_x << output_a << output_b;dont_initialize();}private:void gen_input(){wait();output_a.write(0); output_b.write(0);wait();output_a.write(0); output_b.write(1);wait();output_a.write(1); output_b.write(0);wait();output_a.write(1); output_b.write(1);wait(100);}void display_variable(){cout << "A = " << output_a.read() << " "<< "B = " << output_b.read() << " "<< "X = " << input_x.read() << endl;}}; /* tb_nand2 */#endif /* TB_NADN2_H */

该测试模块中包含:

  • 一个 SC_CTHREAD 进程 gen_input() 依时钟 clk.pos() 生成所有输入组合。
  • 一个 SC_METHOD 进程 display_output() 用于输出当前输入与输出状态。
  • dont_initialize() 意味着不要在仿真零时刻对 SC_METHOD 类型进程 display_output() 初始化。如果不使用该语句,运行结果将会出现 A = 0 B = 0 X = 0,这将不符合预期。

有了验证程序,我们就可以在顶层模块 Top 中将 Nand2TB_Nand2 进行例化,并连接各个信号,以验证设计的正确性。

Top.h:

#ifndef TOP_H
#define TOP_H#include <systemc.h>
#include "Nand2.h"
#include "tb/TB_Nand2.h"SC_MODULE(Top)
{sc_signal<bool> sig_a, sig_b, sig_x;sc_in_clk clk;Nand2 na2;TB_Nand2 tb_na2;SC_CTOR(Top) : na2("NAND2"),tb_na2("TB_NAND2"){na2.input_a(sig_a);na2.input_b(sig_b);na2.output_x(sig_x);tb_na2.output_a(sig_a);tb_na2.output_b(sig_b);tb_na2.input_x(sig_x);tb_na2.clk(clk);}
}; /* Top */#endif /* TOP_H */

最后我们例化顶层模块 Top ,并添加波形跟踪:

main.cpp:

#include "Top.h"int sc_main(int, char **)
{Top top("TOP");sc_clock clk("CLK", 20, SC_NS);top.clk(clk);sc_trace_file *tf = sc_create_vcd_trace_file("Nand2");tf->set_time_unit(1, SC_NS);sc_trace(tf, top.sig_a, "A");sc_trace(tf, top.sig_b, "B");sc_trace(tf, top.sig_x, "X");sc_trace(tf, top.clk, "CLK");sc_start(200, SC_NS);sc_close_vcd_trace_file(tf);return 0;
}

运行结果:

$ ./build/demo_systemc SystemC 2.3.0-ASI --- May 20 2025 10:22:36Copyright (c) 1996-2012 by all Contributors,ALL RIGHTS RESERVEDNote: VCD trace timescale unit is set by user to 1.000000e-09 sec.
A = 0 B = 0 X = 1
A = 0 B = 1 X = 1
A = 1 B = 0 X = 1
A = 1 B = 1 X = 1
A = 1 B = 1 X = 0

输出验证了一个 NAND 门的真值功能。

生成的波形文件 Nand2.vcd 可通过以下命令转换为 Modelsim 支持的格式:

vcd2wlf Nand2.vcd Nand2.wlf

然后在 Modelsim 中加载 Nand2.wlf 文件,即可查看波形:
vsim


Ref

[1] 李挥, 陈曦. SystemC电子系统级设计[M]. 北京: 科学出版社, 2010.
[2] LearnSystemC.com. Learn SystemC[EB/OL]. [2025-06-13]. https://www.learnsystemc.com/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87207.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87207.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/87207.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决 Golang 下载golang.org/x包失败方案

在 Golang 开发过程中&#xff0c;不少开发者都遇到过这样的困扰&#xff1a;当试图下载golang.org相关包时&#xff0c;会出现访问失败的情况&#xff0c;尤其是golang.org/x系列包&#xff0c;作为众多第三方库依赖的核心组件&#xff0c;其无法正常下载会严重影响项目的开发…

CppCon 2016 学习:BUILDING A MODERN C++ FORGE FOR COMPUTE AND GRAPHICS

你提供的这段文字是关于 设计一个精简但足够的 C 框架来驱动 Vulkan 的目标陈述&#xff0c;属于项目文档或演讲的第一部分 “Goals”。我们可以把它逐项拆解并深入理解&#xff1a; PART (I – I): GOALS&#xff08;目标&#xff09; 总体目标&#xff1a; 构建一个最小但足…

# AI武装大脑:技术管理者如何用人工智能重构认知与决策系统

作为一位经历了15年技术管理实战的老兵&#xff0c;我见过太多项目因为决策失误、认知局限而陷入泥潭。直到我开始系统性地用AI武装大脑&#xff0c;才真正找到了突破技术管理瓶颈的利器。今天&#xff0c;我要分享的不是那些泛泛而谈的AI概念&#xff0c;而是如何用AI真正提升…

【Linux】UDP与TCP协议

目录 UDP协议 1.1通信流程 1.2函数 socket bind sendto recvfrom close 1.3实现udp通信 TCP协议 1.1TCP头部结构 1.2通信流程 三次握手 正式通信 四次挥手 1.3协议特性 面向字节流 可靠传输 序列号和确认号 重传机制 流量控制和拥塞控制 1.4常用函数 s…

gbase8s之MyBatis批量update问题

源代码 <update id"updateDynamicTableData"><foreach collection"mapList" item"map" separator";">UPDATE ${tableName} SET<foreach collection"map" item"value" index"key" separ…

博图SCL中WHILE语句的使用详解及案例

在西门子TIA Portal的SCL&#xff08;结构化控制语言&#xff09;编程中&#xff0c;WHILE循环是处理条件迭代任务的核心工具。它根据布尔表达式动态控制循环执行&#xff0c;适用于不确定循环次数的场景。下面从语法、执行流程、注意事项到实际案例全面解析。 一、WHILE循环基…

简单聊聊JVM中的几种垃圾收集算法

3.4、分代收集算法 分代收集算法&#xff0c;可以看成以上内容的延伸。它的实现思路是根据对象的生命周期的不同&#xff0c;将内存划分为几块&#xff0c;比如把堆空间划分为新生代和老年代&#xff0c;然后根据各块的特点采用最适当的收集算法。 在新生代中&#xff0c;存在…

依赖已导入,已下载,无法使用问题

明明已经导入依赖&#xff0c;却无法使用相关注解 于是&#xff0c;我使用 mvn dependency:tree -Dverbose 来查看是否有依赖冲突 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal on project agileboot…

答题考试系统小程序ThinkPHP+UniApp

ThinkPHPUniapp开发的小程序答题考试系统&#xff0c;支持多种试题类型、多种试题难度、练题、考试、补考模式&#xff0c;提供全部前后台无加密源代码&#xff0c;支持私有化部署. 更新日志 V1.7.1修复一些问题 解决考场成绩列表重复问题&#xff1b; 解决后台材料题选择子…

DHCP服务管理

目录 DHCP协议 DHCP的优势 DHCP的分配方式 应用场景 注意 工作流程 何时更新租约 当客户端重启后 客户端类型 DCHP安装与配置 网络规划&#xff1a; 配置 DHCP 作用域 启动 DHCP 服务 配置路由器 配置路由器网卡 IP 开启 IP 转发&#xff08;确保跨网段通信&…

12.UDP客户端

准备工作 硬件准备&#xff1a;确保你的STM32板子已经正确连接了DP83848网络芯片。 软件设置&#xff1a; 安装好STM32CubeMX用于配置工程。 选择合适的STM32 HAL库版本。 如果可能的话&#xff0c;安装LwIP库支持TCP/IP协议栈。 步骤 1. 使用STM32CubeMX配置项目 打开…

希尔脚本简介及常用命令代码整理

一、Shell 脚本简介 1. 定义 Shell 是用户与操作系统内核交互的桥梁&#xff0c;常见类型有 Bash、Zsh、PowerShell 等。Shell 脚本则是一系列 Shell 命令的集合&#xff0c;通常保存为后缀为.sh 的文本文件。 2. 作用 类别描述自动化重复性任务例如定期备份数据、执行定时…

【人工智能下的智算网络】广域网优化

一、广域网络多路径I/O写的并行路径优化方案 1.1、数学建模 网络拓扑优化​ 1. ​拓扑抽象与路径发现​ ​邻接矩阵建模​&#xff1a; 将网络节点抽象为图顶点 G (V, E)&#xff0c;链路带宽与延迟定义为边权 w(e)。构造邻接矩阵 A&#xff0c;其中元素 A_{ij} 表示节点 …

AI测试开发工程师如何用大模型调用工具:从入门到实践

在软件测试领域&#xff0c;测试工程师常常面临测试用例设计复杂、数据生成繁琐、结果验证耗时等挑战。随着大语言模型&#xff08;LLM&#xff09;的迅速发展&#xff0c;Chat类大模型&#xff08;如GPT、LangChain支持的模型&#xff09;为测试开发提供了一种全新思路——工具…

迁移学习基础

知识的“跨界复用” 你是一位经验丰富的厨师&#xff08;源模型&#xff09;&#xff0c;尤其擅长做意大利菜&#xff08;源任务/源域&#xff09;。现在&#xff0c;老板让你去新开的一家融合餐厅工作&#xff0c;需要你做亚洲菜&#xff08;目标任务/目标域&#xff09;。你…

AI医生24小时在线:你的健康新‘算法监护人

2025年仲夏&#xff0c;中国医疗AI领域迎来爆发式突破&#xff1a;罗湖医院集团率先部署"DeepSeek-腾讯混元"双AI诊疗系统&#xff0c;实现患者15分钟极速就诊闭环&#xff1b;复旦大学研发的微量血液检测技术取得重大突破&#xff0c;仅需数滴血样即可筛查上千种疾病…

Java 中 DataSource-数据源 的基础介绍

Java 中 DataSource-数据源 的基础介绍 一、核心概念解析1.1 数据源&#xff08;Data Source&#xff09;1.2 数据库连接池&#xff08;Connection Pool&#xff09;1.3 二者关系1.4 DataSource 接口 二、DataSource 解决的问题与优势2.1 DataSource 的作用2.2 传统方式的局限性…

Vue + Vite 项目部署 Docker 全攻略:原理、路由机制、问题排查与开发代理解析

Vue Vite 项目部署 Docker 全攻略&#xff1a;原理、路由机制、问题排查与开发代理解析 本文面向希望将 Vue 3 Vite 项目部署到生产环境&#xff08;Docker NGINX&#xff09;并深入理解路由行为、构建机制与常见问题排查的开发者。 &#x1f4e6; 一、项目准备 以 Vue 3 …

Vue3 + TypeScript 使用 v-bind() 在 <style scoped> 中动态设置 CSS 样式值

使用要求&#xff1a; Vue 3.3 <style scoped>&#xff0c;Vue 的 v-bind() 在 CSS 中只支持在 scoped style 或 CSS Modules 中使用v-bind("cssVar") 双引号包裹响应式变量&#xff0c;变量 cssVar 必须是 Vue 的响应式数据&#xff08;如 ref 或 reactive&…

php列表头部增加批量操作按钮,多选订单数据批量微信退款(含微信支付SDK)

index_search.html data-table-id:表格id data-rule:需要传输的列表字段 data-action:控制器方法 <a class="layui-btn layui-btn-primary layui-btn-sm" style=