前言
在开始后续篇幅之前,梳理一下C/C++的语法知识点;对于嵌入式开发或者软件开发的人来说,C/C++并不陌生。但对于SV”选手“来说,这是前期最大的”障碍“。本篇侧重梳理,授之以渔的方式,不会详细解读某一个点。
准备
- 验证使用C/C++主要是造case和当作胶合语言,不是开发语言,建议抛开厚厚的书,找一些快速上手的资源,例如:🔗菜鸟教程C++ 🔗菜鸟教程C
- C/C++相比SV,多了一些内存,指针,编译链接的概念,C语言贴近CPU硬件,C++的OOP也比SV"灵活"很多。参考🔗Linux C编程一站式学习 🔗C++基础教程
- 借助Chatgpt,🔗Copilot等AI辅助,有时提供的代码甚至可以复制直接使用。
- 学习好的开源代码,遇到问题整理学习,从实际code出发。
GCC编译选项
GCC编译流程参考🔗g++编译详解
选项 | 说明 |
---|---|
LIBRARY_PATH | 开发时,设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。和编译时链接库 -Ixx`` -Lxx 作用类似 |
LD_LIBRARY_PATH | 发布时,设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。和运行时链接库-Wl,-rpath 作用类似 |
-o | 直接生成可执行文件 |
-O/-O1/-O2/-O3 | 优化代码,减少代码体积,提高代码效率,但是相应的会增加编译的时间 |
-O0 | 关闭优化 |
-DMACRO | 以字符串"1"定义 MACRO 宏。 |
-DMACRO=DEFN | 以字符串"DEFN"定义 MACRO 宏。 |
-g | 生成调试信息。GNU 调试器可利用该信息 |
-IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY。 |
-LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY。 |
-lLIBRARY | 连接时搜索指定的函数库LIBRARY。 |
-Wno-deprecated-declarations | 关闭 使用弃用方式导致的警告,关闭其他警告-Wnoxxx |
-Wall | 开启编译器的所有警告选项 |
-Wl,-rapth=xxx | 如果在非标准位置安装了自定义库,可以使用-rpath确保程序找到它们 -Wl : l 为linker缩写 |
-Xlinker -rpath=xxx | 和-Wl类似,只不过-Wl需要一个逗号 -Xlinker -rpath -Xlinker xxx 也可以,没有 等于号 |
-Wl,-E | 选项告诉链接器在生成共享库时将所有符号都导出,即使它们没有被显式引用 |
-lm | The flag to link the math library. |
-std=c++2a -std=c++11 | c++2a (C20) -std=c++11 (C11) gcc9默认采用-std=c++14 |
-shared -fPIC | 生成共享库 |
-rdynamic | 在生成可执行文件时将所有符号添加到动态符号表中-LDFLAGS -rdynamic xxx.so |
-DFLAGS -DVCS | 手动编译uvm库,编译uvm_dpi.cc时,需要加上vendor的define |
--verbose | 显示详细的编译链接过程 |
C语言
参考https://blog.csdn.net/Holden_Liu/article/details/122541993
CPP
Item | CPP | SystemVerilog |
---|---|---|
宏定义 | #define LENGTH 10 ,LENGTH使用宏 | define LENGTH 10, `LENGTH使用宏 |
类型转化 | 静态转化 foat f = static_cast<float>(int_i); 动态转换 Derived* ptr_derived = dynamic_cast<Derived*>(ptr_base); C++还存在 const_cast 可以突破 C/C++ 的常数限制,修改常数的值 | 静态转化 num = int'(name); 动态转化 $cast(child_handle, parent_handle); 类的多态,父类句柄指向子类实例 |
extern | extern 是用来在另一个文件中声明一个全局变量或函数。C++中extern 用于声明,然后还有定义和初始化;C++相比SV,多了一层文件的概念,多个文件中,A文件要使用B文件定义的变量/函数,可以使用extern 引用;变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。当使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用extern 关键字在任何地方声明一个变量。虽然可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。 | extern指用于在类外定义(function,task,不会是变量) SV变量的声明和定义在一起; SV的方法也可以先声明,再调用,最后在定义 SV的编译是一整个filelist,C的每个file都可以单独编译,最后在统一链接到一起,所以C多了一个文件的概念。 |
static | static 指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用static 修饰局部变量可以在函数调用之间保持局部变量的值。static 修饰符应用于全局变量时, 会使变量的作用域限制在声明它的文件 内。 | static 静态变量,区别于动态automatic 变量,调用完成后,内存不释放;(默认class内的都是automatic的类型,module内的都是static类型)static 一般放在class…endclass中;C++和sv的 static 变量都可以通过:: 访问,但是C++的static 变量只能在类外初始化; |
函数中的变量 | 定义变量可以放在块 {…} 中,也可以不放在其中,没有强制要求;但是会影响作用域; | SV函数中的变量声明,必须放在开头位置;否则需要放在begin…end 中 |
const | 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量; 整数常量:215u 布尔常量:true,false define预处理器 定义常量:#define LENGTH 10 字符串都是常量 函数头部的结尾加上 const 表示常成员函数,这种函数只能读取成员变量的值,而不能修改成员变量的值;此时如果仍要修改变量的值,需要类型限定符mutable修饰 | const:分为两种:全局性、instance性的 (const 在run-time阶段,而 localparam需要在elaboration-time被赋值) 全局性const:在声明时即赋值,之后不可修改; instace性const:只使用const进行声明,赋值发生在new()中const修饰的变量,不允许被修改,否则编译器报错。 |
传参 | 传值调用 : 把参数的实际值复制给函数的形式参数,修改函数内的形式参数不会影响实际参数。void swap(int x, int y) 指针调用: 把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。void swap(int *x, int *y) 引用调用:类似sv中的ref;void swap(int &x, int &y) 后两种调用,修改形式参数会影响实际参数 | sv默认对变量/数组采用传值调用,对类采用引用; ref可以显示对变量/数组采用引用的方式; |
数组 | C++一般通过指针操作数组; C++ STL标准模板库中提供vector来定义数组,拥有更多成员函数 | |
string | 字符串看成一个数组,声明成指针变量,需要加上const修饰,否则char str = “ABC”; //ISO C++ forbids converting a string constant to ‘char’ char site[7] = {‘R’, ‘U’, ‘N’, ‘O’, ‘O’, ‘B’, ‘\0’}; cout << site << endl; site类似python中的string形式,可以单独通过index引用 C++中#include ,则可以像sv一样,使用关键字string声明字符串,同时也可以像python一样,支持str1+str2的连接 | SV中的字符串通过string声明 |
handle | 1.数组的指针就是数组名,但是这是一个常量指针 2.&array[xx]:取址 int *ptr:声明int类型指针变量;*ptr:解引,取值; ptr+1:增加地址为对应类型的空间大小; 3.函数返回指针类型的变量时,因为函数调用结束时,会释放内存,所以需要static或者动态分配内存new()来保证; | 有指针概念,但是没有指针变量的概念; |
引用 | 引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字; 引用的可读性比指针高 int& r = i; 引用变量,&区别于取址符号; 引用变量在定义时就必须初始化;当作形参可以不初始化; | ref类似,但是只能用于方法的形参中,没有引用变量的概念; |
class | 访问修饰符:public,protected,private;默认是private,可以修饰变量或者函数 继承也分为三种,public继承,protected继承,private继承;class B : public A,class B : protected A,class B : private A this指该类,this->的形式访问 类的instance名访问成员,通过’.‘访问;类的指针访问成员,通过’->'访问,和结构体指针访问类似; 构造函数,就是类的名字;构造函数是无返回类型的,也不需要加void修饰; 析构函数:在不在需要该类时,会自动调用析构函数释放内存。 拷贝构造函数:class_type a = b; 进行赋值时,会自动调用拷贝构造函数,所以a和b的object内存不一样 友元函数:略 内联函数:inline,如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。 int getage() const; const成员函数,const 成员函数可以使用类中的所有成员变量,但是不能修改它们的值 C++在声明类时,Box Box1(3.3, 1.2, 1.5); 就自动创建了类的实例;如果是声明了一个类指针,则需要像sv一样调用new手动分配内存。 C++支持一个类中相同名字函数的重载,也支持重载运算符 多态:需要virtual修饰,动态调用;父类加上virtual,子类可以不用再加,和sv一样 | sv中默认不加就是public 只有一种类型的继承 class_type a = b; 在sv中,a,b指向同一个实例object 构造函数new(),没有析构函数;C++中的new指动态分配内存; this.的形式访问 sv中Box Box1只是声明了句柄,还需要调用new()函数创建实例; sv不支持函数重载,运算符重载;sv只有重写(覆盖); 🔗重写、覆盖、重载区别和存在的意义_c++覆写的意义 多态使用和C++类似,virtual修饰; |
命名空间 | namespace:命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。 using namespace std;using类似sv中的import | package 和 import 管理作用域 |
模板 | 类似一种忽略类型的宏定义函数或者宏定义类;C++是一种强类型语言,所以引入模板增加灵活性,属于泛型编程(Generic Programming) | sv也有类似的,参数化的class;但是C++的更灵活 |
:: | C++中::是域操作符,前面什么都不写代表的是全局函数,例如 ::sc_core::sc_module_name" | 🔗Systemverilog 作用域解析运算符 :: |
从C++11开始,引入了一些新的语法和特性,比如auto
,std::move
,std::unique_ptr
等,这些特性使得 C++11 成为一个更现代、更强大的编程语言。🔗ModernSystemC一个可参考的example,建议逐步采用C++11特性。
Debug
undefine问题
undefine问题是最常见的,在编译或者运行时遇到,undefined symbol
,undefined referenc to
,cannot open shared object file
等。SV的编译是吃进一整个filelist,然后在elaboration阶段统一link链接,生成可执行程序。而C/CPP是可以提前将一个或者一些文件编译为一个静态或者动态库,最后统一做链接生成target文件。在一个文件调用程序,程序的定义在另一个lib实现,如果gcc无法找到对应程序的定义,则会报undefine问题。
一般是PATH,GCC选项,scope的设置问题,总结如下:
-
确保调用的虚函数有实现,才可以被调用
-
确认
LIBRARY_PATH
和LD_LIBRARY_PATH
的设置符合预期 -
确认GCC版本以及
-std=
指定的C++版本 -
g++编译的c code使用
extern “c”
修饰 -
使用DPI时,c code调用SV export function,确保
svSetScope()
符合预期 -
如果在elaboration阶段通过vcs link共享库,是否吃进相应的
.so
文件 -
如果在runtime阶段调用共享库,确保使用
-sv_lib
或-sv_liblist
方式加载.so
文件 -
常用定位命令:
strings -a <binary> | grep "GCC:"
:查看编译时的GCC版本ldd -r <binary>
: 模拟运行时需要动态链接的库nm -g <binary>
: 查看符号表,前面带U(undefined)表用在其他库定义nm -D <binary> | c++filt | grep <string>
:查找匹配string的可读形式函数名readelf -d <binary>
: 查看依赖的共享库,rpath
,soname
Segmentation Fault
TODO
Stack Overflow
TODO