Verilog和FPGA的自学笔记4——多路选择器(always语句)

标题写多路选择器,实际上……

今天记录一下如何用Verilog在FPGA上做一个多路选择器。
是的,虽说要做mux,重点却在于always语句

组合逻辑?时序逻辑?

先说说Verilog的逻辑。
Verilog主要包含两大逻辑:组合逻辑与时序逻辑。
如果要看完整定义,给大家放下面了:

  • 组合逻辑:输出仅由当前输入决定,与电路过去的状态无关,没有记忆功能。
  • 时序逻辑:输出不仅取决于当前输入,还依赖于电路过去的状态(即有记忆功能)。

如果要人话 废话 一点,组合逻辑只是一个逻辑运算,没有反馈(输出再接回输入),输出仅由输入决定,输入变化,输出立马跟着变。

而时序逻辑有反馈,有了反馈,自然可以构成记忆电路。这方面典型的就是锁存器和触发器了。
RS触发器
上面是我们再熟悉不过的RS触发器,可以看到两个输出是有反馈回输入的。

Verilog 的数字进制格式

再说说Verilog中数字咋写。

在数字电路方面,我们一般会用二进制,十进制,和十六进制,其对应书写格式如下:

  • 位宽+单引号’+进制类型+数

几个栗子:

  • 两位二进制3:2’b11
  • 8位十六进制FF:8‘hff(不区分大小写)

注意一个问题,Verilog中的位宽永远以二进制的位宽为准! 2‘hff这样写就不对!(不要问谁曾经这么写……捂脸)

如果不注明位宽和进制类型,光给编译器扔个数,那人家就默认你写了个32位的十进制数。

还有一点,不同位宽的数是可以做运算的,比如这样:2’b10 - 1’b1,结果为2’b1。但不推荐这样写,有时候可能会出问题。

太好啦,终于把两个逻辑和数字进制格式说清楚啦,接下来就可以和always见面啦!

always语句!

语法见下:个人认为这种玩意特简洁……

always @(敏感列表) begin
逻辑书写之地
end

先大体看一眼。

电平信号?组合逻辑!

举个例子:

//此乃mux精髓所在^_^
always @(*) begin
	if(sel == 1'b1)
		out = A;
	else
		out = B;
end 

说说几个膈应人的地方(为啥说膈应呢因为学的时候没人跟我解释@*空格一堆乱七八糟都是些啥)
always@(星) (优快云编辑器里不知道咋写,晕死……) 之间可以有空格,也可以没有,大家怎么喜欢怎么来。

@是敏感列表操作符,用来触发always语句执行条件的。后面的括号内部需要写敏感列表,也就是always的执行条件。

这里我们写*,其实是一种省略。意思就是当always语块内部有一个电平输入信号发生变化时,就会执行本always语句。比如上面示例代码中,输入信号有sel,A,B三个,则当任何一个变化时,都会导致always语句的执行。

如果你不想省略,可以写成以下这样:

always @(sel or A or B) begin
	if(sel == 1'b1)
		out = A;
	else
		out = B;
end 

当然我懒,我是不会写的哈哈。

至于if-else语句,除了花括号用begin和end以外,其余一毛一样,就不废话啦。

时序逻辑!

如果我们把敏感列表中的电平信号换成时钟信号,就变成了always的时序逻辑,比如:

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        q = 1'b0; 
    else
        q = d;
end

上面的例子中写了俩边沿信号,分别是clk(时钟)和rst_n(复位),之间用关键字"or"连接。前面两个posedge 和 negedge 则是信号类型的关键字:

  • posedge:在信号上升沿到来时触发
  • negedge :在信号下降沿到来时触发

这种边沿触发的信号,一般用于触发器或者寄存器,对应时序逻辑了!

如果想写多个触发条件,继续往后写即可:

always @(类型 信号1 or 类型 信号2 or …… or 类型 信号n)

这里可能有人要问了,如果电平信号和边沿信号掺和在一起,这算啥?

always @(key or posedge clk)

有人管这玩意叫(非正常的)混合逻辑,虽然没有明令禁止,但大家最好不要写这种东西,指不定综合工具会给你整出个啥来……

一点注意,不要在多个always语块里给同一个数据赋值!,否则综合器会报多驱动源的错。
啥是在多个always语块里给同一个数据赋值?

reg cnt;

always @(posegde sts_clk or negedge sts_rst) begin
	if(!sys_rst)
		cnt <= 1'b0;
	else
		cnt <= cnt + 1;
end

always @(posegde sts_clk or negedge sts_rst) begin
	if(!sys_rst)
		cnt <= 1'b0;
	else
		cnt <= cnt;
end

这就是在多个always语块里给同一个数据赋值。

第一个always内有cnt的计数逻辑,第二个always内有cnt的保持逻辑,那么两个always都对cnt有激励,到了真实硬件中到底输入谁的信号就会产生问题,也就是存在多驱动源的问题。在代码设计阶段我们就得避免。

mux的代码实现

好啦,不用多说我也知道,大家现在一定都会写mux的Verilog代码啦!为延续程序员Ctrl+C Ctrl+V传统,代码给大家放在下面啦嘻嘻:

module mux(
    input   		A,    //输入信号in1
    input   		B,    //输入信号in2
    input   		sel,    //选择控制信号sel
    
    output reg		out     //输出信号out
    );
always@(*) begin	//"*"为通配符,在这个模块中的任何一个输入信号或电平发生变化时
	if(sel == 1'b1)
		out = A;
	else
		out = B;
end

endmodule

写.v文件时有一点要注意,always的输出信号(本代码中的out)必须定义为reg型,否则将编译错误!
比如这样就不行:

output  out    //错误:out作为always的输出信号必须是reg

咦?tb文件里的输出不是写wire来着?难道让RTL里的reg和tb里的wire对应吗??
哈哈,有这个疑问说明你Verilog学的挺扎实!确实,RTL里的reg和tb里的wire对应是合法且必须的。

接下来就是仿真文件了,仨信号一一变化即可。还不会写仿真文件的欢迎参考我的上篇博文~~~

如果有不明白或错误之处,也希望大家在评论区给出,帮助大家的同时也能再次提升自己对于FPGA和Verilog的理解,感谢大家!!

系列链接:
上一篇:Verilog和FPGA的自学笔记3——仿真文件Testbench的编写
下一篇:Verilog和FPGA的自学笔记5——三八译码器(case语句与锁存器)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值