verilog

工具

vscode切换shell和编辑器ctrl+飘号,ctrl+1

verilator

【Verilator】 1 简明教程_verilator教程-CSDN博客

法1

用 verilog和testbench一起编译:

verilator -Wall -Wpedantic --trace -cc mux21.v --exe sim_main.cpp

进入生成目录并编译:

cd obj_dir
make -j -f Vmux21.mk

运行仿真:

./Vmux21

./obj_dir/Valu 命令执行可执行文件进行仿真,此时会生成波形图 waveform.vcd,我们只需要执行 gtkwave waveform.vcd 即可查看波形图。

法2

Verilog 设计转换为可执行的 C++ 仿真模型并自动编译的完整工作流指令

verilator -Wall --trace -cc mux21.v --exe sim_main.cpp --build

法3

  1. SystemVerilog 转换为 C++,会生成一个名为 obj_dir的子目录,里面是转换所生成的文件

     verilator --cc mux21.v
    
  2. 写完testbench后,运行 Verilator 并重新生成包含测试用例的 .mk 文件

    verilator -Wall --trace -cc mux21.v --exe sim_main.cpp
    
    • -Wall 表示开启 C++ 所有警告
    • –trace 表示开启波形跟踪
    • -cc 转换.sv为cpp
    • –exe 根据后面指定的testbench 生成用于仿真的可执行文件
  3. 进行编译

    make -C obj_dir -f Valu.mk Valu
    
  4. 运行testbench

    ./obj_dir/Valu 命令执行可执行文件进行仿真,此时会生成波形图 waveform.vcd,我们只需要执行 gtkwave waveform.vcd 即可查看波形图。

语法

参考:Verilog 语法 - 数字电路教程

wire类型在每次赋值前要加assign,而reg类型在每次赋值前不需要加任何东西。

在 always 块内被赋值的信号应定义成 reg 型,用 assign 语句赋值的信号应定义成 wire 型。

操作符

~按位取反、& 按位与、| 按位或。

||逻辑或

img

wire:

在Verilog中,线网型信号(wire)是一种主要的数据类型,用于表示连接信号。它类似于实际电路中的导线,用于传输信号。wire类型的信号通常用于组合逻辑电路中,表示信号的传递和连接。

wire类型信号的主要特点是它们不能存储值,只能传递值。因此,它们通常用于组合逻辑电路,而不是时序逻辑电路。wire类型的信号在assign语句中被赋值,但不能在always块中被赋值。

assign:

实现组合逻辑操作的一种主要描述方式。只能给wire赋值

assign out={b,a}把b,a拼接到out上面去

reg:

除 wire 类型外,另外一种常用的数据类型,一般表示寄存器类型数据,不过并不绝对,记住一条原则:在 always 块内被赋值的信号应定义成 reg 型,用 assign 语句赋值的信号应定义成 wire 型。

always:

除 assign 外,另外一种实现赋值操作的关键字,两者都不可嵌套,区别在于,assign 语句只能实现组合逻辑赋值,且一个 assign 语句后面只能跟一条赋值表达式。而 always 即能实现组合逻辑赋值,又能实现时序逻辑赋值操作,且可以包含多条赋值表达式,多条赋值表达式,则应位于 begin/end 对中间。

img

img

USTC VLab Verilog OJ | YAVG Group Presents

所有的数字电路都是由逻辑门和连线构成的,因此理论上来说都可以通过模块的连接和assign语句进行描述,然而在很多情况下这并不是最方便的一种方式,过程块提供了一种更加方便的描述方式,always过程块便是其中最常用的一种。
对于可综合电路(即能转化成实际电路的verilog描述方式,与之相对的是不可综合电路,多用于电路仿真,不能转换成实际电路),有两种always块的语法形式:
-组合逻辑电路:always@(*)
-时序逻辑电路:always@(posedge clk)
组合逻辑电路的always块与assign语句等效,用户描述组合逻辑电路时,可根据便利性选择其中一种方式使用。两者生成的硬件电路一般是等效的,但在语法规则上稍有不同:
-assign语句只能对一个信号进行赋值,always块内可对多个信号进行赋值
-assign语句中被赋值信号为wire类型,always块内被赋值信号需定义为reg类型
-always块内支持更加丰富的语法,如使用if…else..、case等适合实现交复杂的组合逻辑
例如下述两条语句是等效的(out1需定义为wire类型,out2需定义为reg类型,但这仅仅是语法上的要求,生成的电路并没有区别):
assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;
其对应的电路图如下所示:
image-20250101152942896
always语句后的括号内放的是敏感变量列表,对于上例来说,可以写成always @(a,b,c,d) out2 = a & b | c ^ d,但为了简单起见,我们一般都用符号*代替。

posedge/negedge:

posedge是Verilog 关键字,表示上升沿的意思。always @(posedge clk) 表示在 clk 信号的上升沿的时刻,执行 always 块内部的语句,与此相对应的,是表示下降沿的关键字 negedge。凡是带有 posedge 或 negedge 的 always 块,都会被综合成时序逻辑电路。
在同一个always进程块中,同一触发信号只能使用一种边沿,即上升沿和下降沿不可同时使用。(例如:always@(posedge clk or negedge clk),这种写法是错误的)

