前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HDLBits:在线学习Verilog(七 · Problem 30-34)

HDLBits:在线学习Verilog(七 · Problem 30-34)

作者头像
数字积木
发布2021-04-15 11:42:37
5790
发布2021-04-15 11:42:37
举报
文章被收录于专栏:数字积木

本系列内容来自于知乎专栏,链接如下:https://zhuanlan.zhihu.com/c_1131528588117385216 本系列文章将和读者一起巡礼数字逻辑在线学习网站 HDLBits 的教程与习题,并附上解答和一些作者个人的理解,相信无论是想 7 分钟精通 Verilog,还是对 Verilog 和数电知识查漏补缺的同学,都能从中有所收获。

Problem 30: If statement(Always if)

if语句通常对应一个二选一多路复用器,如果条件为真,则选择其中一个输入作为输出;反之如果条件为假,则选择另一个输入所谓输出。if语句必须在过程块内使用。

下面给出了一个基本的if语句和其综合出来的电路。

代码语言:javascript
复制
always @(*) begin
    if (condition) begin
        out = x;
    end
    else begin
        out = y;
    end
end

这与下面使用条件运算符连续赋值的语句是等价的:

代码语言:javascript
复制
assign out = (condition) ? x : y;

但是,过程if语句使用不当可能会引入新的错误,只有out在所有的条件下都被赋值才会生成正确的组合电路,具体的错误下一个训练才会讲到,

牛刀小试

构建一个可以在a和b之间选择的二选一多路复用器。如果sel_b1和sel_b2都为真,输出b,其他情况输出a。请使用两种方法作答,一次使用assign赋值,一次使用if语句。

解答与分析

代码语言:javascript
复制
// synthesis verilog_input_version verilog_2001
module top_module(
    input a,
    input b,
    input sel_b1,
    input sel_b2,
    output wire out_assign,
    output reg out_always);
    
    assign out_assign = sel_b1? sel_b2? b:a :a;

    always@(*)
        if(sel_b1&sel_b2)
            out_always = b;
        else
            out_always = a;

endmodule

Problem 31: If statement latches(Always if2)

常见的错误来源:如何避免引入锁存器

在设计电路时,必须首先具体考虑电路:

1、我想实现一个逻辑门;

2、我想实现一个具有输入并产生输出的组合逻辑块;

3、我想实现一组组合逻辑,紧接着一组触发器。

不要上来就写代码,这样往往与你想象的电路相差很远。

代码语言:javascript
复制
if (cpu_overheated) then shut_off_computer = 1;
if (~arrived) then keep_driving = ~gas_tank_empty;

除了你指定的情况以外,会发生些什么,答案是什么也不会发生,输出保持不变。而这往往就导致了电路的错误,所以说语法正确的代码不一定能产生合理的电路(组合逻辑+触发器)。

输出保持不变,这就意味着电路需要记住当前状态,从而产生锁存器。组合逻辑(比如逻辑门)不能记住任何状态。

代码语言:javascript
复制
Warning (10240): ... inferring latch(es)

上述这类警告通常情况下代表错误,除非锁存器是故意生成的。组合电路输出必须在所有输入的情况下都有值。这意味着必须需要else子句或着输出默认值。

牛刀小试

示例:以下代码包含生成锁存器的错误,请勿模仿!!!修复错误,只有当它真的过热时才关闭计算机,真的到达目的地或者需要加油时,才停止驾驶。

代码语言:javascript
复制
always @(*) begin
    if (cpu_overheated)
       shut_off_computer = 1;
end

always @(*) begin
    if (~arrived)
       keep_driving = ~gas_tank_empty;
end

解答与分析

代码语言:javascript
复制
// synthesis verilog_input_version verilog_2001
module top_module (
    input      cpu_overheated,
    output reg shut_off_computer,
    input      arrived,
    input      gas_tank_empty,
    output reg keep_driving  ); //

    always @(*) begin
        if (cpu_overheated)
            shut_off_computer = 1;
        else
            shut_off_computer = 0;
    end

    always @(*) begin
        if (~arrived)
            keep_driving = ~gas_tank_empty;
        else
            keep_driving = ~arrived;
    end

endmodule

Problem 32: Casestatement(Always case)

Verilog中的case语句几乎等同于if-else if-else序列,它将一个表达式与其他表达式列表进行比较。它的语法和功能与C语言中的switch语句稍有不同:

代码语言:javascript
复制
always @(*) begin     // This is a combinational circuit
    case (in)
      1'b1: begin 
               out = 1'b1;  // begin-end if >1 statement
            end
      1'b0:    out = 1'b0;
      default: out = 1'bx;
    endcase
end

1、case语句以case开头,每个case项以冒号结束。而switch语句没有。

2、每个case项只执行一个语句。这样就不需要C语言中break来跳出switch。但这也意味着如果您需要多个语句,则必须使用begin ... end。

3、case项允许重复和部分重叠,执行程序匹配到的第一个,而C语言不允许重复的case项目。

牛刀小试

如果存在大量的case项,则case语句比if语句更方便。因此,在本练习中,创建一个6选1的多路复用器。当sel介于0和5之间时,选择相应的数据输入。其他情况输出0。数据输入和输出均为4位宽。

注意:不要生成锁存器(详见:Problem 31: If statement latches(Always if2))。

解答与分析

