• HDLbits exercises 4 (MORE VERILOG FEATURES节选题)


    目录

    1\ CONDITIONAL

    2\ REDUCTION

    3\ VECTOR100R

    (1)while循环

    (2)for循环

    (3)repeat循环

    (4)forever循环

    4\ POPCOUNT255

    5\ ADDER100I

    6\ BCDADD100


    1\ CONDITIONAL

    Given four unsigned numbers, find the minimum. Unsigned numbers can be compared with standard comparison operators (a < b). Use the conditional operator to make two-way min circuits, then compose a few of them to create a 4-way min circuit. You'll probably want some wire vectors for the intermediate results.

    HINT:

    Verilog中的三目运算符的用法:

    verilog中有跟c语言类似的三目运算符,这可以用于在一行中根据条件选择两个值中的一个,而不用在组合always块中使用if-then。下面给出一些示例:

    1. (0 ? 3 : 5) // This is 5 because the condition is false.
    2. (sel ? b : a) // A 2-to-1 multiplexer between a and b selected by sel.
    3. always @(posedge clk) // A T-flip-flop.
    4. q <= toggle ? ~q : q;
    5. always @(*) // State transition logic for a one-input FSM
    6. case (state)
    7. A: next = w ? B : A;
    8. B: next = w ? A : B;
    9. endcase
    10. assign out = ena ? q : 1'bz; // A tri-state buffer
    11. ((sel[1:0] == 2'h0) ? a : // A 3-to-1 mux
    12. (sel[1:0] == 2'h1) ? b :
    13. c )

    CORRECT1:将case语句和三目运算符进行配合使用,我这里的三目运算符是嵌套使用的。

    module top_module (
        input [7:0] a, b, c, d,
        output [7:0] min);

        // assign intermediate_result1 = compare? true: false;
        always @(*)
            begin
                case(a>b)
                    1:min=(b>c)?((c>d)?d:c):((b>d)?d:b);
                    0:min=(a>c)?((c>d)?d:c):((a>d)?d:a);
                endcase
            end

        
    endmodule

    CORRECT2:

    module top_module (
        input [7:0] a, b, c, d,
        output [7:0] min);

        wire [7:0] mintemp1;
        wire [7:0] mintemp2;
        assign mintemp1 = (a     assign mintemp2 = (c     assign min = (d

    endmodule

    2\ REDUCTION

    Parity checking is often used as a simple method of detecting errors when transmitting data through an imperfect channel. Create a circuit that will compute a parity bit for a 8-bit byte (which will add a 9th bit to the byte). We will use "even" parity, where the parity bit is just the XOR of all 8 data bits.(奇偶校验经常被用作传输数据时检测错误的简单方法。创建一个电路,为一个8位字节计算一个奇偶校验位(它将在1个字节的基础上增加1位)。如果是偶校验,只要将该8位数据第一位和第二位进行异或,然后将得到的结果和第三位异或,依次下去,直到和第七位异或,
    这样得到的最后结果,就是偶校验位;如果是奇校验,将上面的偶校验位取反即可。)

    DEVELOPMENT(拓展):

            其实在verilog中,与,或,非,异或,同或,这5个运算符,除了非以外,其他4个都是二目运算符,也即是需要两个操作数,然后中间加上相应的运算符。
            但是这里有一种称作,一元约减运算符,就是说,与,或,异或,同或,这4个运算符可以作为一元约减运算符,也即是只需要一个操作数,例如:even_bit = ^a;
            不过运算的过程,和二目运算符不同了,一元约减运算符的运算过程是:首先将操作数 的第一位和第二位进行与、或、非运算;然后再将运算结果和第三位进行与、或、非运算; 依次类推直至最后一位。
            就是说,assign even_bit = ^a;等效于assign even_bit=a[0]^a[1]^a[2]^a[3]^a[4]^a[5]^a[6]^a[7];
            最后提到一下C语言 &=^=|=运算符,例如:b^=a;等效于b=b^a;这个和上面提到的,不是同一个东西,注意不要混淆。我第一次看到那个语句的时候,就是利用C语言的方式来理解的,百思不得其解。

    HINT:

    缩位运算符:

    缩位运算符可以对向量的各个位进行和、或和异或操作,产生1位的输出:

    & a[3:0]      // AND : a[3]&a[2]&a[1]&a[0].   Equivalent to (a[3:0] == 4'hf)
    |  b[3:0]      // OR :  b[3]|b[2]|b[1]|b[0].         Equivalent to (b[3:0] != 4'h0)
    ^  c[2:0]      // XOR : c[2]^c[1]^c[0]

    CORRECT:

    module top_module (
        input [7:0] in,
        output parity); 

        assign parity = ^in;
        
    endmodule

    3\ VECTOR100R

    Given a 100-bit input vector [99:0], reverse its bit ordering.

    DEVELOPMENT:

    verilog循环语句:

    (1)while循环

    格式:

    1. while (condition) begin
    2. end

    while 循环中止条件为 condition 为假。如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。当然,执行语句只有一条时,关键字 begin 与 end 可以省略。

    下面代码执行时,counter 执行了 11 次。

    1. `timescale 1ns/1ns
    2. module test ;
    3. reg [3:0] counter ;
    4. initial begin
    5. counter = 'b0 ;
    6. while (counter<=10) begin
    7. #10 ;
    8. counter = counter + 1'b1 ;
    9. end
    10. end
    11. //stop the simulation
    12. always begin
    13. #10 ; if ($time >= 1000) $finish ;
    14. end
    15. endmodule

    注意:

    当我使用while作为循环编程时,有时会弹出:

    Error (10119): Verilog HDL Loop Statement error at top_module.v(16): loop with non-constant loop condition must terminate within 250 iterations File: /home/h/work/hdlbits.7268514/top_module.v Line: 16

    意思是:“非常量循环条件的循环必须在250次迭代内终止”

    这里的话有两个解决方法:

    方法一:修改编译器默认循环上限。

    在英特尔官网上有给出该错误的解决方案,即在工程的.qsf文件中添加
    set_global_assignment -name VERILOG_NON_CONSTANT_LOOP_LIMIT 300。

    此时循环次数上限修改为 300,实测最大循环上限为 5000,这是很多Verilog教材中没有提到的。
    方法二:当然就是换一个循环语句了,用for之类的循环不香么。

    (2)for循环

    格式:

    1. for(initial_assignment; condition ; step_assignment) begin
    2. end

    initial_assignment 为初始条件。condition 为终止条件,condition 为假时,立即跳出循环。

    step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。

    一般来说,因为初始条件和自加操作等过程都已经包含在 for 循环中,所以 for 循环写法比 while 更为紧凑,但也不是所有的情况下都能使用 for 循环来代替 while 循环。

    下面 for 循环的例子,实现了与 while 循环中例子一样的效果。需要注意的是,i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i -- 的形式。

    1. // for 循环语句
    2. integer i ;
    3. reg [3:0] counter2 ;
    4. initial begin
    5. counter2 = 'b0 ;
    6. for (i=0; i<=10; i=i+1) begin
    7. #10 ;
    8. counter2 = counter2 + 1'b1 ;
    9. end
    10. end

    (3)repeat循环

    格式:

    1. repeat (loop_times) begin
    2. end

    repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。

    下面 repeat 循环例子,实现了与 while 循环中的例子一样的效果。

    1. // repeat 循环语句
    2. reg [3:0] counter3 ;
    3. initial begin
    4. counter3 = 'b0 ;
    5. repeat (11) begin //重复11次
    6. #10 ;
    7. counter3 = counter3 + 1'b1 ;
    8. end
    9. end

    (4)forever循环

    格式:

    1. forever begin
    2. end

    forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。

    forever 相当于 while(1) 。

    通常,forever 循环是和时序控制结构配合使用的。

    例如,使用 forever 语句产生一个时钟:

    1. reg clk ;
    2. initial begin
    3. clk = 0 ;
    4. forever begin
    5. clk = ~clk ;
    6. #5 ;
    7. end
    8. end

    例如,使用 forever 语句实现一个时钟边沿控制的寄存器间数据传输功能:

    1. reg clk ;
    2. reg data_in, data_temp ;
    3. initial begin
    4. forever @(posedge clk) data_temp = data_in ;
    5. end

    ERRO:

    module top_module( 
        input [99:0] in,
        output [99:0] out
    );
        always @(*) begin
            for(i=0;i<100;i++) begin
                assign out[i]=in[99-i];
            end
        end
    endmodule

    CORRECT:

    module top_module( 
        input [99:0] in,
        output [99:0] out
    );
        integer i;
        always @(*) begin
            for(i=0;i<100;i++) begin
                out[i]=in[99-i];
            end
        end
    endmodule

    HINT:

    always与assign不能共存,当你把assign写入always块里面时会报这个错误:

    Error (10043): Verilog HDL unsupported feature error at top_module.v(8): Procedural Continuous Assignment to register is not supported File: /home/h/work/hdlbits.7267506/top_module.v Line: 8
    

    意思是:Verilog HDL不支持的特性错误在top_module.v(8),不支持用于注册的过程连续赋值。

    具体不能共存是因为:

    1、被assign赋值的信号定义为wire型;被always(*)结构块的信号定义为reg型。类型不同不能共用。

    2、另外一个区别则是更细微的差别:举个例子,

        wire a;

          reg b;

       assign a = 1'b0;

       always@(*)

           b = 1'b0;

        在这种情况下,做仿真时a将会正常为0, 但是b却是不定态。这是为什么?verilog规定,always@(*)中的*是指该always块内的所有输入信号的变化为敏感列表,也就是仿真时只有当always@(*)块内的输入信号产生变化,该块内描述的信号才会产生变化,而像always@(*) b = 1'b0;

        这种写法由于1'b0一直没有变化,所以b的信号状态一直没有改变,由于b是组合逻辑输出,所以复位时没有明确的值(不定态),而又因为always@(*)块内没有敏感信号变化,因此b的信号状态一直保持为不定态。事实上该语句的综合结果有可能跟assign一样(本人没有去尝试),但是在功能仿真时就差之千里了。

    4\  POPCOUNT255

    A "population count" circuit counts the number of '1's in an input vector. Build a population count circuit for a 255-bit input vector.

    ERRO:

    module top_module( 
        input [254:0] in,
        output [7:0] out );
        integer i;
        assign out=8'b00000000;
        always @(*)
            begin
                while(i<255)
                    begin
                    if(in[i]==1)
                        out=out+1'b1;
                    else
                        out=out;
                    end

            end
        
    endmodule
     

    CORRECT1:

    module top_module( 
        input [254:0] in,
        output [7:0] out );
        integer i;
        always @(*)
            begin
            out=8'b00000000;
                for(i=0;i<255;i++)
                    begin
                        if(in[i]==1)
                        out=out+1'b1;
                        else
                        out=out;
                    end

            end
        
    endmodule

    CORRECT2:

    module top_module (
        input [254:0] in,
        output reg [7:0] out
    );

        always @(*) begin    // Combinational always block
            out = 0;
            for (int i=0;i<255;i++)
                out = out + in[i];//无非就是判断in,里面有几个一,有一的话直接当做技术信号好了。

        end
        
    endmodule

    HINT:

    So many things to add... How about a for loop?

    使用循环的时候,如果循环次数大于250次的话,就别用while了,换一下;

    值初始化的时候,如果有always块的话,就写在里面即可,别用assign写外面了,这样不能共存。

    5\ ADDER100I

    通过实例化100个全加法器来创建一个100位二进制脉动进位加法器。加法器将两个100位的数字和一个进位相加,得到一个100位的和并执行。为了鼓励您实际实例化全加法器,还需要输出脉动进位加法器中每个全加法器的进位。Cout[99]是最后一个满加法器的最后一个带出值,也是你经常看到的带出值。

    HINT:

    有许多完全加法器要实例化。这里可以使用实例数组或generate语句。

    DEVELOPMENT:

    参考:https://blog.csdn.net/weixin_45270982/article/details/115339274

    generate语句:

    1、介绍

    generate生成语句可以动态的生成verilog代码,当对矢量中的多个位进行重复操作 时,或者当进行多个模块的实例引用的重复操作时,或者根据参数的定义来确定程序中是否应该包含某段Verilog代码的时候,使用生成语句能大大简化程序的编写过程。生成语句生成的实例范围,关键字generate-endgenerate用来指定该范围。生成实例可以是以下的一个或多个类型:模块、用户定义原语、门级语句、连续赋值语句、initial和always块。

    2、用法

    generate_for语句
    (1)、必须使用genvar声明一个正整数变量,用作for循环的判断。(genvar是generate语句中的一种变量类型,用在generate_for中声明正整数变量,放在generate内外都可以。)
    (2)、需要复制的语句必须写到begin_end语句里面。就算只有一句!!!!!!
    (3)、begin_end需要有一个类似于模块名的名字。
    例1:assign语句实现

    module test(bin,gray);
           parameter SIZE=8;
           output [SIZE-1:0] bin;
           input [SIZE-1:0] gray;
           genvar i; //genvar i;也可以定义到generate语句里面
           generate
                  for(i=0;i               begin:bit
                         assign bin[i]=^gray[SIZE-1:i];
                  end
           endgenerate
    endmodule

    等同于下面语句

    assign bin[0]=^gray[SIZE-1:0];
    assign bin[1]=^gray[SIZE-1:1];
    assign bin[2]=^gray[SIZE-1:2];
    assign bin[3]=^gray[SIZE-1:3];
    assign bin[4]=^gray[SIZE-1:4];
    assign bin[5]=^gray[SIZE-1:5];
    assign bin[6]=^gray[SIZE-1:6];
    assign bin[7]=^gray[SIZE-1:7];

    generate_if语句
    generate_for用于复制模块,而generate_if则是根据模块的参数(必须是常量)作为条件判断,来产生满足条件的电路。相当于判断语句。

    module    generate_if(
        input                     t0                    ,
        input                     t1                    ,
        input                     t2                    ,
        output                      d            
    );

    localparam    S = 6;                //定义模块所需参数,用于判断产生电路

    generate 
        if(S < 7)        
            assign d = t0 | t1 | t2;
        else
            assign d = t0 & t1 & t2;
    endgenerate

    endmodule

    generate_case语句

    generate_case其实跟generate_if一样,都是根据参数(都必须为常量)作为判断条件,来产生满足条件的电路,不同于使用了case语法而已。

    module    generate_case(
        input                     t0                    ,
        input                     t1                    ,
        input                     t2                    ,
        output                      d            
    );

    localparam    S = 8;                //定义模块所需参数,用于判断产生电路

    generate 
        case(S)
        0:assign d = t0 | t1 | t2;
        1:assign d = t0 & t1 & t2;
        default:
            assign d = t0 & t1 | t2;
        endcase
    endgenerate

    endmodule

    ERRO:

    module top_module( 
        input [99:0] a, b,
        input cin,
        output [99:0] cout,
        output [99:0] sum );
                                 
        generate
            for(i=0;i<100;i=i+1)
            begin:         
                    assign {cout[i],sum[i]}=a[i]+b[i]+cout[i];
            end
        endgenerate    
    endmodule

    CORRECT:

    module top_module( 
        input [99:0] a, b,
        input cin,
        output [99:0] cout,
        output [99:0] sum );
        genvar i;
        generate
            for(i=0;i<100;i=i+1)
            begin:ct 
                if(i!=0)
                    assign {cout[i],sum[i]}=a[i]+b[i]+cout[i-1];//需要加上前一位的进位,而不是加上本位的进位。
                else
                    assign {cout[0],sum[0]}=a[0]+b[0]+cin;

            end
        endgenerate
    endmodule

    6\ BCDADD100

    您将得到一个名为bcd_fadd的BCD一位数加法器,它将两个BCD数字和进位相加,并生成一个和进位。

    module bcd_fadd (
        input [3:0] a,
        input [3:0] b,
        input     cin,
        output   cout,
        output [3:0] sum );

    实例化100个bcd_fadd,创建100位BCD脉冲进位加法器。你的加法器应该将两个100位的BCD数字(打包成400位向量)和一个进位相加,得到一个100位的和并进位。

    本题示意图:

    将上图所示的每个小bcd_fadd全部实例化即可。

    ERRO:

    module top_module( 
        input [399:0] a, b,
        input cin,
        output cout,
        output [399:0] sum );
        wire [99:0] t;
        integer i;
        always@(*)
            begin
                for(i=0;i<100;i++)
                begin
                    if(i==0)
                        begin
                            bcd_fadd inst(a[3:0],b[3:0],cin,t[0],sum[3:0]);
                        end
                    else
                        begin
                            bcd_fadd inst(a[3+4*i:4*i],b[3+4*i:4*i],t[i-1],t[i],sum[3+4*i:4*i]);
                        end
                end
            cout=t[99];
            end 

    endmodule

    CORRECT:

    module top_module( 
        input [399:0] a, b,
        input cin,
        output cout,
        output [399:0] sum );
        wire [99:0] t;
        genvar i;
        generate
            for(i=0;i<100;i++)
                begin:add
                    if(i==0)
                        begin
                            bcd_fadd inst(a[3:0],b[3:0],cin,t[0],sum[3:0]);
                        end
                    else
                        begin
                            bcd_fadd inst(a[3+4*i:4*i],b[3+4*i:4*i],t[i-1],t[i],sum[3+4*i:4*i]);
                        end
                end
            assign cout=t[99];
        endgenerate
    endmodule

  • 相关阅读:
    English语法_关系代词 - that
    关于面向对象的几大特性所能够解决的编程问题
    JavaScript中的模板直面量
    数据库索引详解
    Android 12.0 SystemUI下拉状态栏定制化之隐藏下拉通知栏布局功能实现(二)
    1个程序员单干之:怎样给我的升讯威在线客服系统编写堪比 MSDN 的用户手册
    2023-10-01 LeetCode每日一题(买卖股票的最佳时机)
    (附源码)php希尔顿酒店管理系统 毕业设计 041148
    【Python自学笔记】报错No module Named Wandb
    rpm包管理工具(常用命令)
  • 原文地址:https://blog.csdn.net/weixin_48304306/article/details/126813640