Verilog笔记

github传送门(练习写的代码,约束文件,仿真文件)


文章目录

  • Vivado
      • 基本流程
  • 关于封装IP核和使用Block Design
  • Verilog
      • module / 模块
      • always块
        • 组合逻辑
        • 时序
      • Generate块(书上p83)
      • task / function
      • 其他
    • 仿真
    • ip核的创建和使用方法:
  • 烧录
  • BASYS3
    • 按键
      • 防抖方法
    • 数码管
  • 函数
    • 实现固定频率的方法:
    • 数码管数字显示
  • 约束文件
  • 知识点
    • 进位和溢出

Vivado

基本流程

  1. 写代码。在sources里添加文件,把写好的模块粘贴进去并保存。
  2. [可选] 写仿真文件 (Simulation)(模板代码)。用来在电脑上测试代码的运行结果是否达到预期,如果没问题就可以下到板子上 (写好仿真文件后在最左边Simulation - Run Simulation)(莽的话可以不用仿真直接上板子)。
  3. 综合 (Synthesis)。先检查代码有没有毛病,然后根据代码机器自动生成最优电路图(可以在最左边Synthesis - Open Synthesized Design - Schematics里看到电路图)。
  4. 实现 (Implementation)。把综合的结果与现实中的板子具体联系起来(通过约束文件),相当于从软件走到了硬件。
  5. 生成比特流文件 (Bitstream)。生成的比特流文件就可以上传到板子上了。
  6. 上传到板子上,通过Hardware Manager操作。

关于封装IP核和使用Block Design

千万千万千万不要把IP核导入到源代码所在的工程文件里,测试的时候debug有你哭的。

Verilog

module / 模块

  • module里的内容都是并发执行的。
    不在always和initial块里的代码就是单纯调用这个module的时候才会执行。(如果输入改变了会调用这个module)

always块

  • 多个always块并发执行,也就是说不同的主循环函数并发执行。

  • always里不能实例化模块

  • 一般组合逻辑电路用阻塞赋值(=),时序逻辑电路用非阻塞赋值(<=)。

组合逻辑

  • always @(触发器变量)是组合逻辑电路,always @(posedge clk)是时序逻辑电路。

时序

  • posedge(positive edge) 是上升沿关键字,上升沿指的是变量从0变成1的那个瞬间。对应下降沿(negedge - negative edge)就是变量从1变成0的那一瞬间。因此@(posedge var)指的是对var的上升沿敏感,意思就是var达到上升沿的时候就会触发。

  • always @(<敏感信号表达式>)
    可以理解成onValueChangeListener,当敏感信号表达式的值发生变化时,就会执行always块。

  • 因此always @(posedge clk)代表在每次时钟clk到达上升沿的时候会执行一次(因为并发执行,所以时序逻辑电路用非阻塞赋值就可以实现与时钟上升沿同步修改变量),因此这个always块里的代码可以理解成主循环。

  • 不论上升沿clk还是下降沿rst,在触发时读取到clk和rst的值都是这两个信号接下来要变成的值。(即clk读取到的值为1,rst读取到的值为0)

Generate块(书上p83)

  • 用来在模块中循环实例化其他模块。关键字为generate,对应声明循环控制变量的关键字为genvar
    示例:
genvar i;
generate
	for(i=0; i<n; i=i+1) 
	begin: tag_name
		module_name_1 instance_name_1 (input_1,output_1);
		//可以继续实例化别的模块,如:
		//module_name_2 instance_name_2 (input_2,output_2);
	end
endgenerate
//执行完generate块后的实例名字分别为:tag_name[0].module_name_1, tag_name[1].module_name_1 ...

task / function

  • 直接reg、integer、wire不属于端口,调用task/function的时候没有对应的传入。
  • 带返回值的函数是function,只能定义输入,function的名字是输出;不带返回值的函数是task,可以定义输入和输出。
  • task的output如果有赋值的话至少在module和task中一处定义为reg
  • task和function内部只能是阻塞赋值
  • function的返回值可以当作常量
  • task和function可以带parameter,但 暂时没找到调用时改变参数的方法
  • task可以启动task和function,function只能启动function

