写在前面
在自己准备写一些简单的verilog教程之前,参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好,奈何没有中文,在下只好斗胆翻译过来(加点自己的理解)分享给大家。
这是网站原文:Verilog Tutorial
这是系列导航:Verilog教程系列文章导航
什么是逻辑综合( logic synthesis )?
逻辑综合是将高层次的设计描述转换为优化后的门级设计的过程。逻辑综合会使用一个标准的单元库,它有许多简单的单元,例如像与门或门非门等基本的逻辑门、加法器、多路选择器、存储单元和触发器。标准单元组合起来则被称为技术库。通常,这种技术库以晶体管的尺寸(0.18u,90纳米)而为人所知。
电路描述是用硬件描述语言(HDL)编写的,例如如Verilog。设计师应该首先理解架构描述,然后他才应该考虑设计限制,如时间、面积、可测试性和功率等。
我们将在最后一章的Verilog教程中,看到一个典型的大型设计流程的例子。
在Verilog发明之前的设计方法
正如你必须在大学里所经历的一样,一切(所有的数字电路)都是手动设计的。绘制卡诺图、优化逻辑、绘制原理图等。这就是工程师们早期设计数字逻辑电路的方法。好吧,如果只设计几百个门的话这方法也还行。
hdl与逻辑综合的影响
高级设计不太容易出现人为错误,因为设计是在一个较高的抽象层级上进行描述的。高层次的设计是在不对设计约束过于关注的情况下完成的。从高层次设计转换到门级设计是通过综合工具,使用各种算法优化整体设计来实现的。这就消除了设计和次优化设计中不同设计风格的问题。逻辑综合工具允许技术独立设计。对于技术独立的描述,设计重用是可能的。
我们在这里讨论什么?
对于Verilog来说,其综合流程也与其他语言相同。接下来的文章,我们要看的是特定代码是如何转化为门级电路的。"delay"就是一个例子。我们无法综合delay,但是当然我们可以通过添加缓冲器来增加特定信号的delay。但这也会变得过于依赖于综合目标技术了。
首先,我们将聚焦在无法综合的构造,下表显示了无法被综合的构造。
无法被综合的构造
构造类型 | 说明 |
initial | 只能用于testbenches |
events | 只能用于testbenches |
real | 不支持 |
time | 不支持 |
force and release | 不支持 |
assign and deassign | 不能用于reg,只能用于wire |
fork join | 使用非阻塞赋值可以达到同样的效果 |
primitives | 仅仅支持门级原语 |
table | 不支持 |
示例:无法被综合的构造
任何包含上述构造的代码都是不可综合的,但是在可综合的构造中,糟糕的编码也可能会引起综合问题。我就见过某个工程师的代码中,触发器的敏感列表居然同时是时钟的上升沿和下降沿。
另一种常见的错误类型是,一个reg变量被多个always块所驱动。这也肯定会引起综合错误。
示例1:Initial 语句
module synthesis_initial(
clk,q,d);
input clk,d;
output q;
reg q;
initial begin
q <= 0;
end
always @ (posedge clk)
begin
q <= d;
end
endmodule
示例2:Delays语句
a = #10 b; //这句代码仅在仿真时有效
综合工具通常会忽略这些构造,只是假设上面的语句中没有#10,因此综合工具会将上面的代码简单地视为
a = b;
与X 和 Z的比较总是被忽略
似乎所有新的硬件设计工程师都有一个共同的问题--他们通常倾向于将变量与x和z进行比较。实际上这是最糟糕的事情,请避免与X和Z相比较。将您的设计限制在0和1这两个状态。仅在芯片的IO端口才使用三态(tri-state)。
可以被综合的构造
Viilog是这样一种简单的语言--您很容易就可以编写出很容易实现和被映射到门级电路的代码。使用 if和case语句的代码很简单,使用综合工具不会引起很多麻烦。但是,如果你喜欢花哨的代码风格,并且喜欢有一些麻烦,好吧,你也别怕,不过你最好在有了一定经验后再使用它们。使用高层次的构造是很有趣的,而且很省时间。
建模任何逻辑的最常见方法是使用assign语句或always块。assign语句只可用于组合逻辑;而always块则可以用于组合逻辑和时序逻辑。
构造类型 | 关键词或描述 |
端口 | input, inout, output |
参数 | parameter |
模块定义 | module |
信号和变量 | wire, reg, tri |
例化 | module instances / primitive gate instances |
函数和任务 | function , task |
过程语句 | always, if, else, case, casex, casez |
过程块 | begin, end, named blocks, disable |
数据流 | assign |
循环语句 | for, while, forever |
逻辑电路建模
根据我们在数字设计中所学到的内容,我们知道只有两种类型的数字电路。一种是组合逻辑电路,另一种是时序逻辑电路。只有很少的规则需要遵循,就可以获得良好的综合输出和避免意外发生。
使用assign语句的组合逻辑电路
可以使用assign和always块来实现组合逻辑电路。使用assign语句在Viilog中编写简单的组合电路非常简单,就像下面的例子中所描述的那样。
assign Y=(A&MP;B)|(C);
示例1:三态缓冲器(Tri-state buffer)
module tri_buf (a,b,enable);
input a;
output b;
input enable;
wire b;
assign b = (enable) ? a : 1'bz;
endmodule
示例2:多路选择器(Mux)
module mux_21 (a,b,sel,y);
input a, b;
output y;
input sel;
wire y;
assign y = (sel) ? b : a;
endmodule
示例3:简单拼接
module bus_con (a,b);
input [3:0] a, b;
output [7:0] y;
wire [7:0] y;
assign y = {a,b};
endmodule
示例4:带进位的1位加法器
module addbit (
a , // first input
b , // Second input
ci , // Carry input
sum , // sum output
co // carry output
);
//Input declaration
input a;
input b;
input ci;
//Ouput declaration
output sum;
output co;
//Port Data types
wire a;
wire b;
wire ci;
wire sum;
wire co;
//Code starts here
assign {co,sum} = a + b + ci;
endmodule // End of Module addbit
示例5:乘2电路
module muliply (a,product);
input [3:0] a;
output [4:0] product;
wire [4:0] product;
assign product = a << 1;
endmodule
示例6:3-8译码器
module decoder (in,out);
input [2:0] in;
output [7:0] out;
wire [7:0] out;
assign out = (in == 3'b000 ) ? 8'b0000_0001 :
(in == 3'b001 ) ? 8'b0000_0010 :
(in == 3'b010 ) ? 8'b0000_0100 :
(in == 3'b011 ) ? 8'b0000_1000 :
(in == 3'b100 ) ? 8'b0001_0000 :
(in == 3'b101 ) ? 8'b0010_0000 :
(in == 3'b110 ) ? 8'b0100_0000 :
(in == 3'b111 ) ? 8'b1000_0000 : 8'h00;
endmodule
使用always块的组合逻辑电路
在使用always语句建模时,如果不小心的话,有可能会综合出来一个锁存器。(似乎没有人喜欢设计中的锁存器,尽管它们速度更快,而且采用的晶体管也较少。这是因为时序分析工具总是有锁存器的问题,锁存器启用时的故障是另一个问题)。
用always语句消除锁存器的一个简单方法是始终在always代码的开头将0驱动到等式的左侧变量,如下文代码所示。
示例:用always语句实现的3-8译码器
module decoder_always (in,out);
input [2:0] in;
output [7:0] out;
reg [7:0] out;
always @ (in)
begin
out = 0;
case (in)
3'b001 : out = 8'b0000_0001;
3'b010 : out = 8'b0000_0010;
3'b011 : out = 8'b0000_0100;
3'b100 : out = 8'b0000_1000;
3'b101 : out = 8'b0001_0000;
3'b110 : out = 8'b0100_0000;
3'b111 : out = 8'b1000_0000;
endcase
end
endmodule
时序逻辑电路
时序逻辑电路是利用always块的敏感列表中的边缘敏感元素实现的。时序逻辑只能使用always块来实现。通常我们使用非阻塞赋值来进行实现时序逻辑电路。
示例:简单的触发器
module flif_flop (clk,reset, q, d);
input clk, reset, d;
output q;
reg q;
always @ (posedge clk )
begin
if (reset == 1) begin
q <= 0;
end else begin
q <= d;
end
end
endmodule
Verilog编码风格
如果你看看上面的代码,你会发现我强加了一个看起来很酷的编码风格。
- 信号和变量要使用有意义的名称
- 不要把水平和边缘敏感的元素混合在同一个always块中
- 避免将上升沿触发和下降沿触发的触发器混合在一起
- 使用括号优化逻辑结构
- 简单的组合逻辑使用连续赋值语句
- 时序逻辑使用非阻塞赋值,组合逻辑使用阻塞赋值
- 不要将阻塞赋值和非阻塞赋值的混合在同一个always块中
- 别对同一变量的进行多次赋值
- 明确地定义"if-else语句"或"case语句"
- 📣您有任何问题,都可以在评论区和我交流📃!
- 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net
- 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!