代码语言:javascript
复制
// synthesis verilog_input_version verilog_2001
module top_module (
    input [2:0] sel,
    input [3:0] data0,
    input [3:0] data1,
    input [3:0] data2,
    input [3:0] data3,
    input [3:0] data4,
    input [3:0] data5,
    output reg [3:0] out   );//

    always@(*) begin  // This is a combinational circuit
        case(sel)
        3'b000: out = data0;
        3'b001: out = data1;
        3'b010: out = data2;
        3'b011: out = data3;
        3'b100: out = data4;
        3'b101: out = data5;
        default: out = 4'b0000;
        endcase
    end

endmodule

注意这里一定要用default声明一下不在case项里的输出,否则会生成不必要的寄存器,影响电路的功能。

Problem 33: Priority encoder(Always case2)

优先编码器是组合电路,当给定输入时,输出输入向量中的右边第一个1的位置。例如,输入8'b10010000的,则优先编码器将输出3'd4,因为位[4]是从右数第一个1。

牛刀小试

构建一个4位优先编码器。如果没有输入均为零,则输出零。请注意,4位数字有16种输入发的可能。

小提示:使用十六进制(4'hb)或十进制(4'd11)与二进制(4'b1011)相比可以节省打字量。

解答与分析

这是二进制编码的写法,看起来相对直观一点。

代码语言:javascript
复制
// synthesis verilog_input_version verilog_2001
module top_module (
    input [3:0] in,
    output reg [1:0] pos);

always@(*)
    case(in)
    4'b0000: pos = 2'b00;
    4'b0001: pos = 2'b00;
    4'b0010: pos = 2'b01;
    4'b0011: pos = 2'b00;
    4'b0100: pos = 2'b10;
    4'b0101: pos = 2'b00;
    4'b0110: pos = 2'b01;
    4'b0111: pos = 2'b00;
    4'b1000: pos = 2'b11;
    4'b1001: pos = 2'b00;
    4'b1010: pos = 2'b01;
    4'b1011: pos = 2'b00;
    4'b1100: pos = 2'b10;
    4'b1101: pos = 2'b00;
    4'b1110: pos = 2'b01;
    4'b1111: pos = 2'b00;
    default: pos = 2'b00;
    endcase

endmodule

作者提供了16进制的编码的写法,他觉得这样打字打的少,2333333。

代码语言:javascript
复制
module top_module (
	input [3:0] in,
	output reg [1:0] pos
);

	always @(*) begin			// Combinational always block
		case (in)
			4'h0: pos = 2'h0;	// I like hexadecimal because it saves typing.
			4'h1: pos = 2'h0;
			4'h2: pos = 2'h1;
			4'h3: pos = 2'h0;
			4'h4: pos = 2'h2;
			4'h5: pos = 2'h0;
			4'h6: pos = 2'h1;
			4'h7: pos = 2'h0;
			4'h8: pos = 2'h3;
			4'h9: pos = 2'h0;
			4'ha: pos = 2'h1;
			4'hb: pos = 2'h0;
			4'hc: pos = 2'h2;
			4'hd: pos = 2'h0;
			4'he: pos = 2'h1;
			4'hf: pos = 2'h0;
			default: pos = 2'b0;	// Default case is not strictly necessary because all 16 combinations are covered.
		endcase
	end
	
endmodule

当然,有更简单的写法,那就看下一题吧。

不过还是先谈谈优先译码器,可能在学数字电路的时候优先译码器是左边第一个为0的数字,在本题中是右面第一个。两者都可以叫做优先译码器,出现的第一个数字把后面的数字屏蔽掉了,第一个数字具有较高的优先级,所以叫做优先译码器。

与优先译码器相对应的简单译码器,简单译码器的输入要求只能有一个1。出现多个1的时候视不同情况有不同的处理方式。

Problem 34: Priority encoder with casez(Always casez)

牛刀小试

构建一个8输入的优先编码器。给定一个8位向量,输出输入向量中左数第一个1的位置。如果输入均为0,则输出零。例如,输入8'b10010000应该输出3'd4,因为位[4]是第一个出现1的位置。

如果还按上一个练习(Problem 33: Priority encoder(Always case2))写case语句的话,case语句中将有256个case项。如果case语句中的case项与某些输入无关,就可以减少列出的case项(在本题中减少到9个)。这就是casez的用途:它在比较中将具有值z的位视为无关项(即输入01都会匹配到)。

例如:下面的代码就是上一个联系中的4输入优先编码器:

代码语言:javascript
复制
always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // in[3:1]输入什么都可以
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end

case项是按顺序检查的(实际上,它更像是生成一个巨大的真值表然后生成超大的门)。注意有输入(例如,4'b1111)匹配多个case项。选择第一个匹配(因此4'b1111匹配第一个case项,out = 0)。

还有一个类似的casex,将输入的x和z都视为无关。不认为casex比casez有什么特别的好处。(作者个人感觉没必要用casex,z和x状态的问题涉及电路的基本知识)

符号"?" 是z的同义词,所以2'bz0与2'b?0相同。

解答与分析

代码语言:javascript
复制
// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos);

always @(*)
    casez (in)
        8'bzzzzzzz1: pos = 0;
        8'bzzzzzz1z: pos = 1;
        8'bzzzzz1zz: pos = 2;
        8'bzzzz1zzz: pos = 3;
        8'bzzz1zzzz: pos = 4;
        8'bzz1zzzzz: pos = 5;
        8'bz1zzzzzz: pos = 6;
        8'b1zzzzzzz: pos = 7;
        default: pos = 0;
    endcase

endmodule

嗯,用casez就方便多了。。。


原文作者:ljgibbs

原文地址:https://zhuanlan.zhihu.com/p/58315855 本公众号授权发布

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档