其他

  • 带符号关键字为signed,默认不带符号
  • assign(连续赋值)只能对wire类型,变量赋值只能对reg类型
  • 注意时序电路在仿真时模块里counter的满足条件要小得多
  • initial块是初次运行时会执行一次。多个initial块也可以并发执行。
  • x是未知态,z是高阻态(可以理解成另一种未知态)
  • casez忽略z,casex既忽略z也忽略x
  • parameter要理解成常量。变量用reg。
  • 常数定义前的数字表示二进制的位数,与基数无关。比如4’ha等价于4’b1010。
  • 实例化一个模块的时候它的参数是可以改变的,方法有两种:
    module_name #(param1[, param2...]) instance_name (inputs,outputs);
    
    module_name instance_name(inputs, outputs);
    defparam instance_name.param_1 = param1[, instance_name.param_2 = param2...];
    
  • if-elsefor等语句不能直接写在模块里,需要写在always中。
  • 判断语句要想写在assign中,可以
    assign res = (op == 2'b00) ? res1 :
    			 (op == 2'b01) ? res2 :
    			 defaultres;
    
  • inputoutput等信号默认都是wire类型。一般只有在模块中有赋值语句时才需要将output信号定义为reg型。
  • 如果输入的位数与模块定义的输入位数不匹配,模块读进来时高位会自动补x。

仿真

常用的函数:

函数名 描述
$readmemb(filename, mem) 从文件读取二进制数据到 mem 数组
$readmemh(filename, mem) 从文件读取十六进制数据到 mem 数组
$display(“output” [, var] ); 在console中输出,语法类似c的printf,%b 二进制, %d, %h, %t表示时间,对应var = $time
$write(“output”[, var] ); 同display,但display自带换行,write不带

ip核的创建和使用方法:

http://www.digilent.com.cn/community/332.html%EF%BC%89


烧录

1.基本知识:

  • RAM(random access memory)即随机存储内存,这种存储器在断电时将丢 失其存储内容,故主要用于存储短时间使用的程序。
  • ROM(Read-Only Memory)即只读内存,是一种只能读出事先所存数据的固 态半导体存储器。
  • RAM和ROM分别对应电脑的内存和硬盘。

2.烧录含义:

  • 直接把代码上传到板子相当于上传到RAM(内存)里,烧录是把代码上传到ROM(硬盘)里。
  • 板子每次开机或重启的时候会直接从ROM里调用程序。

3.实现:

  • 参考《你的Basys 3第一个入门实验官方指导手册.pdf》第35页
  • 烧录时选择Memory Device型号:s25fl032p-spi-x1_x2_x4

BASYS3

  • 新建工程时选择板子型号:xc7a35tcpg236-1

  • BASYS3板子自带的时钟CLK频率是100MHz,对应引脚名为W5,固定常数:

parameter T1S = 100000000 // 即100M
parameter T1MS = 100000;
//或
parameter T1S = 10**8;
parameter T1MS = 10**5;

按键

按键按下为1,弹起为0

防抖方法

间隔一定时间读取两次按键值,如果两次读取结果一样则认为不抖了。

数码管

  • 数码管是一个四位带小数点的七段共阳极数码管(低电平有效,即0是点亮,1是熄灭),共12位。
    从高位(第11位)到低位(第0位)分别为:

    11 ~ 8位: 控制第几个数码管的显示情况。比如若第10位是0,后面7~0位的值就作用于第二个数码管的显示情况
    7 ~ 1位: 从最上面一横开始,顺时针一位对应一个数码管的一小段(一个小直线),中间横线为第1位
    0位: 小数点是否显示
    e.g.1011_00000000_1表示第二个数码管显示"8",小数点不亮,其他三个数码管均熄灭

  • 要想四个数码管显示不同的数字,控制数码管以很快的频率一次显示一位即可。
    (示例代码)

    针脚名 位数 含义
    W4 11 控制第1个数码管的显示情况
    V4 10 控制第2个数码管的显示情况
    U4 9 控制第3个数码管的显示情况
    U2 8 控制第4个数码管的显示情况
    W7 7 a
    W6 6 b
    U8 5 c
    V8 4 d
    U5 3 e
    V5 2 f
    U7 1 g
    V7 0 dp(小数点)

函数

实现固定频率的方法:

?这种方法不能在硬件上实现,会报错,只能在仿真的时候实现。

input CLK;
parameter T1MS = 10**5;
always @(posedge CLK) begin
    repeat(T1MS*200) @(posedge CLK) // wait 200ms
    //do something like changing input values here...
end

input CLK;
parameter T1MS = 10**5;
integer count = 0;

always @(posedge CLK) begin
	count <= count + 1;
	if (count == T1MS*200) begin
		count <= 0;
		//do every 200ms
		//do something like changing input values here...
	end
end

input CLK;
parameter T1MS = 10**5;
integer count = 0;
reg CLK_10HZ = 0;

always @(posedge CLK) begin
	count <= count + 1;
	if (count == T1MS*100) begin
		count <= 0;
		CLK_10HZ = ~CLK_10HZ;
	end
end

always @(posedge CLK_10HZ) begin
//do every 100ms / 10HZ
//do something like changing input values here...

end

数码管数字显示

代码在这


约束文件

约束文件是用来把代码里的input、output的变量名和板子上的管脚一一对应起来用的,格式:
配置引脚:
set_property PACKAGE_PIN 引脚名 [get_ports 对应代码里的端口变量名]
配置标准电压:
set_property IOSTANDARD LVCMOS33 [get_ports 对应代码里的端口变量名]
BASYS3板子上所有引脚对应的约束文件(记得端口名改成自己的):

知识点

进位和溢出

  • 进位只在涉及无符号数时有意义
  • 溢出只在使用有符号数时才有意义
    • 只有两操作数符号位相同,且与结果符号位不同时才溢出
    • overflow = x*y*~s + ~x*~y*s

你可能感兴趣的:(Verilog)