ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

verilog language-Procedures

2021-11-28 13:34:20  阅读:178  来源: 互联网

标签:end language always pos b0 verilog input Procedures out


Problem 28:Always blocks(combination)(Alwaysblock1)

任何电路都可以用 moduleassign 语句组合出来。

这里介绍一种过程块,always 块,提供了一种更便捷地描述电路的方法

有两种 always 块可以综合出电路硬件的

always @(*)//综合逻辑
always @(posedge clk)//时序逻辑
  • 组合 always 块相当于 assign 语句

过程块内的代码与外部的 assign 代码不同。

  • 过程块中,我们可以使用比较丰富的语句(if-then ,case ),但不能包含连续赋值

例如,assign 和组合和 always 块描述相同的电路,两者创造出了相同的组合逻辑电路,只要任何输入(右侧)改变值,两者都将重新计算输出

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

对于组合 always 块,敏感变量列表总是使用 (*)

如果把所有的输入都列出来也是可以的,但容易出错的(可能少列出了一个),并且在硬件综合时会忽略您少列了一个,仍按原电路综合。 但仿真器将会按少列一个来仿真,这导致了仿真与硬件不匹配。(在 SystemVerilog 中,使用 always_comb)

使用 assign 语句和组合 always 块来构建与门。

// synthesis verilog_input_version verilog_2001
module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
assign out_assign=a&b;
    always @(*)
        out_alwaysblock=a&b;//两种方法实现与门
endmodule

Problem 29:Always blocks(clocked)(Alwaysblock2)

对于硬件综合来说,存在两种 always 块

always @(*)//组合逻辑
always @(posedge clk)//时序逻辑

时序 always 块也会像组合 always 块一样生成一系列的组合电路,但同时在组合逻辑的输出生成了一组触发器(或寄存器)

  • 通过时序 always 块的输出会在下一个时钟上升沿(posedge clk)后可见。

阻塞性赋值和非阻塞性赋值

在 Verilog 中有三种赋值方法:

连续赋值(assign x=y;):不能在过程块内使用;
过程阻塞性赋值(x=y;):只能在过程块中使用;
过程非阻塞性赋值(x<=y):只能在过程块内使用。

使用 assign 语句,组合 always 块和时序 always 块,这三种方式来构建异或门。

  • 时序 always 块生成了与另外两个不同的电路,多了一个触发器,因此输出会有一定的延时
// synthesis verilog_input_version verilog_2001
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 @(*) out_always_comb=a^b;
    always @(posedge clk) out_always_ff<=a^b;//注意这里最好用<=,过程非阻塞性赋值
endmodule

  • 从仿真的波形图可以看出,out_always_ff 比其他两个输出延迟了一个时钟周期,这就是非阻塞性赋值带来的。

Problem 30:If statement(Always if)

  • if 语句通常对应一个二选一多路复用器,如果条件为真,则选择其中一个输入作为输出;反之如果条件为假,则选择另一个输入所谓输出。
  • if 语句必须在过程块内使用。
    always @(*) begin
        if (condition) begin
            out = x;
        end
        else begin
            out = y;
        end
    end
    

  • 这与下面使用条件运算符连续赋值的语句是等价的:
    assign out = (condition) ? x : y;
    

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

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

sel_b1 sel_b2 out_assign
out_always
0 0 a
0 1 a
1 0 a
1 1 b
// 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;
    always @(*)begin
        if(sel_b1&sel_b2)
            out_always=b;
        else 
            out_always=a;
    end

endmodule

Problem 31:If statement latches(Always if2)

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

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

  1. 我想实现一个逻辑门;
  2. 我想实现一个具有输入并产生输出的组合逻辑块
  3. 我想实现一组组合逻辑,紧接着一组触发器

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

语法正确的代码不一定会产生合理的电路(组合逻辑 + 触发器)。

在你用 if 语句的时候,Verilog 会在你指定情况之外保持输出不变,这意味着电路需要记住当前状态,从而产生锁存器,组合逻辑并不能记住任何状态,这就会导致错误。

Warning (10240): ... inferring latch(es)

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

示例:以下代码包含生成锁存器的错误,请勿模仿!!!

修复错误,只有当它真的过热时才关闭计算机,真的到达目的地或者需要加油时,才停止驾驶。

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

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

修改如下:

// 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-elseif-else。

语法示例:

always @(*) begin     // This is a combinational circuit
    case (in)//与C的switch不同
      1'b1: begin 
               out = 1'b1;  // begin-end if >1 statement
            end
      1'b0:    out = 1'b0;
      default: out = 1'bx;
    endcase
end

如果存在大量的 case 项,则 case 语句比 if 语句更方便。

在本练习中,创建一个 6 选 1 的多路复用器。当 sel 介于 0 和 5 之间时,选择相应的数据输入。 其他情况输出 0。数据输入和输出均为 4 位宽。

注意:不要生成锁存器(Problem 31)

