Verilog笔记

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


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有你哭的。git

Verilog

module / 模块

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

always块

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

  • always里不能实例化模块web

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

组合逻辑

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

时序

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

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

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

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

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