当前位置: 首页 > 工具软件 > Verilator > 使用案例 >

verilator常用基础知识

江正德
2023-12-01

预学习阶段

verilator语法学习

常用网站:

定义一个模块

module name(varible, ...);  // 定义一个模块
    // 模块逻辑
endmodule  // 模块结束

输入标识符:input,输出标识符:output

module name(
    input a,b,
    output c,d
);

赋值语句

assign x = y;

与、或、非、异或、同或

assign out = !in;  // 非门,C语言写法
assign out = ~in;

assign out = a & b;  // 与门
assign out = a | b;  // 或门
assign out = a ^ b;  // 异或门
assign out = !(a ^ b);  // 同或门

线网型信号:wire,一般用来表示中间信号

wire a, b, c;

向量:将一组相关的信号用一个向量名称统一命名的方式

wire [7:0] w;  // 表示一个8bit的向量信号w

向量拼接:{},用于将向量进行拼接

d[2:0] = {a[0], b[1], c[1]};  // 表示d向量的第一位是a[0],第二位是b[1],第三位是c[1]

表示自定义bit的写法:n'bxxx..xxx:表示共有n位,每位为x,其中b表示的是binary

复制算子:取连续值的情况

{5{1'b1}} // 5'b11111
{2{a,b,c}} // The same as {a,b,c,a,b,c}
{3'd5, {2{3'd6}}} // 9'b101_110_110

模块调用:父模块调用子模块

module top_module(  // 父模块
  input 	a,
  input 	b,
  output 	out
);
    mod_a mymod(  // 调用子模块 mymod为子模块实例化名称
        .in1 (a),  // 端口对应
        .in2 (b),  
        .out (out)); 
endmodule


module mod_a (   // 子模块
  input 	in1, 
  input 	in2, 
  output 	out 
);
assign out = in1 & in2;
endmodule

always块语法格式

  • 组合逻辑电路:always@(),与assign语句等效
    • assign语句只能对一个信号进行赋值,always块内可对多个信号进行赋值
    • assign语句中被赋值信号为wire类型,always块内被赋值信号需定义为reg类型
    • always块内支持更加丰富的语法,如使用if…else..、case等适合实现交复杂的组合逻辑
module my (input a, input b, output reg out);
	always@(a, b) out = a & b;
endmodule
  • 时序逻辑电路:always@(posedge clk || negedge clk)
    • 阻塞赋值(如x = y;),该赋值方式只能用在过程块(如always@(*))内
    • 非阻塞赋值(如x <= y;),该赋值方式只能用在过程块内(如always@(posedge clk)

在设计Verilog模块时,请遵循以下原则:

  • 在组合逻辑的always块内采用阻塞赋值
  • 时序逻辑的always块内采用非阻塞赋值

if...else:控制语句,if语句用于过程块内部,其对应的电路是二选一的选择器

// 在`assign`中
assign out = (condition) ? x : y;
// 在`always`中
always@(*)
    begin
        if (condition) out = x;
        else if (condition) out = y;
        else out = z;
    end

case:条件语句

case (condition):
    a: 
    b: 
    c:
endcase

debug:调试,$display, $strobe, $monitor,使用$finish去结束这个仿真。

initial:从仿真0开始执行,在整个仿真过程中只执行一次;如果一个模块中包含了多个initial,则这些initial块从仿真0时刻开始并发执行,且每个块执行是各自独立的,一本在块中使用beginend定义范围域

initial 
    begin
        //;
    end

暂时先学这么多,等后面有不懂的再回头看。

运行verilator官网的example

来看看verilator官方给的与C++交互的例子

mkdir test_our
cd test_our

cat >our.v <<'EOF'
  module our;  # 定义一个module
     initial  # 定义一个initial块
     	begin   # initial块的开始
     		$display("Hello World"); # debug输出Hello World
     		$finish; # debug完成
     	end # initial块的结束
  endmodule  # our模块的结束
EOF

cat >sim_main.cpp <<'EOF'
  #include "Vour.h"  # our.v被verilator编译成Vour.h
  #include "verilated.h"  # verialtor官方库
  int main(int argc, char** argv, char** env) {
      VerilatedContext* contextp = new VerilatedContext;  # verilator上下文指针
      contextp->commandArgs(argc, argv);  # 检查参数
      Vour* top = new Vour{contextp};  # 实例化our模块
      while (!contextp->gotFinish()) { # 一直到contextp仿真完成才退出
      	top->eval(); # 更新电路状态
      }
      delete top;
      delete contextp;
      return 0;
  }
EOF

编译

verilator -Wall --cc --exe --build sim_main.cpp our.v
  • -Wall:让verilator执行强类型警告
  • --cc:得到C++输出
  • --exe:和wrapper文件一起,为了创建一个可执行文件
  • --build:让verilator能让自己执行

运行程序

./obj_dir/Vour
Hello World
- our.v:2: Verilog $finish

电路设计举例

处理器内部一般有一个PC寄存器,其中存储指令地址,正常运行过程中,PC的值会随时间增加,同时从指令寄存器中取出相应地址的指令,因此,在此实现处理器的取指令电路,包含两部分:PC模块、指令存储器

module pc_reg(
	input wire clk,
	input wire rst,
	output reg[5:0] pc,
	output reg ce 
);
	always@(posedge clk) begin
		if (rst == 1'b1) begin
			ce <= 1'b0;  //  复位信号有效时,指令存储器使能信号无效
		end else begin
			ce <= 1'b1
		end
	end
	always@(posedge clk) begin
		if (ce === 1'b0) begin
			pc <= 6'h00  // 指令存储器使能信号无效的时候,PC保持为0
		end else begin:
			pc <= pc + 1'b1  // 指令存储器使能信号有效的时候,PC在每个时钟+1
		end
	end
endmodule
 类似资料: