条件运算符、约简运算符与 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
