HDLBits——More Verilog Features

HDLBits——More Verilog Features

Problem 36: Conditional ternary operator(Conditional)

Requirement:

给出四个无符号数,请找到其中的最小值。

提示:

  1. 使用条件运算符描述一个两路的最小值电路,然后组合它来创建一个4路最小电路,可能需要一些wire变量用于表述中间结果。
  2. 无符号数可以使用比较运算符进行比较
Solution:
module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min);//
    
    // assign intermediate_result1 = compare? true: false;
    wire [7:0] min1,min2;
    assign min1 = (a > b) ? b : a;
    assign min2 = (c > d) ? d : c;
    assign min = (min1 > min2) ? min2 : min1;

endmodule

PS:条件运算符? :是一个三元运算符,可以在一行代码上实现一个二选一,而不需要在 always 块中使用 if-else 语句,例如:

always @(posedge clk)         // 一个T触发器
  q <= toggle ? ~q : q;

((sel[1:0] == 2'h0) ? a :     // 一个三选一MUX
 (sel[1:0] == 2'h1) ? b :
                      c )
Timing Diagram:

image-20211129125436455

Problem 37: Reduction operators(Reduction)

Requirement:

奇偶校验是常用于信道传输数据时检测错误的方法,构建一个电路,计算8位字节输入的校验位(将向该字节添加第9位)。 这里使用偶校验,即奇偶校验位是所有8个数据位的XOR。

奇偶校验:通过检验位将传输1的个数变成奇数就是奇校验,变成偶数就是偶校验。

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

    assign parity = ^ in;

endmodule

PS:

有时候想要构建一个输入比较多的门,对一个向量的所有位进行操作,如 a[0]&a[1]&a[2]&a[3]…,但这对于长的标量来说,这很麻烦。归约运算符(Reduction Operators),是只有一个操作数的一元运算符(类似于 ! 和 ~),可以对向量的每一位进行 AND,OR 和 XOR,产生一位输出,同理还有 &,|,~^,例如:

&a [3:0] // AND:a[3]&a[2]&a[1]&a[0]
|b [3:0] // OR: b[3]|b[2]|b[1]|b[0]
^c [2:0] // XOR:c[2]^c[1]^c[0]
! VS ~

“!”表示逻辑取反,“~”表示按位取反。当面对位宽为1时,两个操作符的作用相同。

当位宽不为1时,“~”会将变量的各个位依次取反如:a[3:0] ={1,0,0,1} , ~a ={0,1,1,0};

“!”会将变量作为一个值去做处理,非0为1:a[3:0] ={1,0,0,1} ,a=5,!a=0。a[3:0] ={0,0,0,0} ,a=0,!a=1。

Problem 38: Reduction: Even wider gates(Gates100)

Requirement:

构建具有100个输入的组合电路。

电路一共有3个输出:

image-20211129152510547

Solution:
module top_module( 
    input [99:0] in,
    output out_and,
    output out_or,
    output out_xor 
);

    assign out_and = & in;
    assign out_or = | in;
    assign out_xor = ^ in;

endmodule
Timing Diagram:

image-20211129152826315

Problem 39: Combinational for-loop: Vector reversal2(Vector100r)

Requirement:

给了一个长度是100的向量,请把它翻转后输出。

提示:for 循环(组合 always 块或者 generate 块)在这里很有用。 这道题中,因为不需要模块实例化(必须使用 generate 块),建议使用 always 块。

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

方法二:

module top_module( 
    input [99:0] in,
    output [99:0] out
);

    generate 
        genvar i;
        begin
            for (i = 0; i<100; i=i+1) begin:name2
                assign out[i] = in[99-i];
            end
        end
    endgenerate
endmodule

PS:

  1. 翻转指的是高低位,不是 0、1。
  2. 要在语句块内声明变量,必须得给块命名,即在 begin 后面 : 名字
  3. 在过程语句中(initial或者always),被赋值信号需要定义成 reg 类型,否则 wire 类型。

Problem 40 Combinational for-loop: 255-bit population count

Requirement:

设计电路来计算输入矢量中 ’1‘ 的个数。

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

    always @(*) begin:name1 
        integer i;
        out = 8'b0;
        for (i = 0; i<255; i=i+1) begin
            if (in[i] == 1) begin
                out = out + 1;
            end
            else begin
                out = out;
            end
        end
    end

endmodule

不能写作 generate,会报错,我的猜测是遍历和重复还是有区别的,这里只是遍历,没有重复生成什么代码。

Timing Diagram:

image-20211129160905540

Problem 41 Generate for-loop: 100-bit binary adder 2

Requirement:

通过实例化 100 个全加器来实现一个 100bit 的二进制加法器。该加法器有两个 100bit 的输入和 cin,输出为 sum 与 cout。为了鼓励大家使用实例化来完成电路设计,我们同时需要输出每个全加器的 cout, 故 cout[99] 标志着全加器的最终进位。

Solution:
module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );

    add_1bit add_1bit_0(
        .a(a[0]),
        .b(b[0]),
        .cin(cin),
        .cout(cout[0]),
        .sum(sum[0])
    );
    generate
        genvar i;
        for(i=1;i<100;i=i+1) begin:add_100
            add_1bit add_1bit_i(
                .a(a[i]),
                .b(b[i]),
                .cin(cout[i-1]),
                .cout(cout[i]),
                .sum(sum[i])
            );
        end
    endgenerate

endmodule


module add_1bit (
    input a,
    input b,
    input cin,
    output sum,
    output cout
);

    assign cout = (a&b) | (a&cin) | (b&cin);
    assign sum = a^b^cin;

endmodule

错过:cin 作为输入,由外部信号确定,不要给人家瞎赋值。

generate 语句

生成语句可以动态的生成 verilog 代码,当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,使用生成语句能大大简化程序的编写过程。generate 语句有 generate-for、generate-if、generate-case 三种语句。

generate-for语句

(1) 必须用 genvar 关键字定义 for 语句的变量。

(2)for 语句的内容必须加 begin 和 end (即使就一句)。

(3)for 语句必须有个名字,写在 begin 后面。

方法二:不例化

module top_module( 
    input [99:0] a, b,
    input cin,
    output reg [99:0] cout,
    output reg [99:0] sum );



    integer i;
    always @(*) begin
        cout[0] = (a[0] & b[0]) | (a[0] & cin) | (b[0] & cin);
        sum[0] = a[0] ^ b[0] ^ cin;
        for(i=1;i<100;i=i+1) begin
            sum[i] = a[i] ^ b[i] ^ cout[i-1];
            cout[i] = (a[i] & b[i]) | (a[i] & cout[i-1]) | (b[i] & cout[i-1]);
        
        end
    end

endmodule

注意:

  1. 两个 always 语句之间是并行的。

  2. 直接赋值会提示 always 里面只能赋值 reg 型,所以把 cout[0],sum[0] 也放进去,毕竟输入是不会变的,always 理论上只启动一次。

方法三:不用公式不例化

module top_module( 
    input [99:0] a, b,
    input cin,
    output reg [99:0] cout,
    output reg [99:0] sum );

    integer i;
    always @(*) begin
        {cout[0],sum[0]} = a[0] + b[0] + cin;
        for(i=1;i<100;i=i+1) begin
            {cout[i],sum[i]} = a[i] + b[i] + cout[i-1];
        end
    end
    
endmodule

Problem 42 Generate for-loop: 100-digit BCD adder

Requirement:

本题已经提供了一个名为 bcd_fadd 的 BCD 一位全加器,我们需要实例化 100 个 bcd_fadd 来实现 100 位的 BCD 进位加法器。该加法器应包含两个 100bit 的 BCD 码(400bit 的矢量)和一个 cin, 输出产生 sum 和 cout。

已知:

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

    wire [99:0] cout_100;
    bcd_fadd dcd_fadd_0(
        .a(a[3:0]),
        .b(b[3:0]),
        .cin(cin),
        .cout(cout_100[0]),
        .sum(sum[3:0])
    );

    generate
        genvar i;
        for(i=1;i<100;i=i+1) begin : bcc_fadd
            bcd_fadd dcd_fadd_i(
                .a(a[4*i+3:4*i]),
                .b(b[4*i+3:4*i]),
                .cin(cout_100[i-1]),
                .cout(cout_100[i]),
                .sum(sum[4*i+3:4*i])
            );
        end
    endgenerate

    assign cout = cout_100[99];

endmodule

注意:

  1. 先声明一个 wire 型的 cout_temp 来存放每次计算后 cout 的值。

  2. 注意向量的下标是从大到小的,所以切片时也要从大到小。

  3. 1 位 BCD 码对应 4 位二进制,但 100 位 BCD 码存储时还是以一串二进制形式存的,需要以四为单位存、取、用。

  4. 全加器之间的联系:上次的进位输出是这次的进位输入,除此以外,sum 各自算各自的。

方法二:仿照上一题来一个不用例化的,我的设想是:

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output reg [399:0] sum );

    reg [99:0] cout_100;
    integer i;
    always @(*) begin
        {cout_100[0],sum[3:0]} = a[3:0] + b[3:0] + cin;
        for(i=1;i<100;i=i+1) begin
            {cout_100[i],sum[4*i+3:4*i]} = a[4*i+3:4*i] + b[4*i+3:4*i] + cout_100[i-1];
        end
    end
    
endmodule

理想很丰满,现实是 error:

[VRFC 10-1775] range must be bounded by constant expressions

切片的上下限必须都是常数才行,唉。


做了一下午,效率好低。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值