FPGA设计:always组合逻辑块的讲解和译码器的实现

描述

always 语句

always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。多用于仿真时钟的产生,信号行为的检测等。

always语句的格式:

always @(posedge clk) begin
    temp    = a ;
    a       = b ;
    b       = temp ;
end

()中可以是*,表示:每当任何输入发生变化时执行begin和end之间的语句。也可以是posedge clk,表示:时钟上升沿时执行begin和end之间的语句。assign语句和always @(*)创建相同的组合逻辑,比如下面代码中的两个输出波形是一致的。

module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
assign out_assign=a&b;
    always @(*) begin
       out_alwaysblock=a&b; 
    end
endmodule

连续性赋值,过程性赋值 :连续性赋值总是处于激活状态,任何操作数的改变都会影响表达式的结果;过程赋值只有在语句执行的时候,才会起作用。

阻塞赋值与非阻塞赋值 :是过程赋值的两种类型。

阻塞赋值 :阻塞赋值属于顺序执行,下一条语句执行前,当前语句一定会执行完毕。使用等号 = 作为赋值符。initial语句用阻塞赋值。

非阻塞赋值 :并行执行,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。使用 <= 作为赋值符。

使用非阻塞赋值是为了避免竞争冒险,那么实际使用中只需要记住:always时序逻辑块中多用非阻塞赋值<=(后面的时序逻辑电路会另外介绍),always组合逻辑块中多用阻塞赋值=; 在仿真电路时,initial 块中一般多用阻塞赋值=。 例如下面代码中,用assign语句,always组合语句,always时钟语句实现异或:

module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,
    output reg out_always_comb,
    output reg out_always_ff   );
assign out_assign=a^b;
    always @(*) begin
       out_always_comb=a^b; 
    end
    always @(posedge clk) begin
       out_always_ff<=a^b; 
    end
endmodule

if 语句格式

if (condition1)       true_statement1 ;
else if (condition2)        true_statement2 ;
else if (condition3)        true_statement3 ;
else                      default_statement ;

如果只有两种情况,就只有if和else。下面用上一节中MUX2_1做练习,上一节中采用了assign的三元运算符语句,这里使用always if语句实现2选1的功能:

module mux2_1 
(
input wire in1, 
input wire in2, 
input wire sel, 
output reg out 
 ); 
 
 always@(*) begin
 if(sel == 1'b1)//当“if...else...”中只有一个变量时不需要加“begin...end".但是为了不遗漏,还是加上
 out = in1; //always块中的组合逻辑关系时使用阻塞赋值“=”进行赋值
 else
 out = in2;
 end
 endmodule

3-8译码器的实现

从真值表看译码器功能:A2A1A0=000-111共8种输入,对应输出Y的下标,对应下标的输出为1(高电平),其他输出为0(低电平)。3-8译码器的作用:按照之前点亮LED灯的思路,如果一个I/O端口控制一个LED灯,那么I/O端口很有可能不够用,这时候聪明人就想出用3-8译码器的方式,3个I/O口就可以控制8个LED。

时钟

设计规划

这个示例中,绘制波形图如图所示。

时钟

时钟

编写代码

module decoder3_8
(
input wire in1 , 
input wire in2 , 
input wire in3 , 
output reg [7:0] out 
);


 always@(*) begin
 //使用"{}"位拼接符将31bit数据按照顺序拼成一个3bit数据
 if({in1, in2, in3} == 3'b000)
 out = 8'b0000_0001;
 else if({in1, in2, in3} == 3'b001)
 out = 8'b0000_0010;
 else if({in1, in2, in3} == 3'b010)
 out = 8'b0000_0100;
 else if({in1, in2, in3} == 3'b011)
 out = 8'b0000_1000;
 else if({in1, in2, in3} == 3'b100)
 out = 8'b0001_0000;
 else if({in1, in2, in3} == 3'b101)
 out = 8'b0010_0000;
 else if({in1, in2, in3} == 3'b110)
 out = 8'b0100_0000;
 else if({in1, in2, in3} == 3'b111)
 out = 8'b1000_0000;
 else
 out = 8'b0000_0001;
end
 endmodule

代码中使用always组合逻辑块。由于if对应的执行语句只有一句,可以不使用begin,end但是由于怕以后复杂代码会遗漏,还是选择加上。always组合逻辑使用阻塞语句,用=进行赋值。

上面RTL代码综合出的RTL视图:

时钟

编写testbench

`timescale 1ns/1ns
module tb_decoder3_8();


//reg define
reg in1;
reg in2;
reg in3;
wire [7:0] out;


 //初始化输入信号
 initial begin
 in1 <= 1'b0;
 in2 <= 1'b0;
 in3 <= 1'b0;
 end


 always #10 in1 <= {$random} % 2;
 always #10 in2 <= {$random} % 2;
 always #10 in3 <= {$random} % 2;
 //-------------decoder3_8_inst----------------
 decoder3_8 decoder3_8_ins
 (
 .in1(in1), 
 .in2(in2), 
 .in3(in3), 
 .out(out) 
 );


 endmodule

经过两个实例后,testbench已经超级熟悉了吧。

对比波形

看看波型:

时钟

验证了波形的正确性

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分