【USTC】verilog 习题练习 26-30

26 进位选择加法器

前例中的加法器成为串行进位加法器,只有等前一级的加法器运算结束产生进位位之后,下一级加法器才能利用进位位进行计算,因此电路延时会随加法器串联级数的增加而线性增加,这使得电路计算速度大大降低。设每一级全加器的延时为t,则32bit加法器的延时则为:32t。
为降低电路整体延时,我们可以按下图进行设计:
【USTC】verilog 习题练习 26-30_第1张图片
我们将电路分为两段,每段实现16bit的加法,为了使高16位与低16位同时进行运算,我们采用两个add16对高位进行计算,区别在于进位位分别为0和1,最终通过低16位加法器的输出进位作为选择控制信号,选择高16位的运算结果。这样,32bit加法器的延时就变为:16t+tmux2 ≈16t,延时降低了接近一倍,这种以空间(增加电路)换时间(提高速度)的做法,在数字电路设计中经常使用。
请创建Verilog模块,实现上图中的电路结构,其中add16不需要用户编写,其声明如下:
module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
       assign {cout,sum} = a + b + cin;
endmodule

输入格式

32bit a, 32bit b

输出格式

32bit sum 为 a 与 b 的和

注意:在always模块中,需要用reg

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

       assign {cout,sum} = a + b + cin;

endmodule
module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cin,cin1,cin2,cout;
    wire [15:0] sum0, sum1, sum2;
    reg [15:0] sum_total;
    assign cin  = 0;
    assign cin1 = 0;
    assign cin2 = 1;
    add16 add0 (a[15:0], b[15:0], cin, sum0[15:0], cout);
    add16 add1 (a[31:16], b[31:16], cin1, sum1[15:0],);
    add16 add2 (a[31:16], b[31:16], cin2, sum2[15:0],);
    
    always @(*) 
        begin
            case(cout)
                1'b0: sum_total = sum1;
                1'b1: sum_total = sum2;
            endcase
        end
        
    assign sum = {sum_total,sum0};
    
endmodule

27 加法减法器

题目描述

通过对加法器进行改造,可以支持加、减两种运算。我们知道,电路中有符号数通常使用补码表示,如-b其补码为:~b + 1(按位取反然后加1)。因此,对于减法算式a-b,可以理解为a+(-b) = a+(~b+1)= a + (~b) +1,因此对于减法运算,可以将加法器进行如下改造实现:【USTC】verilog 习题练习 26-30_第2张图片
实现减法运算时,首先通过32bit的异或门,将信号b按位取反,同时将输入进位位置1,实现加法运算时,b保持不变,输入进位位置0。
其中add16模块代码如下,用户可直接调用:

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
	assign {cout,sum} = a + b + cin;
endmodule

请创建Verilog模块,实现上述电路功能。

输入格式

32位的a,b,以及一个1位信号sub,sub为1时为减法,sub为0时为加法

输出格式

32位信号sum 注:我想你读到这里的时候,一定跟我一样想着直接用sum=(sub==0?a+b:a-b)逃课了,但是请老老实实地按题目要求分高位低位取补码相加哦~~~

module add16 ( input[15:0] a, input[15:0] b, input cin, output[15:0] sum, output cout );
	assign {cout,sum} = a + b + cin;
endmodule
module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire[31:0] b_reverse;
    wire cout1,cout2;
    // 等价于先对输入求反码,然后再加1。
    // 最终的结果是一个可以做两种操作(即加法和减法)的电路:(a + b + 0)和(a + ~b + 1)。
    assign b_reverse = b^{32{sub}}; 
    add16 inst1(a[15:0], b_reverse[15:0], sub, sum[15:0], cout1);
    add16 inst2(a[31:16], b_reverse[31:16], cout1, sum[31:16], cout2);
endmodule

28 always过程块_组合逻辑

题目描述

所有的数字电路都是由逻辑门和连线构成的,因此理论上来说都可以通过模块的连接和assign语句进行描述,然而在很多情况下这并不是最方便的一种方式,过程块提供了一种更加方便的描述方式,always过程块便是其中最常用的一种。
对于可综合电路(即能转化成实际电路的verilog描述方式,与之相对的是不可综合电路,多用于电路仿真,不能转换成实际电路),有两种always块的语法形式:
-组合逻辑电路:always@()
-时序逻辑电路:always@(posedge clk)
组合逻辑电路的always块与assign语句等效,用户描述组合逻辑电路时,可根据便利性选择其中一种方式使用。两者生成的硬件电路一般是等效的,但在语法规则上稍有不同:
-assign语句只能对一个信号进行赋值,always块内可对多个信号进行赋值
-assign语句中被赋值信号为wire类型,always块内被赋值信号需定义为reg类型
-always块内支持更加丰富的语法,如使用if…else..、case等适合实现交复杂的组合逻辑
例如下述两条语句是等效的(out1需定义为wire类型,out2需定义为reg类型,但这仅仅是语法上的要求,生成的电路并没有区别):
assign out1 = a & b | c ^ d;
always @(
) out2 = a & b | c ^ d;
其对应的电路图如下所示:
【USTC】verilog 习题练习 26-30_第3张图片
always语句后的括号内放的是敏感变量列表,对于上例来说,可以写成always @(a,b,c,d) out2 = a & b | c ^ d,但为了简单起见,我们一般都用符号*代替。
试创建一verilog模块,实现一与门,分别用assign语句和always块实现。

输入格式

1位的a,1位的b

输出格式

1位的out_assign,1位的out_alwaysblock

module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
    //assign statement
	assign out_assign = a & b;
    
    //combinational always block
    always @(*) begin
        out_alwaysblock = a & b;
    end
endmodule

29 always过程块_时序逻辑

题目描述

通过前例已经了解到,对于可综合电路,有两种always块的语法形式:

  • 组合逻辑电路:always@(*)
  • 时序逻辑电路:always@(posedge clk)

用always描述的时序逻辑电路,除了像组合逻辑always块那样生成组合逻辑电路外,还会生成一组触发器(或称寄存器),用于寄存组合逻辑的输出。寄存器的输出只有在时钟的上升沿时(posedge clk)才会更新,其余时刻均保持不变。
阻塞赋值和非阻塞赋值:
在Verilog中,有三种赋值方式,分别为:

  • 连续赋值(如assign x = y;),该赋值方式只能用于过程块(如always块)之外
  • 阻塞赋值(如x = y;),该赋值方式只能用在过程块(如always@(*))内
  • 非阻塞赋值(如x <= y;),该赋值方式只能用在过程块内(如always@(posedge clk)

在设计Verilog模块时,请遵循以下原则:

  • 在组合逻辑的always块内采用阻塞赋值
  • 时序逻辑的always块内采用非阻塞赋值

违背这一原则将可能导致难以发现的电路错误,且可能导致仿真与综合的不一致,请用户切记。至于为何这样,初学者可以不必理会,简单理解为verilog语法规范性要求即可。
创建一verilog电路,分别采用上述三种赋值方式实现异或门电路,如下图所示:【USTC】verilog 习题练习 26-30_第4张图片

Hint

  • always块内被赋值的信号都应定义成reg类型
  • always块内,组合逻辑采用阻塞赋值(a = b),时序逻辑采用非阻塞赋值(a <= b
  • always语句括号内是敏感变量列表,时序逻辑是边沿敏感的,posedge clk表示的是clk信号的上升沿,此外,还可以是negedge clk,表示clk信号的下降沿。

输入格式

一位线网型变量clk,a, b。clk为时钟,a,b为输入

输出格式

一位线网型变量out_assign,out_always_comb,out_always_ff。out_assign为a,b连续赋值得到的结果。out_always_comb为a,b阻塞赋值得到的结果。out_always_ff为a,b非阻塞赋值得到的结果

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 @(*) begin
        out_always_comb = a ^ b;
    end
    
    always @(posedge clk)
        out_always_ff <= a ^ b;
//用户编辑到此为止
endmodule

30 if...else...语句

题目描述

if语句用于过程块内部,其对应的电路是二选一的选择器,
【USTC】verilog 习题练习 26-30_第5张图片
以下述代码为例:
always@(*)
begin
if(condition) out = x;
    else out = y;
end
上述代码与下面的assign语句完全等效:
assign out = (condition) ? x : y;
试创建一Verilog模块,分别采用assing语句和过程块内的if语句实现下述选择器电路:

【USTC】verilog 习题练习 26-30_第6张图片

Hint:
1.if…else…可以嵌套使用,如

if … 
else if… 
else if… 
else…

2.使用if语句描述组合逻辑时,务必加上else语句,以免产生锁存器(数字电路设计中应尽力避免产生锁存器)
3.本题两个输出信号波形其实是完全一致的,原则上是为了训练大家采用assign和过程块内的if语句使用,所以希望大家能够两种方式都各自尝试一下

输入格式

信号a, b, 选择信号sel_b1, sel_b2

输出格式

通过assign语句选择的信号out_assign 通过if语句选择的信号out_always

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) begin
            out_always = b;
        end
        else begin
            out_always = a;
        end
    end
//用户编辑到此为止
endmodule

你可能感兴趣的:(fpga开发)