HDLBits(4) Procedures合集

5 篇文章 3 订阅

Alwaysblock1:combinational

由于数字电路是由连接电线逻辑门组成的,任何电路都可以表示为一些模块和赋值语句的组合。然而,有时这并不是描述电路最方便的方法。过程(block总是一个例子)提供了描述电路的另一种语法。

有两种类型的always块是与可综合硬件相关的:
组合逻辑: always @(*)
时序逻辑: always @(posedge clk)

always块等价于赋值语句,因此总是有两种方式来表达一个组合电路。选择使用哪个方法主要是哪个语法更方便的问题。过程块内部代码的语法与外部代码不同。过程块有更丰富的语句集(例如if-then, case),不能包含连续的赋值但也引入了许多新的不直观的出错方法

例如,赋值语句和组合always块描述相同的电路。两者都创建了相同的组合逻辑。当任何输入(右侧)更改值时,两者都将重新计算输出

assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;

对于组合always块,总是使用(*)的敏感性列表。明确列出信号很容易出错的(如果您错过了一个),并且在硬件合成时被忽略。如果你显式地指定了灵敏度列表并且错过了一个信号,合成的硬件仍然会表现得像指定了 ( * ) 一样,但是模拟将不匹配硬件的行为。(在SystemVerilog中,使用always_comb。)

关于wire和reg的注意事项:赋值语句的左边必须是net类型(例如wire),而过程赋值语句(在always块中)的左边必须是变量类型(例如reg)。这些类型(wire vs. reg)与合成什么硬件无关,只是Verilog作为硬件模拟语言使用时遗留下来的语法。
在这里插入图片描述

Problem Statement

使用assign语句和组合的always块构建与门。(因为赋值语句和组合语句总是相同地阻塞函数,所以没有办法强制你同时使用这两种方法。但你是来练习的,对吧?)

Writing Code

// synthesis verilog_input_version verilog_2001
module top_module(
    input a, 
    input b,
    output wire out_assign,//赋值语句左侧用wire
    output reg out_alwaysblock//always中的过程赋值语句用reg
    //用两种方式,两种输出
);
    //assign statement
    assign out_assign = a & b;
    
    //combinational always block
    always@(*) begin
    	out_alwaysblock = a & b;
    end
    
endmodule

Alwaysblock2:clocked

对于硬件合成,有两种类型的总是相关的块:

组合:always @(*)
时序:always @(posedge clk)

时序always块创建一组组合逻辑像组合always块一样,但也在组合逻辑输出处创建一组触发器(或“寄存器”)不像逻辑的输出是立即可见的,它的输出而是在下一个(posedge clk)之后才可见。

阻塞 VS 非阻塞赋值

在Verilog中存在三种赋值:
·连续赋值(assign x = y;):不能在过程(“always块”)中使用。
·过程阻塞赋值(x = y;):只能在过程中使用。
·过程非阻塞赋值(x <= y;):只能在过程中使用。

划重点!
在组合always块中,使用阻塞性赋值。在时序always块中,使用非阻塞性赋值。完全理解是为什么对硬件设计用处不大,还需要理解Verilog模拟器如何跟踪事件不遵循这一规则就很难发现仿真和综合硬件之间既不确定又不同的错误。

Problem Statement

使用赋值语句组合always块时钟always块三种方法构建异或门。注意,时钟always块产生一个不同于其他两个的电路:有一个触发器,所以输出被延迟。
在这里插入图片描述

Writing Code

// synthesis verilog_input_version verilog_2001
module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,//赋值语句用wire
    output reg out_always_comb,//过程块always @(*)用reg
    output reg out_always_ff   );//过程块always @(posedge clk)用reg

    assign out_assign = a^b;
    
    always @(*)begin
    	out_always_comb = a^b;
    end
    
    always @(posedge clk)begin
    	out_always_ff <= a^b;//注意这儿是<=阻塞式赋值!
    end
    
endmodule

在这里插入图片描述解释:为啥下面这个地方不一样但是还是success了
前文有一句:
“注意,时钟always块产生一个不同于其他两个的电路:有一个触发器,所以输出被延迟。”
时序always块创建一组组合逻辑像组合always块一样,但也在组合逻辑输出处创建一组触发器(或“寄存器”)不像逻辑的输出是立即可见的,它的输出而是在下一个(posedge clk)之后才可见。

