FPGA Verilog基本语法及模块说明

1.FPGA Verilog基本语法及其说明

代码备注
reg/wire设计中所有的信号类型定义,只有 reg 和 wire 两种(一般来说,用always设计代码都是 reg,其它的都是用 wire)
parameter设计代码中所有的位宽、长度、状态机命名等,建议都用参数表示,阅读方便并且容易修改(参数化定义,和C语言里面的宏定义#define类似)
assign/always程序块主要部分,至简设计法对always使用有严格规范,语法格式见下方,详情请关注后续有关组合逻辑和时序逻辑的博客
if else/casealways里面的语句,使用if else 和 case 两种方法用来做选择判断,可以完成全部设计
算术运算符(+,-,*,/,%)可以直接综合出相对应的电路。但除法和求余运算的电路面积一般比较大,不建议直接使用除法和求余
赋值运算符(=,<=)时序逻辑用"<=“,组合逻辑用”=";其他情况不存在
关系运算符(==,!=,>,<,>=,<=)
逻辑运算符(&&,!,II)
位运算符(~,^,&,I)
移位运算符(<<,>>)
拼接运算符({ })

信号类型详解:Verilog HDL信号类型(reg&&wire)
程序语句详解:Verilog HDL程序语句(assign&&always)
数字表示详解:Verilog HDL数字表示(进制/X态/Y态)
按位逻辑运算符:Verilog HDL按位逻辑运算符及逻辑运算符

(附)assign/always语法格式

//组合逻辑格式如下:
always@(*) begin
//代码语句
end

//或者可以直接使用assign
//时序逻辑格式如下:
always@(posedge clk or negedgerst_n) begin
	if (rst_n==1'b0)begin
		//代码语句
	end
	else begin
		//代码语句
	end
end
/*
时序逻辑中,敏感列表一定是clk的上升沿和复位的下降沿、最开始
必须判断复位。
*/

2.模块(module)

2.1 模块简介

模块(module)是 Verilog 的基本描述单位,是用于描述某个设计的功能或结构,是与其他模块通信的外部端口。
模块在概念上可以等同于一个器件,如通用器件(与门、三态门等)或通用宏单元(计数器、ALU、CPU等)。因此一个模块可以在另一个模块中调用,一个电路设计可由多个模块组合而成。在进行大型数字电路的设计时,可以将其分割成大小不一的小模块,每个小模块实现特定的功能,最后通过由顶层模块调用子模块的方式来实现整体功能。(可类比于C语言,main()函数为顶层模块,自己写的函数就相当于一个个的功能模块)

2.2 模块结构

模块有五个主要部分:端口定义、参数定义(选)、I/O说明、内部信号声明、功能定义。
模块总是以关键词module开始,以关键词endmodule结尾,模块的一般语法结构如下所示:

//端口定义:
module module_name(
	clk,    // 端口1,时钟
	rst_n,  // 端口2,复位
	dout    // 其他信号,如dout
);

//参数定义:(可选,不必须)
parameter DATA_W = 8;

// I/O说明:
input clk;                // 输入信号定义
input rst_n;              // 输入信号定义
output [DATA_W-1:0] dout; // 输出信号定义

//信号说明:运用reg/wire定义
reg [DATA_W-1:0] dout; //信号类型
reg signal1;           //信号类型

//...........以下为描述功能部分.............

//功能定义:

//1.组合逻辑写法:
always@(*) begin
//代码语句
end

//2.时序逻辑写法:
always@(posedge clk or negedgerst_n) begin
	if (rst_n==1'b0)begin
		//代码语句
	end
	else begin
		//代码语句
	end
end
//结束
endmodule

2.3 模块解析

2.3.1 端口定义

上述代码中的module_name类似于C语言中的函数名,大家可以随意取名。我们假如起名为MCU,那么就可以把这个模块想象成一块MCU。那么这块MCU有着许多的管脚。其中有的管脚叫做clk,也就是时钟管脚;有的叫做rst_n,也就是复位管脚,如下图所示:
在这里插入图片描述

2.3.2 参数定义

写法:parameter D_W = 8;
此项多用于后续大型设计中有着许多的管脚,管脚的信号位宽不一样,一个个修改的话过于麻烦,类似C语言宏定义。

2.3.3 接口定义

在I/O说明中,模块的端口可以是输入端口、输出端口或者双向端口,说明格式如下:
输入端口:
input [信号位宽-1:0] 端口名1;
input [信号位宽-1:0] 端口名2;
…;
输入端口:
output [信号位宽-1:0] 端口名1;
output [信号位宽-1:0] 端口名2;
…;
双向端口:
inout [信号位宽-1:0] 端口名1;
inout [信号位宽-1:0] 端口名2;
…;

信号位宽:为什么要写成信号位宽-1的形式呢,比如信号位宽为8,那么信号位为0-7。假如以刚才的MCU举例的话,我们输入一个信号位宽为8,那么就代表着需要MCU的8个管脚来接收这个信号。如下图:
在这里插入图片描述

2.3.4 信号类型

信号类型一共有两种,一个是reg(寄存器型),另一个是wire(线型)。写法见上方模块结构内部。

3.模块例化

一个模块能够在另外一个模块中被引用,这样就建立了描述的层次。模块实例化语句形式如下:
module_nameinstance_name(port_associations);

//举例如下:
module and(
	C,
	A,
	B
);
input A,B;
output C;
//其余部分省略
endmodule

//在下面的“and_2”模块中对上一模块“and”进行例化,有两种方式:
module and_2(xxxxxxxx);
.......

//方式一:实例化时采用位置关联,T3对应输出端口C,A对应A,B对应B
and A1 (T3,A,B);
//方式二:实例化时采用名字关联,例如.C是and器件的端口,其与信号T3相连
and(
.C(T3),
.A(A),
.B(B)
);

在实例化中可能有些管脚没有用到,可在映射中采用空白处理。

//例:
DFF d1(
.Q(QS),
.Qbar(),
.Data(D),
.Preset(),
.Clock(CK)
);
//输入管脚悬空端口的输入为高阻态

说明:
1.建议在例化的端口映射中采用名字关联,这样当被调用的模块管脚改变时不易出错。
2.模块例化类似于C语言中的实参和形参。举个例子,我们先定义一个模块为USB,模块内部含有三个管脚,在一个单片机的扩展板上面有三个USB接口,我们假设单片机扩展板为模块MCU,那么我们在使用MCU时,可以把三个USB模块命名为USB1,USB2,USB3,在使用时需要在MCU模块中例化三个USB。
3.还是以USB模块为例,假如例化时有.C©,那么.C()代表原来module USB时里面的C管脚,括号里面的C代表连接的其他部位名字为C的管脚。

Tip:如果您在阅读的过程中发现任何不妥之处欢迎前来指正!

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zz小叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值