速度过Verilog 知识
关键点1. 连续赋值语句目标只能是线网类型wire
关键点2.计算相对来说没有延迟 计算的结果会立即赋给左边信号
关键点3.连续赋值语句不能出现在过程块中
在前面我们讲述的行为级建模时指出了,行为级建模的过程块分为了两种一种是过程语句(Intial always) 还有一种就是语句块 ,采用过程语句对组合电路进行描述时,作为全部的输入信号 需要列入敏感信号列表
对时序电路进行描述时,需要把时间信号和部分输入信号列入敏感信号列表
下面展示可综合的语句
过程语句always initial也是过程语句但是他不能被综合
语句块:串行语句块 begin end
赋值语句: assign , = , <=
条件语句 : case ,casex , casez
循环语句 : for
1,过程语句中被赋值的信号必须定义成reg类型
2,串行语句块 begin end
3,过程赋值语句有阻塞和非阻塞之分
结构化建模是将硬件描述成一个分级的子模块系统,通过逐层调用这些模块构成功能复杂的数字逻辑电路和系统的一种描述方式。
模块结构化 有三类
1.模块级建模
2.门级建模
3.开关级建模
大方向
组合逻辑电路
时序逻辑电路
还有一些小题后面介绍 这里讲述大概的过程
仿真验证和testbench 的编写
仿真也叫模拟,就是通过输入测试信号,比对输出信号来确定是否得到与期望所一致的正确的设计结果。
在仿真代码中,其中原本的激励信号定义为reg型 显示信号定义为wire型
testbench是一个测试平台 信号集成在模块内部,没有输入输出。
tb 其实我学的不是很好
先把被测模块进行实例化
再产生测试激励
举例#50 clk=~clk 这种就是会产生以100为周期的标准时间单元
timescale 1ns/1ns
还有1ns/100ps 这里说的前面1ns是刻度
后面的100ps是精度 就是你在波形图上识别的最小刻度
tb中最难点就是任务与函数了
任务和函数的作用是再较大的行为级设计划分为较小的代码段,允许设计者在多个地方重复使用的相同代码提取出来
1、在第一行task语句中不能列出端口名称。
2、任务的输入、输出和双向端口数量不受限制,甚至可以没有输入、输出和双向端口。
3、在任务定义的描述语句中,可以出现不可综合操作符合语句,但这样会造成任务不可综合。
4、在任务中可以调用其他的任务或函数,也可以调用自身。
5、在任务定义结构中不可出现initial和always语句。
6、在任务定义中可以出现“disable中止语句“,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回调用任务的地方继续执行。
我觉得这里最重要的是第五点 ,在任务定义中一般不可以出现intial always语句重要重要重要
<任务名>(端口1,端口2,…,端口n);
在调用task时,必须要注意一下几点:
1、task的调用是过程性语句,因此只能出现在always过程块和initial过程块中,调用task的输出与输入参数必须是reg类型的
2、task调用语句中的列表必须与task定于时的输入,输出,双向端口参数说明的顺序相匹配
3、在调用task时,参数按照值传递,而不能按照地址传递
4、在一个task中,可也直接访问上一级调用模块中的任何寄存器
5、可以使用循环中断控制语句disable来终端任务执行,当task被中断后,程序流程将返回调用任务的地方继续执行。
我觉得这里最重要的是第一点 , task只出现在过程语句也就是intial 和 always这些过程语句
在我的理解中是这样的,task既然是叫任务,那么设计师在设计这个task东西的时候就有想法, 我们把一些复杂的情况,重复使用的东西封装在一起 ,变成一个任务模块。就像我们红绿灯在等待的时候,我测试那么只要稍微改动几个灯的颜色,观察最后结果
module traffic_lights;
reg clock, red, amber, green;
parameter on=1, off=0, red_tics=350,
amber_tics=30,green_tics=200;
//交通灯初始化
initial red=off;
initial amber=off;
initial green=off;
//交通灯控制时序
always
begin
red=on; //开红灯
light(red,red_tics); //调用等待任务
green=on; //开绿灯
light(green,green_tics); //等待
amber=on; //开黄灯
light(amber,amber_tics); //等待
end
//定义交通灯开启时间的任务
task light(color,tics);
output color;
input[31:0] tics;
begin
repeat(tics) @(posedge clock);//等待tics个时钟的上升沿
color=off;//关灯
end
endtask
//产生时钟脉冲的always块
always
begin
#100 clock=0;
#100 clock=1;
end
endmodule
我们把task当成了一种拿来主义的操作,填写几个数字就可以了。在定义中给了task的是,task(输出颜色,输入时间)
然后在上面的使用中直接用
light(green,green_tics);
(1) function定义结构不能出现在任何一个always块或者initial过程块中,与task定义相同
(2) 不允许输出output端口声明(包括输出和双向端口) ,但可以有多个input输入端口
(3) [range]参数指定函数返回值的类型或位宽,是一个可选项,若没有指定,默认缺省值为宽度 1 bit的寄存器数据
(4) function_name为所定义函数的名称,对函数的调用也是通过函数名完成的,并在函数结构体内部代表一个内部变量,函数调用的返回值就是通过函数名变量传递给调用语句。函数定义在函数内部会隐式定义一个寄存器变量,该寄存器变量和函数同名并且位宽也一致,函数通过在函数定义中对该寄存器的显式赋值来返回函数计算结果。
(5) input_declaration为各个输入端口的位宽和类型进行说明,在函数定义中至少要有一个输入端口
(6) function定义不能包括有任何时间控制语句,即任何#,@或者wait来标识的语句,和task不同
我觉得在这里最重要的是第五第六点 就第五点来说是这样的我觉得不得不佩服发明这个的人的智慧 function 叫函数 哦我们从小到大在数学中接触过函数f(x) 所以其实同理我们在定义的时候 定义一个函数 那要个屁的结果,y=f(x)是一个完整的函数 y的出现是在函数实现时必要的,在这里的定义中那么是不是 不能出现Output 但是但是必须出现x的值 ,并且也可以出现多个input 既然有"x"的值了,声明时候记得加入范围 这是第五点的缘由。对于第六点来说是这样的,task是一个任务主要的是我们用来直接实现的,它内部可以带一点触发性质的比如说repeat@(posedge clk) 但是不能出现always 块 这种过程语句 你是一个实现任务要always干嘛,那函数就更 容易理解了,我们设计出函数只是为了更加快速的操作 比如设计了 a+b 那么只要填入 1 2 再来一个数字接收结果就行了,不仅不能出现过程块而且不能出现时间相关的控制叙述 (我就求一个值,我还需要等时间,那我为什么不用…)
1、 函数的调用可以在过程块中完成,也可以在assign这样的连续赋值语句中出现,这与task有些许不同
2、函数调用语句不能单独作为一条语句出现,只能作为赋值语句的右端操作数
3 、函数可以调用函数,但是不能调用任务
4、函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。
说明
为什么使用我就可以再任何地方因为还是那句话f(x) 的使用 我并不包含任何时间分量 全是静态的,我只是用来求值 那么assign y=F(x) 可以用在always中在什么时候等于f(x)不是都可以的吗
module tryfact;
//函数定义------------------------------
function [31:0] factorial;
input [3:0] operand;
reg [3:0] index;
begin
factorial = operand ? 1 : 0;
for(index = 2; index <= operand; index = index + 1)
factorial = index * factorial;
end
endfunction
//函数的测试----------------------------------
reg [31:0] result;
reg [3:0] n;
initial begin
result = 1;
// 0的阶乘为1, 1的阶乘也为1
for (n = 2; n < 9; n = n + 1) begin
result = factorial(n);
$display("正整数 n= %d的阶乘为 result= %d", n, result);
end
$display ("Finalresult = %d",result);
end
endmodule//模块结束
intial 语句中的变量必须为reg类型
之前的wire 变reg 不就相当于还是处理的那部分结果嘛
UDP 用户自定义元件模型
既然有可综合的整体模块调用那么就额可以有不可综合的UDP 类似于模块
最大的区别是输出只有一个并且是reg类型
输入可以有很多个 所有端口的位宽必须是1比特