往下看之前,推荐大家先看一下这个:
度娘找到的一个我觉得写的还不错的教程,做实验之前看一下,哪怕刚开始看不懂也不要紧,到后面帮助很大的。
还有一个稍微高级一些的,涉及到一些设计规范。如果只是为了完成课程设计的话完全可以不看这个,好奇的同学可以看看:
http://www.360doc.com/content/15/0111/23/20647836_440011821.shtml
我主要说一下我认为比较容易被大家忽视的地方。凡是综合的时候会报error的东西我就不写了,那种错误软件会给出特别明确的提示,照着提示直接修改就行,总不至于连这个都改不对
所以我下面我就不说那些编译器会报错的东西了,主要还是一些我认为比较重要的注意事项以及大家开始的时候可能注意不到的东西。
开始用Verilog的时候需要有一个意识,虽然Verilog因为语法和C语言相似,也被称为类C语言,但是它和C有着本质的不同。C语言属于软件型语言,而Verilog属于硬件编程语言。C语言最后形成的是二进制代码,执行的时候是顺序执行的,而且一般情况下,程序是严格按照语句的设计来运行。而Verilog则是根据Verilog语句生成电路,最后由生成的电路完成设计者的功能。而生成的电路在某些情况下,并不能像C语言一样完成程序设计者的所有要求,在将程序语言综合成电路的过程中,如果程序的设计没有严格按照某些规范,最后生成的电路可能会有意想不到的逻辑产生,导致“明明程序的逻辑设计正确,但是电路不能正常工作”的现象。此外,设计出来的电路属于并发式执行而非顺序执行,所以在刚开始进行设计的时候,需要改变自己在C语言设计过程中产生的顺序性思维。
就我个人的体验来说,刚开始的时候最容易犯得错误就是在条件判断语句中没有将所有的条件都列出来,导致综合软件综合处多余的锁存器产生的错误。
说人话就是case语句之后没有default,if/else if语句的最后没有跟一个else将所有其它可能的条件都封锁到一个行为上。尤其是在用always语句写组合逻辑的时候更容易出现类似的错误。
如果出现了程序能正常通过软件的检查,变量的名称没有错误同时程序的逻辑看似正确但是电路不能正常工作的情况时,推荐优先检查该问题。开始编写代码的时候也推荐养成所有的case语句后面都有default,所有的if/else if 语句后面都有else的好习惯。
上面说过了,Verilog生成的电路是并发式执行命令的,但是这并不代表Verilog语句执行任务没有顺序。
在if/else if语句就存在执行顺序的问题,和C语言一样,靠前的判断条件如果先执行,那么后面的判断条件就不会进行判断。当多分支判断语句只有一个条件成立的时候,书写顺序对于执行结果没有影响,但是如果有两个以上条件成立,那么只有最靠前的那个会执行,所以设计的时候要让自己希望执行的部分靠前,不希望执行的靠后,或者充分利用其他的条件,使每种情况下都只有一个判断条件成立。
进行图形化设计的时候,可能会需要一些引脚恒为0或者恒为1。需要恒为1接vcc,需要恒为0接gnd。
关于如何找Vcc和gnd:
双击quartus的图形设计界面的空白位置:
弹出一个器件选择窗口,在图中所示的文本框中输入vcc或者gnd即可直接出现vcc、gnd器件。同理也可用该方法查找其它器件(用),也可以在上面的libraries中查找需要的器件:
自己设计封装的元器件也可以在这个界面调用,点击那个浏览按钮:
然后找到你想使用的bsf文件:
然后直接添加到电路中即可。需要注意的是和你添加的bsf相关的电路文件也是要一起复制到你的工程的目录下的,不然不能使用。
关于图形化设计界面的接线方式,除了直接进行连接之外,quartus会自动将所有同名的线连接在一起。所以只需要从接口上引出一根线,然后双击连接线,将需要连接的接口的线命名为一个名字就可以了,如图所示,两根分离的线都被命名为CPMAR:
对于一组线的情况,命名方式如下:
线名[高位..低位]
如
bus[15..0]
中间是两个英文的句号。
想要连接其中的几根线的方式也很简单,比如连接一根线:
bus[7]
连接多根线
bus[9..2]
此外, 多根线需要用bus line来连接的,就是图中粗的那个:
如果想把洗的node line编程粗的bus line,只要选择对应的线,右键,选择bus line即可
之前说到了如何调用自己封装的模块,那么下面就说一下如何封装你的模块。
选中你要封装的模块(图形化设计或者Verilog设计都可以),选择file->Create/Update->Create Symbol Files for Current File:
然后在弹出的窗口中直接保存即可,可以修改文件名:
如果需要修改生成的bsf文件的外观,可以在工程目录下找到生成的bsf文件,双击打开,即可修改外观:
另外如果只是在Verilog语言中调用模块的话,不需要封装,直接调用就可以。
用Verilog语言调用模块的时候,调用方式很简单只要按照:
被调用模块名 部件名(.接口1(线1), .接口2(线2).......)
比如被调用的模块定义为:
module example(a, b, c);
.......
....
endmodule
那么调用这个模块的时候语法就是:
example example_0(.a(wire1), .b(wire2), .c(wire3));
还有一种调用方式,就是省掉 . +接口名部分,直接按照顺序在调用模块的括号中写所有对应的wire,如:
example example_0(wire1, wire2, wire3);
这样也是可以的,但是缺点在于当接口比较多的时候,会出现自己分不清那个接口对应的是哪一根线的情况,而且这个方式中,哪一个接口和哪一根线对应完全依赖于顺序,更容易出错。所以推荐使用第一种方法。
寄存器组在Verilog中是这样的:
reg [15:0]regfile[31:0]
表示每一个寄存器的位数为16位(150共16个),总共有32个这样的寄存器(310共32个)。
如果想要调用第25个寄存器,调用方式为
regfile[25]
如果想要调用第25个寄存器的某几位,调用方式为:
regfile[25][14:7]
wire型变量。刚开始的时候可能有的人不清楚wire型变量到底是个啥,其实wire型变量就想到于图形化设计电路的时候的那一根根连线,使用wire型变量既可以在模块调用的时候直接使用,也可以使用
assign wire名称=wire名称/寄存器
的方式调用
用assign语句书写条件分支语句。说到条件判断,我们最容易想到的是用if/else或者case语句来实现。其实很多时候我们可以用assign实现分支条件语句的书写,相比于使用需要always块的if/else和case相比,更不容易出错。
方式如下:
assign 变量值=判断条件1?事件1:
判断条件2?事件2:
判断条件3?事件3:
事件4;
当然用更多的判断条件实现更多的时间。
这个方式其实就是
assign 变量值=判断条件?事件1:事件2;
语句的一种应用。在C语言中也存在相同的语句可以使用。
reg,wire类型变量的数量和位数对应关系。如果定义一个8位的reg变量,我们有无数种定义方式:
[7:0]寄存器名
[8:1]寄存器名
[16:9]寄存器名
具体用哪一种,一来要根据情况而定,二来要考虑到自己的使用习惯。如果对于没有特殊要求的寄存器,最好养成把最低位定义成统一的数的习惯(比如0或者1)。对于有特殊要求的寄存器,比如要保存某[15:0]寄存器中数据的高8位,定义的时候也不要定义成[7:0],最好按照[15:0]寄存器中的格式定义为[15:8],这样调用的时候逻辑更加清晰。
善用parameter。parameter就相当于C语言中的宏定义。比如在大二下学期计算机组成原理课设中需要用到的状态机中,状态的定义方式:
parameter s0=4'b0000,s1=4'b0001,s2=4'b0010,s3=4'b0011,s4=4'b0100,s5=4'b0101,s6=4'b0110,s7=4'b0111;
parameter s8=4'b1000,s9=4'b1001,s10=4'b1010,s11=4'b1011,s12=4'b1100,s13=4'b1101,s14=4'b1110;
之后调用的时候就可以直接使用s0,s1来进行操作,修改的时候也分方便。体验过C语言宏定义好处的人,应该就不用我多说在后期修改的时候这种定义方式有多方便了吧。
对于多层嵌套的begin,end语句,可以再开始的时候对每一条begin,end加注释,通过注释实现每一条begin,end的对应。
比如:
always @(xxxx)
begin //always begin
if(条件1)
begin //if(条件1) begin
if(条件2)
begin //if(条件2) begin
end //if(条件2) end
end //if(条件1) end
end //always end
通过这种方式,begin和end的对应关系就不会乱了。
如何设置顶层文件。当工程涉及到多个模块时,可能会出现工程的顶层文件(即仿真以及下载的时候真正需要进行操作的那个文件)和我们设想的不一样。比如在这个工程中,这个是我现在的顶层文件:
可是我需要的顶层文件实际上是这个:
那么那么首先我们要在files里面找到这个文件,然后右键,选择Set as Top-Level Entity即可:
同样的,如果你想要单独测试这个工程里某一个子模块的功能,只要将那个子模块设置成top-level就可以直接对这个模块进行测试了。
进行仿真的时候可能会涉及到多个仿真文件需要切换,这时候就需要我们手动选择仿真文件。选择processing-simulator tools
然后会出现这个窗口,找到你想要使用的仿真文件,然后点击start即可进行仿真。如果start之后发现仿真的窗口没有出现,那就点击start旁边的open即可打开仿真结果窗口:
有的时候我们会希望多位信号直接等于某一个数,操作如下。选中你想要修改的信号区域,然后双击,会弹出一个修改窗口
在Radix中选择数据格式,比如十进制,十六进制,ASCII码等等,然后在下方输入数值。
点击OK之后就会看到变化:
添加重复度高的元器件。有的时候可能会遇到这样的模块:
这是只有两个重复的情况,实际设计中可能还会出现更多的情况。对于这种的,其实完全没有必要一个一个去连接,用一些批量修改的方法可以让工作量减小特别多。就拿这个举例,开始的时候先添加这个74244芯片,然后在一个接口上添加一根线,命名为dataregout[]:
然后将这根线复制4跟,然后再将四根一起复制到下面四个端口上(按住ctrl拖动可直接复制)。对于另一边进行同样的操作,同时将其他单独的线也接好,如图:
连接好了之后可以拖动一下元器件,看看连接线有没有跟着一起移动,如果没有跟着一起移动说明没接上,一起移动了就说明连接成功。然后,需要几个重复的器件,就将整个器件和所有的连接线都选中,然后按住ctrl+鼠标左键拖动到你想要的位置。
然后双击第一个线名,按方向键左键一次,即可将输入光标移动到方括号中,直接输入对应的数字,然后回车,会自动跳到下一条线的名字上,继续进行同样的操作直到所有的线命名完毕:
然后对所有的部件进行同样的操作即可。
不但速度快,而且错误率低,视觉效果更整齐。