verilog hdl高级数字_集成电路设计与HDL(一)

c24edacab2d1d889f2acdc9c0da19318.png

我的本科专业是电子科学与技术(微电子系统设计方向),目前在瑞典Lund University芯片设计方向读硕(MSc in Embedded Electronics Engineering)。我将自此开始通过课余时间总结本科期间对Verilog HDL及其在ASIC/FPGA设计当中所有的关键知识的认识,过去一直在跟着我的中科院科研导师将所有代码文档、总结与描述上传到有道云,不过在现在开源的环境下我觉得像写blog那样在知乎总结自己所学到的东西并将有趣的项目上传到GitHub更为“人道”,得道多助嘛。

从这一节开始我将引用长期以来积攒下来的好书,包括我的HDL启蒙书《Verilog高级数字设计》(夏宇闻可以说是我选择数字IC方向的引路人)、《Verilog与System Verilog编程陷阱》、《数字设计和计算机体系结构(ARM版)· 第2版》(英文版)、《芯片验证漫游指南》以及近期出版的李庆华前辈的《通信IC设计》(这本书我一定要强烈推荐,因为它不论是对IC领域的新生还是有经验的人都不可多得,它自底向上地通过很多实例来讲解主流IC设计思想和方法)。

1.Verilog的基本语法结构

在Verilog中每一个module的语法结构都是如以下伪代码所描述的两部分组成:

端口列表与声明

module 模块名称 #(
parameter 参数变量1=XX,
....
parameter 参数变量N=XX,             //parameter声明该模块内的全局变量
....
) (
    input                输入端口1,
    ....
    input [参数变量N-1:0] 输入端口N, //[]内声明位宽,不声明情况下端口位宽默认1bit
    ....
    output                输出端口1,
    ....
    output [参数变量N-1:0] 输出端口N,
    ....
    inout                 双向端口N,  //inout不会单独存在且其通过三态门实现,所以需过程块再增加
    ....                             //控制信号并列入always敏感信号,相连的inout之间信号存在交叉
);

电路行为描述

//全局变量和局部变量声明

localparam P_H=7;       //localparam声明局部变量
      ....

//寄存器、wire等类型变量声明

wire [5:0] count;
reg [5:0] data_int;

//函数、task声明

function fft=(a,b)
      ....
end function

//电路描述

always@(....)   //()内列出敏感信号,也许是时钟边沿或者电平
      ....
end                 //电路描述结束
      ....
endmodule     //模块结束

2.module电路描述及其内部控制流

对于已经掌握C语言的读者来说控制流已经再熟悉不过了:顺序,分支,循环,而Verilog则完全借鉴了这些结构。但是要指出的是HDL全称为Hardware Description Language(即“硬件描述语言”),它实际上既不面向过程也不面向对象而是作为形式化工具以实现RTL级电路描述进而映射为门级电路甚至晶体管级电路结构

对于不包含寄存器的组合逻辑电路我们有时需要采用连续赋值语句assign,例如在加法器中:

module adderN #(parameter N=3) (
input[N-1:0] a,
input[N-1:0] b,
input     cin,        //cin为进位输入
output   cout,        //cout为进位输出,可理解为溢出
output[N-1:0] sum     //sum为结果输出
);
assign{cout,sum} = a + b + cin;     //{cout,sum}是对两个变量合并赋值的简化描述
                                    //它等价于{cout,sum[N-1:0]}且其表示为4位的数据故位宽匹配为4位
endmodule

有时候我们也需要创建一个多路条件分支选择结构,那么这时我们需要用到case语句,比如描述一个多路选择器:

module mux #(parameter N=2) (
input[N-1:0] a, //sel=00时选择该输入
input[N-1:0] b, //sel=01时选择该输入
input[N-1:0] c, //sel=10时选择该输入
input[N-1:0] d, //sel=11时选择该输入
input[1:0]  sel, //选择器
output[N-1:0] mux_out  //选择器结果输出
);
 
reg[N-1:0] mux_temp;   //中间变量,防止其他调用者误认为输出锁存导致信号卡死
assign mux_out = mux_temp;

always@(a or b or c or d or sel)    //此处为电平触发,一般都在always描述组合电路情况下
case(sel)
0 : mux_temp = a;
1 : mux_temp = b;
2 : mux_temp = c;
3 : mux_temp = d;
default: $display(“Error with sel signal”);  //$display能够在仿真时输出各类信号
endcase

endmodule 

下面将重点介绍Verilog的精髓——always语句上。always过程块会随着敏感信号而不断被执行,其中敏感信号驱动方式包括电平驱动(如上段代码所提到)以及边沿驱动,边沿驱动事件一般对应的是敏感信号发生的跳变瞬间并由posedge对应上升沿negedge对应下降沿。

不过在测试验证代码当中always有时会没有敏感信号列表:

always #10 clock = ~clock;  //#10表示10ns,此句作用为产生一组周期为20ns的方波

这种情况下always会无条件执行但是不可综合,一般用于验证当中产生仿真信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值