HDLbits 1.5 学习笔记

条件运算符、约简运算符与 for 循环(More Verilog Features)

1.条件三元算子

Verilog 有一个三元条件算符(?:),类似于 C:

(condition ? if_true : if_false) (状况?if_true:if_false)

这可以用来根据条件(多工器!)在一行上选择两个值之一,而无需在组合始终块中使用if-then。

Examples: 示例:

(0 ? 3 : 5) // 这是5,因为条件为假。

(sel ? b : a) // 由SEL选择的A和B之间的2对1复用器。

复制代码
always @(posedge clk)         // 一个T型触发器
  q <= toggle ? ~q : q;
always @(*)                   // 单输入有限状态机的状态转换逻辑
  case (state)
    A: next = w ? B : A;
    B: next = w ? A : B;
  endcase
assign out = ena ? q : 1'bz;  // 三态缓冲区

((sel1:0 == 2'h0) ? a : // A 3-to-1 mux 3:1 多工器

(sel1:0 == 2'h1) ? b :c )

作业:给定四个无符号数字,求最小值。无符号数可以与标准比较算子(a < b)进行比较。使用条件算子制作双向 最小 电路,然后组合几个组成四路 最小 电路。你可能需要一些线矢量来做中间结果。

复制代码
module top_module (
    input [7:0] a, b, c, d,
output [7:0] min);
wire [7:0]out1 , out2 ;
assign out1 = ((a<b)? a: b) ;
assign out2 = ((c<d)? c: d) ;
assign out =((out1<out2)? out1: out2);//输出是min,你哪来的out,粗心
endmodule

module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min
);
    wire [7:0] out1, out2;
    assign out1 = (a < b) ? a : b;
    assign out2 = (c < d) ? c : d;
    assign min  = (out1 < out2) ? out1 : out2;
endmodule

还有其他方法:

方法 1:always @(*) + if-else 线性扫描

思路:先假设 a 最小,然后依次和 b、c、d 比,谁小就更新。

verilog

复制代码
module top_module (
    input [7:0] a, b, c, d,
    output reg [7:0] min
);
    always @(*) begin
        min = a;
        if (b < min) min = b;
        if (c < min) min = c;
        if (d < min) min = d;
    end
endmodule

注意:输出要用 output reg 7:0 min,因为你在 always 块里给它赋值。

这种写法很像软件里的"打擂台",电路上就是级联比较器。

方法 2:先两两比,再用 if-else

verilog

复制代码
module top_module (
    input [7:0] a, b, c, d,
    output reg [7:0] min
);
    reg [7:0] min_ab, min_cd;
    always @(*) begin
        if (a < b) min_ab = a;
        else       min_ab = b;
        if (c < d) min_cd = c;
        else       min_cd = d;
        if (min_ab < min_cd) min = min_ab;
        else                 min = min_cd;
    end
endmodule

这和你原来的三目运算符版本几乎一样,只是换成了 if-else。

方法 3:用 function 封装"两路最小"

先定义一个函数 min2,然后组合调用。

verilog

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

    function [7:0] min2;
        input [7:0] x, y;
        begin
            min2 = (x < y) ? x : y;
        end
    endfunction
    assign min = min2(min2(a, b), min2(c, d));

endmodule

这样代码更简洁,也体现了"先做两路最小,再组合"的思想。

方法 4:用数组 + for 循环

verilog

复制代码
module top_module (
    input [7:0] a, b, c, d,
    output reg [7:0] min
);
    reg [7:0] arr [0:3];
    integer i;
    always @(*) begin
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
        arr[3] = d;
        min = arr[0];
        for (i = 1; i < 4; i = i + 1) begin
            if (arr[i] < min)
                min = arr[i];
        end
    end
endmodule

这种写法扩展性很好,比如有 8 个数时,只改数组大小和循环上限即可。

方法 5:嵌套三目运算符一行流

verilog

复制代码
assign min = (a < b) ?
             ((a < c) ? ((a < d) ? a : d) : ((c < d) ? c : d)) :
             ((b < c) ? ((b < d) ? b : d) : ((c < d) ? c : d));

这种写法很紧凑,但可读性差,容易写错,不推荐实际工程中使用。

2.约简算符

你已经熟悉两个值之间的位运算,比如a & b或a ^ b。有时,你想创建一个宽门,作用于一个向量的所有位,比如 (a0 & a1 & a2 & a3 ... ),如果向量太长,这会变得很繁琐。约简算子可以对向量的位进行与、或和异或,产生一个比特输出:

& a3:0 // AND:a3&a2&a1&a0等价于 (a3:0 == 4'h0)

^ c2:0 // XOR: c2^c1^c0

& a3:0 // AND:a3&a2&a1&a0等价于 (a3:0 == 4'hf)

|b3:0 // 或:b3|b2|b1|b0等价于(b3:0 != 4'h0)

^ c2:0 // 异或:c2^c1^c0

这些是只有一个操作数的一元算符(类似于 NOT 的 ! 和 ~ 算符)。你也可以反转这些门的输出,生成NAND、NOR和XNOR门,例如(~& d7:0)。

现在你可以重新审视4输入门和100输入门。

作业:奇偶校验常被用作在通过不完美通道传输数据时检测错误的简单方法。创建一个电路,计算一个8位字节的奇偶校验位(该字节会增加第9位)。我们将使用"偶"奇偶校验,其中奇偶校验位就是所有8个数据位的异或。

复制代码
module top_module (
    input [7:0] in,
output parity);
assign parity = ^ in[7:0];
endmodule
复制代码
module top_module (
    input [7:0] in,
    output parity
);
    assign parity = ^in;
endmodule

3.门槛更宽

构建一个包含100个输入的组合电路,在99:0中。

共有3个输出:

out_and: output of a 100-input AND gate.

out_and:输出一个100输入的AND门。

out_or: output of a 100-input OR gate.

out_or:100输入或门的输出。

out_xor: output of a 100-input XOR gate.

out_xor:输出一个100输入的异或门。

复制代码
module top_module(
    input [99:0] in,
    output out_and,
    output out_or,
    output out_xor
);
assign out_and = & in[99:0] ;
assign out_or = |in[99:0] ;
assign out_xor = ^ in[99:0] ;
endmodule

4.for-loop(矢量反转)

给定一个100位输入矢量99:0,反转其位序。

复制代码
module top_module(
    input [99:0] in,
    output [99:0] out
);
integer j ;
always@* begin
    for (j=0; j<=99;j=j+1) begin
        out[j] = in[(99-j];
    end
end

正确写法二:用 generate + assign(端口保持 wire)

verilog

复制代码
module top_module(
    input [99:0] in,
    output [99:0] out
);
    genvar j;
    generate
        for (j = 0; j <= 99; j = j + 1) begin : reverse_loop
            assign out[j] = in[99 - j];
        end
    endgenerate
endmodule

这种写法不需要改 output 类型,因为 assign 给 wire 赋值。

5.for-loop(255位人口计数)

"人口计数"电路用于统计输入向量中"1"的数量。为255位输入向量构建一个人口计数电路。

复制代码
module top_module(
    input [254:0] in,
output [7:0] out );
integer j ;
always@* begin
    	if (in[i] == 1'b1) begin
        	j+ = 1;//疏忽了,还要对向量进行一个遍历操作才能进行比对
    	end
end

module top_module(
    input [254:0] in,
    output [7:0] out
);
    integer i;
    integer j;
    always @(*) begin
        j = 0; // 1. 初始化计数器
        for (i = 0; i < 255; i = i + 1) begin // 2. 遍历255位
            if (in[i] == 1'b1) begin // 3. 括号里判断当前位是否为1
                j = j + 1; // 4. 修正语法
            end
        end
    end
    assign out = j[7:0]; // 5. 将结果赋给输出端口
endmodule

6.for环路(100位二进制加法器)

通过实例化100个完整加法器,创建一个100位二进制波纹进位加法器。(你还需要写完整的加法器模块,可能需要重复使用你的解法来 fadd。)加法器会添加两个100位数字和一个进位输入,生成100位的求和和。为了鼓励你实际实例化完整加法器,也可以在波纹进位加法器中输出每个满加器的进位输出。Cout99 是上一个完整加法器的最后一个进位,也是你通常看到的进位。

"上一位的进位输出,连着下一位的进位输入"的结构,就叫波纹进位。

复制代码
module fadd(
    input a, b, cin,
    output sum, cout
);
    assign sum = a ^ b ^ cin;
    assign cout = count<3?0:1;
endmodule
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 : adder_loop
            // 提示:这里定义一个 wire 作为当前的进位输入
            wire current_cin = 1;
            fadd u_fadd (
                .a([99 : 0]),
                .b([99 : 0]),
                .cin([99 : 0]),
                .sum([99 : 0]),
                .cout([99 : 0])
            );
        end
    endgenerate
endmodule



module fadd(
    input a, b, cin,
    output sum, cout
);
    assign sum = a ^ b ^ cin;
    assign cout =  (a & b) | (a & cin) | (b & cin); //进位的逻辑
endmodule
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 : adder_loop
            wire current_cin = (i == 0) ? cin : cout[i-1]; //波纹进位的特点
            fadd u_fadd (
                .a(a[i]),       // 改成 a[i]
                .b(b[i]),       // 改成 b[i]
                .cin(current_cin), // 用上面定义的 wire
                .sum(sum[i]),   // 改成 sum[i]
                .cout(cout[i])  // 改成 cout[i]
            );
        end
    endgenerate
endmodule

7.for-loop环路(100位bcd加法器)

你会得到一个名为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位的求和并执行。

复制代码
module top_module(
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum
);
    wire [99:0] carry; // 内部的100位进位传送带
    genvar i;
    generate
        for (i = 0; i < 100; i = i + 1) begin : adder_loop
            // 选择当前级的进位输入
            wire current_cin = (i == 0) ? cin : carry[i-1];

            bcd_fadd u_bcd_fadd (
                .a(a[4*i+3 : 4*i]),
                .b(b[4*i+3 : 4*i]),
                .cin(current_cin),
                .sum(sum[4*i+3 : 4*i]),
                .cout(carry[i])
            );
        end
    endgenerate
    // 将最高位的进位输出到顶层
    assign cout = carry[99];
endmodule
相关推荐
幻智星科技2 小时前
FPGA不同品牌开发平台约束文件迁移难题解析
fpga开发·架构·硬件架构
幻智星科技4 小时前
Karatsuba与CSD优化FPGA大数乘法
c++·fpga开发·密码学
星夜离尘565 小时前
FPGA 实战:W1C 中断状态寄存器,事件置位与软件清除撞在同一拍怎么办
fpga开发
像枫一样6 小时前
STM32串口轮询收发实战教程
arm开发·c++·stm32·单片机·嵌入式硬件·fpga开发·51单片机
传感器与混合集成电路6 小时前
DLM8676计数控制集成电路DC-DC一体化与RS422隔离接口设计
fpga开发
嵌入式软硬件攻城狮21 小时前
9.读写DDR测试
fpga开发
幻智星科技1 天前
多通道数据采集DMA设计详解
fpga开发·udp·硬件架构
泡泡糖的中文规格书1 天前
HDLbits 1.4 学习笔记
fpga开发
智感子2 天前
FPGA触发采集:预触发、多通道同步与软硬件分工怎么落地
fpga开发