HDLBits第五章
1、三元条件运算符
verilog 有一个三元条件运算符 ( ? : ) ,像 C语言一样。
(condition ? if_true : if_false)
这可用于根据一行上的条件(多路复用器!)选择两个值之一,而无需在组合 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 )
练习:
给定四个无符号数,找出最小值。无符号数可以与标准比较运算符 (a < b) 进行比较。使用条件运算符创建两路最小电路,然后组合其中的一些来创建 4 路最小电路。您可能需要一些用于中间结果的向量。
代码实现:
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);
wire [7:0]min1,min2;
assign min1 = (a > b)? b : a;
assign min2 = (c > d)? d : c;
assign min = (min1 > min2)? min2 : min1;
endmodule
验证结果:
2、归约运算符
你已经熟悉两个值之间的按位运算,例如a & b或a ^ b。有时,你想创建一个宽门,操作一个向量的所有位,如(a[0] & a[1] & a[2] & a[3]…),如果向量很长,就会变得很乏味。
归约运算符可以对向量的位进行“与”、“或”和“异或”运算,产生一位输出。
& 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]
这些是只有一个操作数的一元操作符(类似于NOT操作符!和~)。
你也可以将这些门的输出倒转来创建NAND、NOR和XNOR门,例如(~& d[7:0])。
练习:
奇偶校验经常被用作通过不完善的信道传输数据时检测错误的一种简单方法。创建一个电路,该电路将计算一个8位字节的奇偶校验位(将增加第9位字节)。我们将使用“偶数”奇偶校验,其中奇偶校验位就是所有8个数据位的异或。
代码实现:
module top_module (
input [7:0] in,
output parity);
assign parity = ^ in;
endmodule
验证结果:
3、归约:更宽的门
在[99:0]中建立一个有100个输入的组合电路。
有3个输出:
①out_and: 100输入与门的输出。
②out_or: 100输入OR门的输出。
③out_xor: 100输入异或门的输出。
代码实现:
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
验证结果:
4、组合for循环:向量反转2
给定一个 100 位的输入向量 [99:0],反转其位顺序。
代码实现:
module top_module(
input [99:0] in,
output [99:0] out
);
integer i;
always@(*) begin
for( i = 0 ;i <= 99;i = i + 1 ) begin
out[i] = in[99-i];
end
end
endmodule
验证结果:
5、组合for循环:255位总体计数
“总体计数”电路计算输入向量中“1”的个数。为255位输入向量构建总体计数电路。
代码实现:
module top_module(
input [254:0] in,
output [7:0] out );
integer i;
always@(*) begin
out = 8'b0;
for(i = 0;i <= 254;i = i + 1) begin
out = out + in[i];
end
end
endmodule
验证结果:
6、生成for循环:100位二进制加法器2
通过实例化 100 个全加器来创建一个 100 位二进制行波进位加法器。加法器将两个 100 位数字和一个进位相加,以产生 100 位总和并进位。为了鼓励您实际实例化全加器,还要输出行波进位加法器中每个全加器的进位。cout[99] 是最后一个全加器的最后一个进位,也是你经常看到的进位。
拓展:
generate语法
(1)定义genvar,作为generate种的循环变量。
(2)generate语句中定义的for语句,必须要有begin,为后续增加标签做准备。
begin必须要有名称,也就是必须要有标签,因为标签会作为generate循环的实例名称。
(3)可以使用在generate语句中的类型主要有:
① module(模块)
②UDP(用户自定义原语)
③ 门级原语
④ 连续赋值语句
⑤ initial或always语句
generate基本结构如下:
genvar 循环变量名;
generate
// generate循环语句
// generate 条件语句
// generate 分支语句
// 嵌套的generate语句
endgenerate
代码实现:
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
if(i==0)
begin
assign {cout[i],sum[i]} = a[i] + b[i] + cin;
end
else
begin
assign {cout[i],sum[i]} = a[i] + b[i] + cout[i-1];
end
end
endgenerate
endmodule
验证结果:
7、生成for循环: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]cout1;
genvar i;
generate
for( i = 0 ; i < 100; i = i + 1 )
begin:adder
if(i==0)
bcd_fadd U0( .a(a[3:0]), .b(b[3:0]), .cin(cin), .cout(cout1[0]), .sum(sum[3:0]));
else
bcd_fadd U1( .a(a[4*i+3:4*i]), .b(b[4*i+3:4*i]), .cin(cout1[i-1]), .cout(cout1[i]), .sum(sum[4*i+3:4*i]));
end
assign cout = cout1[99];
endgenerate
endmodule
验证结果: