IP:知识产权核
Verilog HDL/VHDL:硬件描述语言 .qpf是工程文件,.v是源代码文件。
integer是有符号数,而人是无符号数。
将负数赋值给变量时使用二进制的补码运算。
位操作时都是从低位对齐,位数不齐的会在高位自动补零。
逻辑运算符的运算结果只有一位,即1,0,X(未知/高阻态)。
Verilog代码详细讲解
代码重点关注:1).输入输出。2).信号位宽。3).计数 等功能。
外面信号给到FPGA里面的是输入,用input; 从FPGA里面出来的信号是输出,用output;
位宽例:output[x:0] led;在led这条线上有(x+1)个管脚(一般是I/O管脚,即led的位宽=(x+1)位,也是有(x+1)比特。比特(bit)就是位的意思。
取x=1,则led赋值就要写成led <= 2'b00(阻塞赋值);或led = 2'b00(阻塞赋值);等号后面的格式为: 位宽'b(比特)xx..(信号个数状态)
注意:数在机器里面是用二进制表示的,即用时注意位宽;如:reg[7:0] aa;aa最大表示只能=1111 1111,即aa=2^8-1=255。
一般的信号(输入输出信号)赋值最好用 :信号x = 位宽'b(比特)xx.. 这种形式表示。
注:Verilog代码是不按顺序执行的,比如各个always块是同时执行的。
1.1设计时用不到的语法
①initial【设计时不用,仿真时用】
②task/function【设计时不用,仿真时很少用】
③for/while/repeat/forever【设计时不用,仿真时很少用】
④integer【设计时不用】
⑤模块内不能有X态,Z态,内部不能有三态接口
⑥cases/casez【设计时仿真时都不用】
⑦force/wait/fork【设计时不用,仿真时很少用】
⑧#5【设计时不用,仿真时用】
1.2设计时使用语法
①reg/wire,parameter
②assign【建议改名时使用】,always
③只允许使用if else和case两种条件语句
④各种运算符
2.1电路设计的3种结构 【posedge是上升沿,negedge是下降沿】
①组合逻辑(无clk信号)【always @ (*)】//如果没有@,那就是不会满足特定条件才执行,而是执行完一次后立马又重复执行,
always @ (*) //*代表敏感变量,敏感变量由综合器根据always里面的输入变量自动添加,不用自己考虑。
begin
语句...
end
②时序逻辑(两种(必有clk信号))【always @ (posedge clk)】
第一种:同步复位的时序电路(只和时钟信号有关)
always @ (posedge clk)begin
if(rst_n == 1'b0)begin
语句...
else begin
语句...
end
end
第一种:异步复位的时序电路(与复位信号和时钟信号有关)
always @ (posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
语句
else begin
语句
end
end
3.电路规范设计的要点注意:(三种电路,两种条件,一一法则。注意理解字面意思)
①一个always只产生一个信号,就是说always里面最好只能有一个信号变化(方便信号观察),但是--》请看下面的引用
为什么会有一个“always里面只输出/产生一个信号,避免互相干扰”这样的说法?因为在很早的时候,综合器功能还不是很健全,把多个信号写在一个always块里,可能会得到与你期待不同的结果。而将每个信号单独写在一个always块里,综合器可以比较容易识别,得到你想要的结果。但是,现在不一样的了,综合器已经十分智能。所以,在always里写多个信号也是可以的,但是需要注意,组合逻辑和时序逻辑最好分开,另外不能用程序化的思维去思考。这段解释来自(https://www.zhihu.com/question/37293913/answer/72097040)
②一个信号只能在一个always里面产生(这点重点注意,就是说信号变量只能在单独的一个always语句中赋值,不能出现在两个或以上的always语句中赋值的情况)
③always是描述一个信号产生的方法,记载什么情况下,这个信号的值是多少;在其他情况下,值为多少(要全部考虑)
④条件判断只允许使用if else和case,其他全都不用。
⑤有posedge或negedge的,一定是D触发器,是时序电路
⑥设计时,如果想立即有结果的,那就用组合逻辑;如果想延时一拍有结果,那就用时序逻辑;
4例化(自己去了解)
4.1例化()
4.2例化方法举例:
module qq( ------->>>>>> qq(设计模块名) u_qq(例化模块名)(
clk; ------->>>>>> .clk ();
rst_n; ------->>>>>> .rst_n ();
vld_in; ------->>>>>> .vld_in ();
. .
. .
. .
rdy_in ------->>>>>> .rdy_in (里面放例化模块的信号)
); );
4.3参数例化。
5.1信号类型(reg/wire)讲解
①设计代码仅用reg和wire。
②设计代码:由本模块产生且是用always产生的信号,则用reg类型
③测试代码:用initial产生的信号(一般是对测试模块的输入),用reg类型
④其他都用wire。
⑤reg信号,不一定产生寄存器。
5.2参数paramete
①如:parameter QQ=12;----->>>>> :就是将整个模块内的QQ替换成12.
②要求参数名要大写。
5.2运算符(注意运算之后位宽的问题,运算之后的位宽可能会改变,注意 )
算术运算符(FPGA里面尽量用+,-,*不用或少用/,%(用到大量的逻辑块))
①时序逻辑用 "<=" (注:"<="不是判断,它在判断里面使用才被认为是判断符,在其他地方都是赋值符号)
②组合逻辑或assign语句里面用 "="。(组合逻辑敏感列表是*号,时序逻辑是posedge信号或者negedge信号)
③逻辑运算符(&&,||,!),一般两边都是1比特信号,一般用于条件判断
④位运算符(~,|,^,&)一般用于赋值语句,在不同长度的数据进行位运算时,系统会自动的将两个数右端对齐,
位数少的操作数会在相应的高位补0,一时的两个操作数按位进行操作。
⑤移位运算符(<<,>>)一般用于乘除运算。右移多少位,相当于除以2的多少次方,左移相反。注意一下:
4'b1001<<1=5'b10010; 4'b1001<<2=6'b100100; (左移会使得位数增加?)
1<<6=32'b1000000; 4'b1001>>1=4'b0100; (右移不会改变位数?)
但是如果操作数已经定义了位宽,则进行移位后操作数改变,但是其位宽不变。
4‘b1001>>4=4'b0000;
⑥拼接运算符({}),{信号1的某几位,信号2的某几位,......信号n的某几位} 将某些信号的某些为列出来,中间用逗号分开,最后用大括号括起来表示一个整体的信号。
在位拼接的表达式中不允许存在没有指明位数的信号。
{a,b[3:0],w,3'b101} //等同于{a,b[3],b[2],b[1],b[0],w,1b'1,1'b0,1'b1}
{4{w}} //等同于{w,w,w,w}
{b,{3{a,b}}} //等同于{b,a,b,a,b,a,b} 这里面的3、4必须是常量表达式。
⑦缩减运算符
这是单目运算符,也包括与、或、非运算。运算规则与位运算相似,不过是对单个运算符的每一位逐步运算,最后的运算结果是一位的二进制数。
c=&B; //意思同c=((B[0]&B[1]) &B[2] ) & B[3];
6赋值语句
在Verilog HDL语言中,信号有两种赋值方式。
A)非阻塞赋值(Non-Blocking)方式(如:b<=a;)
(1)在语句块中,上面语句所赋值的变量不能立即为下面的语句所用;
(2)块结束后才能完成这次赋值操作,赋值的职位上次赋值得到的;
(3)在编写可综合的时序逻辑模块时,这是最常用的复制方法。
B)阻塞赋值(Blocking)方式(如:b=a;)
(1)赋值语句完成后,块才结束;
(2)b的值在赋值语句执行完后立刻改变。
(3)在时序逻辑中使用时,可能会产生意想不到的结果。