// 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;//一定要用default声明不在case项里的输出,否则会生成不必要的寄存器
        endcase
    end

endmodule

problem 33:Priority encoder(Always case 2)

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

构建一个 4 位优先编码器,如果没有一个输入位,则输出零,

// 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
// synthesis verilog_input_version verilog_2001
module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
always @(*) begin
if (in[0]) pos = 0;//最省代码的写法
else if (in[1]) pos = 1;
else if (in[2]) pos = 2;
else if (in[3]) pos = 3;
else pos = 0;
end
endmodule

Problem 34:Priority encoder with casez(Always casez)

构建一个 8 输入的优先编码器,给定一个 8 位向量,输出输入向量中第一个 1 的位置。如果没有输入,则输出 0。

例如,输入 8'b10010000 应该输出 3'd4,因为位[4]是第一个出现 1 的位置。

case 项是按顺序去检查的,实际上像是生成了一个巨大的真值表和一个门,当输入匹配多个 case 项时,匹配第一个遇到的 case。

如果按照上一个去写 case 语句的话,case 语句中将有 256 个 case 项,引入 casez

如果 case 语句中的 case 项与某些输入无关,就可以减少列出的 case 项

casez:在比较中将具有值 z 的位视为无关项。

上题可用 casez 写为

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
// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always @(*)
        casez (in[7:0])
            8'bzzzzzzz1:pos=3'b000;//这里casez是按照顺序来执行的
            8'bzzzzzz1z:pos=3'b001;//也可以写成8‘bzzzzzz10:pos=3'b001,写逻辑比较清晰且不用看顺序
            8'bzzzzz1zz:pos=3'b010;
            8'bzzzz1zzz:pos=3'b011;
            8'bzzz1zzzz:pos=3'b100;
            8'bzz1zzzzz:pos=3'b101;
            8'bz1zzzzzz:pos=3'b110;
            8'b1zzzzzzz:pos=3'b111;
            default :pos=3'b000;
        endcase//注意这边是endcase
endmodule

Problem 35:Always nolatches(Always nolatches)

假设建立一个电路,去处理游戏 PS/2 键盘扫描码。给出接收到扫描码的最后的两个字节,您需要判断是否有按键被按下。是一个相当简单的映射,可以使用 case 语句或者 if-else 语句实现,一共有如下四种情况。

Scancode [15:0] Arrow key
16'he06b left arrow
16'he072 down arrow
16'he074 right arrow
16'he075 up arrow
Anything else none

16 个输入,4 个输出,描述该电路,并识别这四个按键的扫描码并输出

同时为避免生成了不必要的锁存器,必须在所有条件下为所有的输出赋值(参见 Problem 31: If statement latches(Always if2) )。这可能会多打很多字,使你的代码变得冗长。 一个简单的方法是在 case 语句之前为输出分配一个“默认值”:

always @(*) begin
    up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
    case (scancode)
        ... // Set to 1 as necessary.
    endcase//这种代码格式可以确保“默认值”的输出,default项无关紧要了(前提是“默认值”没有被覆盖)
end
  • 笨办法(最全)
// synthesis verilog_input_version verilog_2001
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    always@(*)
    begin
        up=1'b0;
    	down=1'b0;
    	left=1'b0;
    	right=1'b0;
    case (scancode)
        16'he06b: begin
            up=1'b0;
    		down=1'b0;
    		left=1'b1;
            right=1'b0;end
        16'he072:begin
            up=1'b0;
    		down=1'b1;
    		left=1'b0;
            right=1'b0;end
        16'he074:begin 
            up=1'b0;
    		down=1'b0;
    		left=1'b0;
            right=1'b1;end
        16'he075:begin
            up=1'b1;
    		down=1'b0;
    		left=1'b0;
            right=1'b0;end
        default :begin
            up=1'b0;
    		down=1'b0;
    		left=1'b0;
            right=1'b0;end
    endcase
    end
endmodule
  • 笨办法的简化版
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 

always@(*)
    casez(scancode)
        16'he06b: begin up = 1'b0; down = 1'b0; left = 1'b1; right = 1'b0; end
        16'he072: begin up = 1'b0; down = 1'b1; left = 1'b0; right = 1'b0; end
        16'he074: begin up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b1; end
        16'he075: begin up = 1'b1; down = 1'b0; left = 1'b0; right = 1'b0; end
        default:  begin up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0; end
    endcase

endmodule
  • 最佳 solution
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 

always@(*)
begin
    up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
    casez(scancode)
        16'he06b: left = 1'b1;
        16'he072: down = 1'b1;
        16'he074: right = 1'b1; 
        16'he075: up = 1'b1;
    endcase
end

endmodule
  • 上面的“最佳 solution”会报告一个 case 语句没有 default 的 warning,但综合出的电路应该是和“笨办法”一样的

标签:end,language,always,pos,b0,verilog,input,Procedures,out
来源: https://www.cnblogs.com/zjj666/p/15614837.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有