另外一方面的解释是:always @(posedge clk)begin
与正规的相比,少了一个下降沿复位的信号,也就是一开始的状态。此时没有,所以说并不是一开始就是复位状态的,是不定态的。

Always if:if statement

if语句通常创建一个2选1的多路复用器,如果条件为真,则选择一个输入;如果条件为假,则选择另一个输入。
在这里插入图片描述

always @(*) begin
    if (condition) begin
        out = x;
    end
    else begin
        out = y;
    end
end
//注意格式!有begin就有end,有if 就要有else,case必跟default

这相当于使用带有条件运算符连续赋值语句:

assign out = (condition) ? x : y;

然而,if语句提供了一种新的出错方法。只有当输出被赋值时,电路才是组合的。

Problem Statement

构建一个在a和b之间进行选择2选1数据选择器如果sel_b1和sel_b2 都为真,选择b;否则选择a。实现两次,一次用赋值语句,一次用if语句
在这里插入图片描述

Writing Code

// 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   ); 

    //如果sel_b1和sel_b2 都为真,选择b;否则选择a
    assign out_assign = (sel_b1 && sel_b2)?b:a;

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

Always if2:if statement latches

Problem Statement

常见的错误来源:如何避免产生锁存器?
在设计电路时,关于电路必须首先考虑
·我想要这个逻辑门
·我想要一个逻辑组合块,它有一些输入,产生这一些输出
·我想要一个逻辑组合块,后面带有触发器

你千万不能先写代码,然后希望它能产生一个合适的电路。

If (cpu_overheated) then shut_off_computer = 1;
If (~arrived) then keep_driving = ~gas_tank_empty;

语法正确的代码不一定会产生合理的电路(组合逻辑+触发器)。通常的原因是:“除了您指定的那些情况之外,在其他情况下会发生什么?”Verilog的答案是:保持输出不变(产生锁存)。

“除了您指定的那些情况之外,在其他情况下会发生什么?”这应该是我们该突破的思维误区

这种“保持输出不变的行为”意味着需要记住当前状态,从而产生一个锁存器。组合逻辑(例如,逻辑门)不能记住任何状态。注意“Warning (10240): … inferring latch(es)”信息。除非锁存是故意的,否则它几乎总是指出一个bug组合电路必须在所有条件下都有一个值分配给输出。这通常意味着你总是需要else子句或默认赋值给输出。

以下代码包含产生锁存器不正确行为
修复漏洞,这样只有在电脑过热的时候你才会关掉电脑,当你到达目的地或者需要加油的时候才会停止开车(原作者的形象解释)。

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

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

在这里插入图片描述

Writing Code

// 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;//当你 到达目的地 或者 需要加油的时候 才会停止开车
           //此处意为:有油的时候就继续keep_driving
        else  //到达的时候
           keep_driving = ~arrived;
    end

endmodule



总结:if必须带else
case必须带default和endcase

Always case:case statement

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

always @(*) begin     //这是一个组合电路
    case (in)
      1'b1: begin 
               out = 1'b1;  //如果超过了1个语句,就要用begin-end了,相当于{   }
               end
      1'b0: out = 1'b0;
      default: out = 1'bx;
    endcase
end

·case语句以case开头,每个“case item”以冒号结束。switch语句没有。
·每个case项可以只执行一条语句。这使得C语言中使用的**“break”在这里没有必要**。如果需要多个语句,则必须使用begin…end。
·重复的(和部分重复)的case是允许的。使用第一个匹配的。C不允许重复案例项目

Problem Statement

如果在有许多种的情况下,case语句比if语句更方便。因此,在这个练习中,创建一个6对1的多路复用器。当sel在0到5之间时,选择相应的数据输入。否则,输出0。数据输入和输出都是4位宽

Writing Code

// 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'd0是不行的,注意数据格式
            3'b001 : out = data1;
            3'b010 : out = data2;
            3'b011 : out = data3;
            3'b100 : out = data4;
            3'b101 : out = data5;
       		default:out = 4'b0;
        endcase   
    end

endmodule

Always case2:priority encoder

优先级编码器是一种组合电路,当给定一个输入位向量时,输出向量中第一个1位的位置。例如,给定输入8’b10010000的8位优先级编码器将输出3’d4,因为位[4]是第一个高的位。
注意:是从位到位!

Problem Statement

构建一个4位优先级编码器。如果没有一个输入位是高的(例如输入为零),那么输出为零。注意,一个4位数字有16种可能的组合。

Hint

