verilog笔记

逻辑值

1、0、z(高阻态,无驱动)、x(未知逻辑电平,实际不存在)

实际电路中,只存在:1、0、z,还有亚稳态不稳定状态

参数定义

parameter 可以在顶层文件中对模块中参数进行修改
localparam 只能模块内部使用,

换算二进制后位宽的总长度可有可无,verilog会为常量自动匹配合适的位宽
当总位宽大于实际位宽,则自动在左边补0,总位宽小于实际位宽,则自动截断左边超出的位数

如果直接写参数,100,则表示32位的十进制数100

阻塞赋值

顺序赋值

非阻塞赋值

同时赋值

乘和除一般不适用,消耗资源大,有乘法器,使用乘法器,除法通过别的方法技计算
求余一般用在测试文件中

归约运算符 按位运算符

& 可以作为一元运算符(仅有一个参与运算的量);也可以作为二元运算符(有两个参与运算的量)

一元运算是

表示归约与,&m将m中所有bit相与,最后结果为1bit

二元运算符

按位与,m&n m中每一位与n中每一位相与,在运算时候要保证m和n的比特数相等,最后的结果和m(n)的比特数相同。

~& ^ ~^ | ~| 与上面同理

逻辑运算符

&& 与c语言相同 逻辑与运算符结果只有真或假,

||(逻辑或) ==(逻辑相等) !=(逻辑不等)

#关系运算符
< > <= >=

移位运算符

<< >> 左移运算符 右移运算符

位拼接符号

拼接是一堆花括号加逗号组成{,}不同数据之间用”,”隔开
8bit的a 3bit的b 5bit的c 顺序拼接成16位的d
d = {a,b,c}

条件运算符

?:;和C语言中三目运算符相同

优先级

归约运算符 > 算数运算符 > 移位运算符 > 关系运算符 > “==”和“!=” > 按位运算符 > “&&”和”||” > 条件运算符
总体 一元运算符 > 二元运算符 > 三元运算符
使用括号添加优先级

if - else 语句

分支语句

case casex casez
case 分支

系统函数

有一些任务和函数,完成一些特殊的功能,系统任务和系统函数,这些函数大多数只能在Testbench仿真中使用,
timescale 1ns/1ns //时间尺度预编译指令时间单位/时间精度 时间单位和时间精度由值1、10、和100 以及单位s、ms、us、ns、ps 和fs 组成。 时间单位:定义仿真过程所有与时间相关量的单位。 仿真中使用“#数字”表示延时相应时间单位的时间,例#10表示延时 10个单位的时间,即 10ns。 时间精度:决定时间相关量的精度及仿真显示的最小刻度, timescale 1ns/10ps 精度0.01,#10.11 表示延时 10110ps。
下面这种写法就是错误的,因为时间单位不能比时间精度小。
`timescale 100ps/1ns //错误

生要的函数有如下这些,在支持 Verilog 语法的编辑器中都会显示为高亮关键字

1
2
3
4
5
6
7
8
9
$display //打印信息,自动换行
$write //打印信息
$strobe//打印信息,自动换行,最后执行 放在代码最后的地方执行
$monitor//监测变量 打印的变量任何一个变量修改,都会重新执行该语句
$stop//暂停仿真
$finish//结束仿真
$time//时间函数
$random//随机函数
$readmemb//读文件函数 $readmemb读取二进制文件 $readmemh 读取十六进制文件下面我们单独介绍它们的功能,并在 Modelsim 的 Transcript 界面中打印这些信息。

$display() //自动换行
$display(“%b+%b=%d”,a,b,c);//格式“%b+%b=%d”格式控制,未指定时默认十进制
%h 或%H //以十六进制的形式输出
%a 或%D //以十进制的形式输出
%o或%O //以八进制的形式输出
%b 或%B //以二进制的形式输出

initial 只执行一次

$readmemb

$readmemb:用于读二进制文件函数。
$readmemh:用于读十六进制文件函数。

1
2
$readmemb("<数据文件名>",<存贮器名>);
$readmemb("<数据文件名>",<存贮器名>);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
`timescale 1ns/1ns
module tb_test()

integer i;
reg [7:0] a [20:0]; //数据宽度7 表示该存储器包含21个存储单,地址范围从0到20

initial begin
$readmemb("dffd.txt",a);
for(i = 0; i<=20 ;i=i+1) begin
#10;
$write("%s",a[i])
end
end
endmodule

reg [7:0] a [20:0];

1
reg [7:0] a [20:0];
  • reg [7:0] 表示定义8位宽的寄存器类型数据,每位索引范围为7(最高位)到0(最低位)

  • a [20:0] 表示定义深度为21的数组,索引范围为20到0

  • 整体含义:定义了一个包含21个存储单元的存储器,每个单元为8位寄存器,总存储容量为21×8位

实际应用:

该定义合法且常见于硬件描述中,可用于建模RAM、ROM等存储结
示例初始化方式:

1
reg [7:0] a [20:0] = '{21{8'h00}}; // 全部初始化为0

注意事项:

  • 存储器索引通常按降序定义(如[20:0]而非[0:20]),符合Verilog规范
  • 每个存储单元需通过完整下标访问(如a[15]表示第16个单元的8位数据)
    该定义可直接用于实际设计文件(如.v文件),综合工具会将其映射为实际硬件存储单元
