基本结构

  1. 基本设计单元:module

  2. module内的结构:

  3. 端口定义

  4. I/O说明
  5. 信号类型声明
  6. 功能描述

  7. 基本数据类型

  8. 整数常量:整数字面量

  9. 符号常量:parameter

  10. 网络变量:wire、wor(线或)、wand(线与)、tri1和tri0(上拉和下拉电阻)、suply1和suply0(逻辑1即电源,逻辑0即接地)一般用于assign等相关的组合逻辑

  11. 寄存器变量:reg(无符号整数)、integer(32位带符号整数)、real(64位带符号实数)、time(无符号时间)一般用于initial、always等相关的时序逻辑,需要被明确赋值

  12. 特殊数据的表达:

  13. 常量:<符号>[位宽]'[进制]<数字>

    • 较长的数字之间可用“_”隔开
    • 常量未表明位宽时,默认为32bit
  14. 不定值与高阻值:只能在二进制中使用,x表示不定值,z或?(case中常用)表示高阻值

运算符

逻辑比较

  • 算数运算符:求模的符号与被模数一致。
  • 逻辑运算符:结果不确定就返回x(即1'bx),为真就返回1(1b'1),反之返回0(1b'0)
  • 关系运算符
  • 条件运算符
  • 等式运算符:
  • ==为等于
  • !=为不等于
  • ===为全等:可以按位比较不定值和高阻值是否相同,一般用在case中
  • !==为不全等:可以按位比较不定值和高阻值是否不相同,一般用在case中

位运算

  • 位运算符:异或是\^,同或是~\^
  • 缩位运算符:符号同位运算,是单目运算,但其实是对操作数是做递推运算,即第一位与第二位做位运算,再将结果与后面位做位运算
  • 移位运算符:
  • 无符号移位:<<、>>
  • 有符号移位:<<<、>>>
  • 位拼接:{}
  • 便于赋值:assign {A,B}=X(会把X拆成两部分,分别给A和B )
  • 增添新位:assgin A={A,1'b1,1'b0}
  • 简化重复扩展:用{4{A}}代替{A,A,A,A}
  • 嵌套:{ A, {B,C}, {3{D}} }
  • 在拼接表达式中,所有位数必须指明位数

赋值运算

  • 连续赋值:过程块以外的语句使用(比如assign),使用=。
  • 过程赋值:过程块以内的语句使用,使用=(阻塞赋值)或<=(非阻塞赋值)。
  • 非阻塞赋值:块结束的一瞬间,同时进行所有变量的赋值。所以赋值无法传递
  • 阻塞赋值:在语句结束的一瞬间,进行变量赋值
    • 为什么不建议用阻塞赋值:因为电路中只有非阻塞赋值,而没有阻塞赋值。所以阻塞赋值的综合结果可能是未知的
  • 建议:连续使用=赋值,过程使用<=赋值

过程语句(initial/always)

always语句(可综合,类似于编程的顺序执行)

  • 只有reg变量能在always中赋值,它们是过程变量
  • 如果always块中包含多个语句,则==需要使用begin-end或者fork-join包含==
  • always一定要有时序控制,否则会产生循环跳变,即仿真死锁
  • always中最好不使用=赋值,使用<=
  • 敏感信号:分为==边沿触发和电平触发==两种。可以为单个信号,也可以多个信号(用or隔开)。敏感信号不要为x/z,会阻挡进程
  • 边沿触发:用于描述时序逻辑,posedge、negedge
  • 电平触发:用于描述组合逻辑
  • 多个信号时的注意事项:
  • 一定要在敏感列表中列出所有可能的敏感信号,不然可能会产生一个锁存器(外部信号a实际上变化了,但always不敏感,所以内部的信号a没有跟着变化,即a锁存了)
  • 多个always的注意事项:
  • 语句不是按代码先后执行的,多个always可能会并列触发。

initial语句(不可综合)

  • 用途:

  • 在仿真一开始,对各变量进行初始化

  • 在测试文件中生成激励波形作为仿真信号

块语句

顺序块begin-end(可综合,类似于编程的大括号)

  • 强制要求块中的语句顺序执行

  • 每条语句的延迟时间,是上一条语句的结束时间+设定延迟时间

并行块fork-join(不可综合)

  • 强制要求块中的语句同时进行(但不一定同时被触发)
  • 每天语句的开始时间,是并行块的开始时间+设定延迟时间
  • 因此可以用延迟来构造时序

判断语句

if-else

  • 拒绝并列if:语句不是按代码先后执行的,多信号可能会同时触发几个if语句导致产生矛盾,因此不要写并列的if,要写嵌套的。
  • 坚持写全所有的情况或补全else:否则当不满足某些情况的时候,变量可能无法复位,导致产生锁存器

case

  • case
  • casez(不考虑z位的比较)
  • casex(不考虑z和x位的比较)
  • 可用?代替z或x
  • 坚持写全所有的情况或补全default:否则当不满足某些情况的时候,变量可能无法复位,导致产生锁存器

循环语句

循环变量一定要用integer,否则溢出的时候会变负数

强制跳出循环语句使用disable语句

for

repeat(不可综合)

  • repeat(n)指连续执行语句n次

while(可综合/不可综合)

  • 只有while有时序控制时才能综合
  • 一般用在testbench中才用

forever(不可综合)

  • 构造一个无限循环
  • 使用disable跳出
  • 在begin后面起别名,用别名来跳出
  • 一般用在initial中,不能写在地方

方法

  • 主动调用:
  • task:均能调用
  • function:只能调用function
  • 被调用:
  • task:只能在过程块中调用
  • function:均能被调用
  • 返回值:
  • task:用输出变量返回
  • function:用内置寄存器返回

task

  • 写在module里,只能在模块内调用
  • 参数写法和module一致
  • 如果定义是使用inout双向类型,则它既作为输入也作为输出

function

  • 写在module里,但可以在任何地方调用
  • 返回值输出:具有一个同名的内部寄存器,通过==给同名的寄存器赋值==,来输出返回值
  • 记忆:比如,可以将function[7:0] func看成是创建了一个function类型、位宽为8的寄存器func

原语、模块描述层级

门电路原语

  • 是基本操作指令,不可中断

  • 和module写法一致,输出参数固定为第一个

模块描述层级

  • 门级(门级描述)

  • 需要知道所有的逻辑功能

  • 使用门电路原语,是最终想要综合得到的层级(也是最底层的)

  • 算法级(行为级描述)

  • 只需要知道逻辑表达式

  • 使用逻辑表达式

  • 系统级(行为级描述)

  • 只需要知道输入-输出真值表

  • 使用case

  • 算法级(行为级描述)

  • 只需要知道输入-输出的条件关系

  • 使用条件运算符

verilog的内置函数

暂略。

verilator

以下以c++为例,而不是system_c

基本结构

自定义文件

  • verilog文件

  • cpp激励文件:可以控制verilog文件的所有变量,以及文件中所有module的参数。一般用来构造外界时钟

自动生成文件

  • Vxxx.cpp和Vxxx.h中间文件:
  • verilator根据顶层文件负责==将xxx.v转化为Vxxx.cpp和Vxxx.h==
  • 在.cpp内部会自动生成叫做Vxxx的类
  • 有了这两个文件,cpp激励文件就可以在.cpp层面上进行变量的获取和控制

  • Makefile:verilator自动生成的文件,负责将中间文件和cpp激励文件进行编译,生成可执行文件和波形文件

激励文件

头文件

  • :使用更安全的指针声明
  • :testbench需要的类封装
  • "Vxxx.h":根据verilog自动生成的头文件

仿真环境和顶层模组文件

在cpp激励文件中使用仿真环境制造时钟,使用顶层模组文件找到

  • 仿真环境:

  • 实例化testbench类封装为一个对象,这样就可以通过使用对象,来调用内置的类成员和方法

  • c++ const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};

  • contextp->debug()

  • contextp->randReset():设置随即种子

  • contextp->traceEverOn():设置是否打开波形追踪功能

    • contextp->open():设置输出波形的文件名
  • contextp->timeInc():信号延时,相当于verilog中的#延时,单位默认为10ps

  • contextp->time():返回模拟时间

  • contextp->gotFinish():当verilog中调用$finish时为true

  • contextp->dump():存入波形文件

    • 一般这样用,contextp->dump(contextp->time())
  • contextp->close():关闭波形文件

  • 顶层模组文件:

  • Vxxx.h有一个类声明存放全部的定义信息和模组继承关系,在Vxxx.cpp中实现。

  • 实例化一个对象,通过操作对象就可以指定想要操作的模组

  • c++ const std::unique_ptr<Vtop> top{new Vtop{contextp.get(), "TOP"}};

  • top->输入变量

  • top->输出变量

  • top->eval():同时更新所有的值,所有的赋值必须通过exal()才能得到更新,否则不变

    • top->eval_step()
    • top->eval_and_step()
  • contextp->final():