使用十六进制(4’hb)或十进制(4’d11)数字字面值会比二进制(4’b1011)字面值节省键入时间。

Writing Code

因为是第一位的位置,所以说4位的优先级编码器,也就0、1、2、3四种输出结果。
那如何匹配in呢?
抓住几个关键的数字
比如0001、0011、0111、是1
是3
0100是4
1000是8
那么之间的都是输出的某一个固定的数字
加粗样式
为了便于理解和区分:我在下面的程序进行了“分段”
以下是官方的答案,但是我更喜欢我的答案

module top_module (
	input [3:0] in,
	output reg [1:0] pos
);

	always @(*) begin			// Combinational always block
		case (in)//16种情况
			4'h0: pos = 2'h0;	// I like hexadecimal 16进制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
	
	// There is an easier way to code this. See the next problem (always_casez).
	
endmodule

我更喜欢我的答案,因为虽然最后的输出是2进制的,但是我更喜欢前面in的情况用二进制来看,相对于16进制来说,不容易出错

// 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

Always casez:priority encoder with casez

Problem Statement

构建一个8位输入优先级编码器。给定一个8位向量,输出应该指出向量中的第一个是1的位如果输入向量没有1,则输出0。例如,输入8’b10010000应该输出3’d4,因为位[4]是第一个高(为“1”)的位。

与上一次不同的地方在于,这一次是8位,情况更多了,该如何处理??

根据前面的练习(Always_case2), case语句中有256种情况。如果case语句中的case项支持“不关心”位,我们可以将其减少(减少到9种情况)。
这就是casez的作用:它将值z的位视为比较中不关心的位。

例如,这将实现之前练习中的4输入优先级编码器:

always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // in[3:1] can be anything
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end

case语句的行为就像按顺序检查每一项一样(实际上,它做的事情更像是生成一个巨大的真值表,而不是生成门)。注意某些输入(例如,4’b1111)如何匹配多个case项。选择第一个匹配项(因此4’b1111匹配第一个项out = 0,但不匹配后面的任何项)。

还有一个类似的casex,它把x和z都当作“不关心”。我看不出它比casez有更多的用处。
符号?是z的同义词,所以2’bz0和2’b?0相同。

Hint

这里必须使用二进制文字,因为需要为某些位指定z。

Writing Code

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 智慧社区背景与挑战 随着城市化的快速发展,社区面临健康、安全、邻里关系和服务质量等多方面的挑战。华为技术有限公司提出智慧社区解决方案,旨在通过先进的数字化技术应对这些问题,提升城市社区的生活质量。 2. 技术推动智慧社区发展 技术进步,特别是数字化、无线化、移动化和物联化,为城市社区的智慧化提供了可能。这些技术的应用不仅提高了社区的运行效率,也增强了居民的便利性和安全性。 3. 智慧社区的核心价值 智慧社区承载了智慧城市的核心价值,通过全面信息化处理,实现对城市各个方面的数字网络化管理、服务与决策功能,从而提升社会服务效率,整合社会服务资源。 4. 多层次、全方位的智慧社区服务 智慧社区通过构建和谐、温情、平安和健康四大社区模块,满足社区居民的多层次需求。这些服务模块包括社区医疗、安全监控、情感沟通和健康监测等。 5. 智慧社区技术框架 智慧社区技术框架强调统一平台的建设,设立数据中心,构建基础网络,并通过分层建设,实现平台能力及应用的可持续成长和扩展。 6. 感知统一平台与服务方案 感知统一平台是智慧社区的关键组成部分,通过统一的RFID身份识别和信息管理,实现社区服务的智能化和便捷化。同时,提供社区内外监控、紧急救助服务和便民服务等。 7. 健康社区的构建 健康社区模块专注于为居民提供健康管理服务,通过整合医疗资源和居民接入,实现远程医疗、慢性病管理和紧急救助等功能,推动医疗模式从治疗向预防转变。 8. 平安社区的安全保障 平安社区通过闭路电视监控、防盗报警和紧急求助等技术,保障社区居民的人身和财产安全,实现社区环境的实时监控和智能分析。 9. 温情社区的情感沟通 温情社区着重于建立社区居民间的情感联系,通过组织社区活动、一键呼叫服务和互帮互助平台,增强邻里间的交流和互助。 10. 和谐社区的资源整合 和谐社区作为社会资源的整合协调者,通过统一接入和身份识别,实现社区信息和服务的便捷获取,提升居民生活质量,促进社区和谐。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值