阻塞/非阻塞赋值:

采用 <= 进行赋值的语句,称为“非阻塞赋值”,采用 = 进行赋值的语句,称为“阻塞赋值”。在 always 块中,阻塞式赋值方式语句执行有先后顺序,而非阻塞赋值语句则是同时执行。因此,在时序逻辑电路中,两种赋值方式可能或综合出不同的电路结构。

按位或\与

按位与、归并与操作,如该操作符只有一个操作数时,则将该操作数的所有位进行相与操作

向量拼接

part_selection用于选择向量信号中的一部分,而向量拼接算子{a,b,c}用于将多个信号组合成一个位宽更大的向量信号,如:
{3'b111, 3'b000} 等同于 6'b111000
{1'b1, 1'b0, 3'b101} 等同于5'b10101
{4'ha, 4'd10} 等同于 8'b10101010 // 4'ha and 4'd10 are both 4'b1010 in binary
向量拼接时,每个信号都需要有明确的位宽,这样拼接后的信号才会有明确的位宽。例如,{1,2,3}就是非法的,因为无法确定各信号的位宽,语法检查时会报错。
向量拼接算子既可以用于赋值语句的左侧,也可用于右侧,如下所示:

 input [15:0] in;
 output [23:0] out;
 assign {out[7:0], out[15:8]} = in;
 assign out[15:0] = {in[7:0], in[15:8]};
 assign out = {in[7:0], in[15:8]};  

复制算子

复制算子是拼接算子的一种特殊情况,如a={b,b,b,b,b,b}便可以写成a={6{b}}的形式。复制算子的格式为:{num{vector}},其中num必须为常量。如下所示:

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

parameter

定义常量的关键字。它允许你在模块中定义一些固定的值,这些值可以在模块实例化时进行参数化,从而使模块更加灵活和可重用。

case/casez

casez的用途:它将值为z的位在比较中视为无关紧要。

//四输入优先编码器
always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // in[3:1] can be anything
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end
module ALU181a (S, A, B, F, M, CN, CO, FZ);
   input[3:0] S; input[7:0] A,B;  input M, CN;  
   output[7:0] F; output CO, FZ; 
   //wire[7:0] F;   wire CO; 
	wire[8:0] A9, B9;  reg FZ; reg[8:0] F9; reg [7:0] F; reg CO;
   assign A9 = {1'b0, A} ;  assign B9 = {1'b0, B} ;
   always @(M or CN or A9 or B9 or S) begin  
   case (S)
4'b0000 : if (M==0)  F9<=A9+CN  ; 
else  F9<=~A9 ;  
4'b0001 : if (M==0)  F9 <= (A9 |B9) + CN ; 
else  F9<=~(A9 | B9) ;
4'b0010 : if (M==0)  F9 <= (A9 |((~B9)&9'b011111111))+ CN;    
 else  F9<=(~A9) & B9 ; 
4'b0011 : if (M==0)  F9 <= 9'b000000000-CN;     
else  F9<=9'b000000000;
4'b0100:if(M==0)F9<=A9+(A9& ((~B9)&9'b011111111))+CN;     
else  F9<=~(A9 & B9) ;
4'b0101:if(M==0)F9<=(A9|B9)+(A9&((~B9)&9'b011111111))+CN;  
else  F9<= ~B9 ;
4'b0110 : if (M==0)  F9 <= A9 - B9 - CN ;       
else  F9<= A9 ^ B9 ;
4'b0111 : if (M==0) F9 <= (A9 & (((~B9)&9'b011111111))) - CN ;  
else  F9<= A9 & (~B9) ;
     4'b1000 : if (M==0)  F9 <= A9 + (A9 & B9)+CN ;  
else  F9<= (~A9) | B9 ;
     4'b1001 : if (M==0)  F9 <= A9 + B9 + CN ;       
else  F9<= ~(A9 ^ B9) ;
     4'b1010:if(M==0)  F9<=(A9|(((~B9)&9'b011111111)))+(A9&B9)+CN; 
        else  F9<= B9 ;
      4'b1011 : if (M==0)  F9 <= (A9 & B9) - CN ;    
 else  F9<=A9 & B9 ;
      4'b1100 : if (M==0)  F9 <= A9 + A9 + CN ;       
else  F9<=9'b000000001 ;
      4'b1101 : if (M==0)  F9 <= (A9 | B9)+A9+CN;     
else  F9<= A9 | (~B9) ;
      4'b1110 : if (M==0)  F9 <= (A9 | (((~B9)&9'b011111111)))+A9+CN;  else  F9<= A9 | B9 ;
      4'b1111 : if (M==0)  F9 <= A9 - CN ;            
else  F9<= A9 ;
     default : F9 <= 9'b000000000 ;
    endcase 
		//if (A9 == B9) FZ <= 1'b0 ; else  FZ <= 1'b1 ; 
		F <= F9[7:0] ;  
		if(F==8'b00000000) FZ <= 1'b1 ; else FZ <= 1'b0 ;
		if(M==0) CO <= F9[8];
      end
endmodule

posted @ 2025-01-01 15:27  r_0xy  阅读(88)  评论(0)    收藏  举报