1
2
3
4
5
case (sel)
1'b0: ;
1'b1: ;
default: ;
endcase

双向IO使用

1
2
3
4
5
inout io;
reg [7:0]out; 输出的数据

wire oe;//控制使能信号
assign io = oe?out[0]:1'bz;// out[0]取位

右移循环

1
2
3
4
5
6
7
8
reg [7:0]shift_a;

always@(posedge clk) begin
shift_a <={shift_a[0],shift_a[7:1]};
// shift_a[7] shift_a[6] shift_a[5] shift_a[4] shift_a[3] shift_a[2] shift_a[1] shift_a[0]
// shift_a[0] shift_a[7] shift_a[6] shift_a[5] shift_a[4] shift_a[3] shift_a[2] shift_a[1]
//上面的逻辑没一个时钟,就向右移动一位,如上面注释所示
end

左移循环

1
2
3
4
5
6
7
8
reg [7:0]shift_a;

always@(posedge clk) begin
shift_a <={shift_a[6:0],shift_a[7]};
// shift_a[7] shift_a[6] shift_a[5] shift_a[4] shift_a[3] shift_a[2] shift_a[1] shift_a[0]
// shift_a[6] shift_a[5] shift_a[4] shift_a[3] shift_a[2] shift_a[1] shift_a[0] shift_a[7]
//上面的逻辑没一个时钟,就向左移动一位,如上面注释所示
end

位拼接

1
2
3
4
5
6
7
8
wire [3:0]x;
wire [3:0]y;
wire [31:0]n;

assign n = {y,7{x}};//等效 有些编译无法通过
assign n = {y,x,x,x,x,x,x,x};

assign n = {y,x};//假如 y=a x=b, n= 32'hab

除法器

时序逻辑

连续赋值

1
2
3
4
5
6
7
8
9
input a;
input b;

output c;

assign c = a+b; //如果使用该方法些,c需要定义为 wire 线网类型

always@(a,b) //等效always@(a or b)
c = a+b; //如果使用该方法写,c需要定义为 reg 类型

只要是使用always中输出的结果,都需要定义reg类型。

1
2
3
4
5
6
7
8
module add2(a,b,c);
input a;
input b;
output reg [1:0]c;

always@(a or b)
c = a + b;
endmodule

所有敏感信号 always@(*)

1
2
3
4
5
6
7
8
9
10
11
12
13
module 
assign d = sel1?a:sel2?b:c;

always@(*) begin
case({sel1,sel2})
2'b00: d = c;
2'b01: d = b;
2'b10: d = a;
2'b11: d = a;
endcase
end

endmodule

前级仿真

功能仿真

设置仿真工具

在Quartus中,通过 Assignments > Settings > Simulation 选择仿真工具(如ModelSim),并确保 ‌不勾选Run gate-level simulation(否则会触发后仿真流程)‌。

配置测试文件
Native Link 选项卡中添加测试文件(如 testbench.v),并指定顶层模块名称需与RTL代码中的模块名一致‌

执行仿真
通过 Tools > Run RTL SimulationTools > Run Functional Simulation 启动前仿真,此时仅验证逻辑功能,无时序延迟‌

前仿真‌:基于RTL代码,速度快,用于功能验证‌

后级仿真

生成网表文件
完成布局布线后,Quartus会自动生成网表文件(.vo)和时序文件(.sdo),这些文件将用于后仿真‌

启用时序仿真
Settings > Simulation 中勾选 Run gate-level simulation,并确保已加载正确的器件库(如 altera_ver)‌

配置延时文件
在ModelSim中,需将生成的.sdo时序文件与网表文件一同加载,以反映实际布线延迟‌

后仿真‌:基于网表文件,包含布线延迟,用于时序验证‌

生成网表文件(.vo)和时序文件(.sdo)

参数 parameter

模块

1
2
3
4
5
6
module led(clk,rst,out);

parameter cnt_max = 49999;//计数值
....

endmodule

例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module led_top();

reg clk;
reg rst;
wire led_out1,led_out2,led_out3;

//产生了不同的输出频率 #(.cnt_max = 3999)设置模块中的参数
led #(.cnt_max = 3999) LED1(.clk(clk),.rst(rst),.out(led_out1));
led LED2(.clk(clk),.rst(rst),.out(led_out2));
led #(.cnt_max = 69999) LED3(.clk(clk),.rst(rst),.out(led_out3));

defparam LED2.cnt_max = 69999; //修改LED2中的cnt_max的值

endmodule

可以通过parameter 设置实例模块产生不同频率的输出,一个模块多场景应用,也可以使用端口出来设置值

parameter具体可以查看Verilog HDL 入门 (4) ;用于模块内部的参数定义,可通过模块例化时的#()语法修改值

defparam具体可以查看Verilog HDL 入门 (4) ;用于在例化后通过层次化路径强制修改已定义的parameter

冲突与优先级

若同一参数被#()defparam同时修改,defparam的赋值会最终生效(因其在编译后执行)。

但此用法易导致代码可读性差,且部分综合工具可能不支持defparam

设计建议

优先使用#()语法在例化时传递参数,避免混用defparam

defparam通常仅用于仿真调试或遗留代码维护

注意

1. 设计一个功能,分成控制模块与数据处理模块编写,然后通过顶层模块联合在一起

串口

收数据通过采样,在一定时间内通过采样次数去比较是高或低电平,最低采样次数大于16次,找中间点数据分析,