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。下面给出一些示例:

(0 ? 3 : 5)     // This is 5 because the condition is false.
(sel ? b : a)   // A 2-to-1 multiplexer between a and b selected by sel.

always @(posedge clk)         // A T-flip-flop.
  q <= toggle ? ~q : q;

always @(*)                   // State transition logic for a one-input FSM
  case (state)
    A: next = w ? B : A;
    B: next = w ? A : B;
  endcase

assign out = ena ? q : 1'bz;  // A tri-state buffer

((sel[1:0] == 2'h0) ? a :     // A 3-to-1 mux
 (sel[1:0] == 2'h1) ? b :
                      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<b)? a:b;
    assign mintemp2 = (c<mintemp1)?c:mintemp1;
    assign min = (d<mintemp2)?d:mintemp2;

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循环

格式:

while (condition) begin
    …
end

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

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

`timescale 1ns/1ns
 
module test ;
 
    reg [3:0]    counter ;
    initial begin
        counter = 'b0 ;
        while (counter<=10) begin
            #10 ;
            counter = counter + 1'b1 ;
        end
    end
 
   //stop the simulation
    always begin
        #10 ;  if ($time >= 1000) $finish ;
    end
 
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循环

格式:

for(initial_assignment; condition ; step_assignment)  begin
    …
end

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

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

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

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

// for 循环语句
integer      i ;
reg [3:0]    counter2 ;
initial begin
    counter2 = 'b0 ;
    for (i=0; i<=10; i=i+1) begin
        #10 ;
        counter2 = counter2 + 1'b1 ;
    end
end

(3)repeat循环

格式:

repeat (loop_times) begin
    …
end

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

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

// repeat 循环语句
reg [3:0]    counter3 ;
initial begin
    counter3 = 'b0 ;
    repeat (11) begin  //重复11次
        #10 ;
        counter3 = counter3 + 1'b1 ;
    end
end

(4)forever循环

格式:

forever begin
    …
end

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

forever 相当于 while(1) 。

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

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

reg          clk ;
initial begin
    clk       = 0 ;
    forever begin
        clk = ~clk ;
        #5 ;
    end
end

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

reg    clk ;
reg    data_in, data_temp ;
initial begin
    forever @(posedge clk)      data_temp = data_in ;
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<SIZE;i=i+1)
              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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wo~he!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值