Verilog学习心得Word下载.docx
《Verilog学习心得Word下载.docx》由会员分享,可在线阅读,更多相关《Verilog学习心得Word下载.docx(9页珍藏版)》请在冰豆网上搜索。
inputa,b,cin;
outputsum,cout;
assign{cout,sum}=a+b+cin;
endmodule
然后在执行8-bit补码加减运算的ALUmodule中实例化8个FullAdder,表示ALU用到了8个FullAdder。
moduleALU(a,b,result,cout,is_add);
input[7:
0]
a,b;
input
is_add;
output[7:
0]result;
output
cout;
wire[7:
0]b_not=~b;
0]b_in=is_add?
b:
b_not;
0]carry;
assigncarry[0]=is_add?
1'
b0:
b1;
//module实例化
//8-bitrippleadder
FullAdderfa0(a[0],b_in[0],carry[0],result[0],carry[1]);
FullAdderfa1(a[1],b_in[1],carry[1],result[1],carry[2]);
FullAdderfa2(a[2],b_in[2],carry[2],result[2],carry[3]);
FullAdderfa3(a[3],b_in[3],carry[3],result[3],carry[4]);
FullAdderfa4(a[4],b_in[4],carry[4],result[4],carry[5]);
FullAdderfa5(a[5],b_in[5],carry[5],result[5],carry[6]);
FullAdderfa6(a[6],b_in[6],carry[6],result[6],carry[7]);
FullAdderfa7(a[7],b_in[7],carry[7],result[7],cout);
对应在C++中先写FullAdderclass,然后在ALUclass中以FullAdder作为datamember。
classFullAdder
{
};
classALU
FullAdderfa[8];
另外一点,moudle声明port的方式,像是从早期C语言的函数定义中学来的:
char*strcpy(dst,src)
char*dst;
char*src;
{
//...
}
2.Verilog中的模块调用时,指定端口可以使用名称绑定。
C++在调用函数时,参数只能按顺序书写。
例如memset()的原型是:
void*memset(void*s,intc,size_tn);
如果你想将某个buf清零,应该这么写:
charbuf[256];
memset(buf,0,sizeof(buf));
但是如果你不小心写成了:
memset(buf,sizeof(buf),0);
编译器不会报错,但运行的实际效果是根本没有对buf清零。
(记得RichardStevens的书里提到过这一点。
)
在Verilog中,如果要写一个测试ALU的module,那么其中对ALU实例化的指令可以这么写:
modulealu_test;
reg[8:
0]a_in,b_in;
reg
op_in;
wire[7:
0]result_out;
wire
carry_out;
ALUalu0(.a(a_in[7:
0]),.b(b_in[7:
0]),.is_add(op_in),
.result(result_out),.cout(carry_out));
//...
endmodule
这样就比较容易检查接线错误。
另外,在C++中,如果所有参数类型不同,而且之间没有隐式类型转换,那么可以利用C++的强类型机制在编译期检查出这种调用错误。
3.Verilog中把大括弧{}用作bit的并置,因此语句块要用begin/end标示。
Verilog中小括号()和中括号[]的作用与C++中类似,前者用于函数或模块调用,后者用于下标索引。
我想如果Verilog把尖括号<
>
用作bit并置的话,就能把大括号{}解放出来,用作标示语句块,这样写起来更舒服一些。
4.
Verilog本质上是测试驱动开发的。
对于每个module都应该有对应的testbench(或称testfixture)。
比较好的情况是,一个工程师写module,另一个工程师写对应的testbench,这样很容易检查出对电路功能需求理解不一致的地方。
因此还可以说Verilog主张结对编程(pairprogramming)。
例如对前面的ALUmodule的testbench可以写成:
`timescale1ns/1ns
reg[9:
0]get,expected;
has_error;
initialbegin
has_error=1'
b0;
op_in=1'
//testaddition
for(a_in=9'
a_in!
=256;
a_in=a_in+1)
for(b_in=9'
b_in!
b_in=b_in+1)begin
#1;
get={carry_out,result_out};
expected=a_in+b_in;
if(get!
==expected)begin
$display("
a_in=%d,b_in=%d,expected%d,get%d"
a_in,b_in,expected,get);
end
end
//testsubtraction
//...
if(has_error===1'
b0)begin
ALLTESTSPASSED!
"
);
$finish;
5.Verilog比起VHDL的不足之处在于,它只能定义concreteclass,不能定义abstractclass。
也就是说interface和implementation不能分离。
这在设计大型电路时就显得表现力不足。
不过这关系不大,因为可以在编译时选择同一模块的不同实现版本,间接实现了接口与实现的分离。
在VHDL中,强制将接口与实现分离。
对每个模块,你都得先写接口(定义输入输出信号),即ENTITY;
然后至少写一份实现,即ARCHITECTURE。
每个ENTITY可以有不止一份实现,例如可以有行为描述的,也有数据流描述的。
然后在配置文件中选择该ENTITY到底用哪一份实现。
举例来说(选自《VHDL入门·
解惑·
经典实例·
经验总结》一书),分频器模块可以这么写,先定义其接口FreqDevider,然后定义两份实现Behavior和Dataflow:
LIBRARYIEEE;
USEIEEE.Std_Logic_1164.All;
ENTITYFreqDeviderIS
PORT
(Clock
:
IN
Std_Logic;
Clkout:
OUTStd_Logic
END;
ARCHITECTUREBehaviorOFFreqDeviderIS
SIGNALClk:
BEGIN
PROCESS(Clock)
BEGIN
IFrising_edge(Clock)THEN
Clk<
=NOTClk;
ENDIF;
ENDPROCESS;
Clkout<
=Clk;
ARCHITECTUREDataflowOFFreqDeviderIS
--signaldeclarations
--processes
在C++中,既可以写concreteclass,也可以写abstractclass。
比Verilog和VHDL都方便。
6.Verilog和VHDL都有模板的概念,Verilog称为参数(parameter),VHDL称为类属(generic)。
不过好像都只能用整数作为模板参数,不能像C++那样用类型作为模板参数。
7.目前来看,Verilog是硬件描述语言,不是硬件设计语言。
在用Verilog设计电路的时候,我们是把脑子中想好的电路用Verilog“描述”出来:
哪里是寄存器、哪里是组合逻辑、数据通路是怎样、流水线如何运作等等都要在脑子里有清晰的映象。
然后用RTL代码写出来,经过综合器综合出的电路与大脑中的设想相比八九不离十。
这就像说C语言是可移植的汇编语言,以前好的C程序员在写代码的时候,能够知道每条语句背后对应的汇编代码是什么。
verilog设计经验点滴
1,敏感变量的描述完备性
Verilog中,用always块设计组合逻辑电路时,在赋值表达式右端参与赋值的所有信号都必须在always@(敏感电平列表)中列出,always中if语句的判断表达式必须在敏感电平列表中列出。
如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,在综合时将会为没有列出的信号隐含地产生一个透明锁存器。
这是因为该信号的变化不会立刻引起所赋值的变化,而必须等到敏感电平列表中的某一个信号变化时,它的作用才表现出来,即相当于存在一个透明锁存器,把该信号的变化暂存起来,待敏感电平列表中的某一个信号变化时再起作用,纯组合逻辑电路不可能作到这一点。
综合器会发出警告。
Example1:
inputa,b,c;
rege,d;
always@(aorborc)
begin
e=d&
a&
b;
/*d没有在敏感电平列表中,d变化时e不会立刻变化,直到a,b,c中某一个变化*/
d=e|c;
end
Example2:
always@(aorborcord)
/*d在敏感电平列表中,d变化时e立刻变化*/
2,条件的描述完备性
如果if语句和case语句的条件描述不完备,也会造成不必要的锁存器。
if(a==1'
b1)q=1'
//如果a==1'
b0,q=?
q将保持原值不变,生成锁存器!
else
q=1'
//q有明确的值。
不会生成锁存器!
Example3:
reg[1:
0]a,q;
....
case(a)
2'
b00:
q=2'
b00;
b01:
b11;
//如果a==2'
b10或a==2'
b11,q=?
q将保持原值不变,锁存器!
endcase
Example4:
default:
Verilog中端口的描述
1,端口的位宽最好定义在I/O说明中,不要放在数据类型定义中;
moduletest(addr,read,write,datain,dataout)
input[7:
datain;
input[15:
0]addr;
input
read,write;
output[7:
0]dataout;
//要这样定义端口的位宽!
wireaddr,read,write,datain;
reg
dataout;
datain,addr,read,write;
outputdataout;
wire[15:
reg[7:
//不要这样定义端口的位宽!
!
2,端口的I/O与数据类型的关系:
端口的I/O
端口的数据类型
module内部
module外部
wire或reg
output
wire或reg
wire
inout
3,assign语句的左端变量必须是wire;
直接用"
="
给变量赋值时左端变量必须是reg!
Example:
assigna=b;
//a必须被定义为wire!
********
begin
a=b;
//a必须被定义为reg!
end
VHDL中STD_LOGIC_VECTOR和INTEGER的区别
例如A是INTEGER型,范围从0到255;
B是STD_LOGIC_VECTOR,定义为8位。
A累加到255时,再加1就一直保持255不变,不会自动反转到0,除非令其为0;
而B累加到255时,再加1就会自动反转到0。
所以在使用时要特别注意!
以触发器为例说明描述的规范性
1,无置位/清零的时序逻辑
always@(posedgeCLK)
Q<
=D;
2,有异步置位/清零的时序逻辑
异步置位/清零是与时钟无关的,当异步置位/清零信号到来时,触发器的输出立即被置为1或0,不需要等到时钟沿到来才置位/清零。
所以,必须要把置位/清零信号列入always块的事件控制表达式。
always@(posedgeCLKornegedgeRESET)
if(!
RESET)
Q=0;
else
3,有同步置位/清零的时序逻辑
同步置位/清零是指只有在时钟的有效跳变时刻置位/清零,才能使触发器的输出分别转换为1或0。
所以,不要把置位/清零信号列入always块的事件控制表达式。
但是必须在always块中首先检查置位/清零信号的电平。
always@(posedgeCLK)
结构规范性
在整个芯片设计项目中,行为设计和结构设计的编码是最重要的一个步骤。
它对逻辑综合和布线结果、时序测定、校验能力、测试能力甚至产品支持都有重要的影响。
考虑到仿真器和真实的逻辑电路之间的差异,为了有效的进行仿真测试:
1,避免使用内部生成的时钟
内部生成的时钟称为门生时钟(gatedclock)。
如果外部输入时钟和门生时钟同时驱动,则不可避免的两者的步调不一致,造成逻辑混乱。
而且,门生时钟将会增加测试的难度和时间。
2,绝对避免使用内部生成的异步置位/清零信号
内部生成的置位/清零信号会引起测试问题。
使某些输出信号被置位或清零,无法正常测试。
3,避免使用锁存器
锁存器可能引起测试问题。
对于测试向量自动生成(ATPG),
为了使扫描进行,锁存器需要置为透明模式(transparentmode),
反过来,测试锁存器需要构造特定的向量,这可非同一般。
4,时序过程要有明确的复位值
使触发器带有复位端,在制造测试、ATPG以及模拟初始化时,可以对整个电路进行快速复位。
5,避免模块内的三态/双向
内部三态信号在制造测试和逻辑综合过程中难于处理.
补充不知你看了verilog2001版本吗?
现在的verilog在尽量往C语言的风格上靠拢。
1。
敏感变量的描述完备性,我现在用always实现组合逻辑时,都是写成always@(*),这样很很好,自动把所有右端赋值信号加入。
2。
module编写时这样更好:
module(
input[23:
rx_data
//CRC_chkinterface