HDLBits_Verilog学习笔记Ⅰ——Verilog Language_More Verilog Features
信哥稳住
编辑于 2023年11月07日 19:17
收录于文集
共2篇

本专栏集是基于HDLBits上的Verilog练习题的分析与总结的,直接从本小节开始是因为已经有人完成了前面章节的笔记:

脱发秘籍搬运工 https://www.bilibili.com/read/cv16806561/​ 出处:bilibili

本文是继承与上述作者写的剩余章节的笔记。前面的笔记请看原作者。


36. Conditional ternary operator

Verilog 有一个三元运算符(?:),很像 C 语言中的运算符:

它可以在一行上根据条件选择两个值之一,而不需要在组合 always 块内部使用 if-then 语句。

示例:

代码块
clike
自动换行
复制代码
(0 ? 3 : 5) // 这是 5,因为条件为假。
(sel ? b : a) // sel 选择 a 和 b 之间的 2-to-1 多路复用器。

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

always @(*) // 单输入 FSM 的状态转换逻辑
case (state)
A: next = w ? B : A;
B: next = w ? A : B;
endcase

assign out = ena ? q : 1'bz; // 三态缓冲器
((sel[1:0] == 2'h0) ? a : // 3-to-1 mux
(sel[1:0] == 2'h1) ? b:c )
复制成功

练习 给定四个无符号数,找出最小值。无符号数可以使用标准比较运算符(a < b)进行比较。使用条件运算符创建双向最小值电路,然后组合几个这样的电路来创建一个 4 路最小值电路。你可能需要为中间结果使用一些电线向量。

预期解决方案长度:大约 5 行。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module (
   input [7:0] a, b, c, d,
   output [7:0] min);//
   wire [7:0] minab = a < b? a:b;
   wire [7:0] mincd = c < d? c:d;
   assign min = minab < mincd ? minab: mincd;
endmodule
复制成功

BB几句:本人的水平有限,比不上原作者。这里简单说一些心得,有补充可以补充。本题找最小值用三目运算符是比较简单的,当然用if else也可实现。


37. Reduction operators

你已经熟悉了两个值之间的位运算,例如 a&b 或 a^b。有时候,你想要创建一个对一个向量的所有位进行操作的宽门,例如(a[0]&a[1]&a[2]&a[3]……),如果向量很长,这会变得很繁琐。 减少运算符可以对向量的位进行 AND、OR 和 XOR 运算,产生一个位的输出:

代码块
clike
自动换行
复制代码
&a[3:0] // AND:a[3]&a[2]&a[1]&a[0]。等效于(a[3:0] == 4'hf)
|b[3:0] // OR:b[3]|b[2]|b[1]|b[0]。等效于(b[3:0] != 4'h0)
^c[2:0] // XOR:c[2]^c[1]^c[0]
复制成功

这些是一元运算符,只有一个操作数(类似于 NOT 运算符!和~)。你也可以将这些运算符的输出取反,创建 NAND、NOR 和 XNOR 门,例如(~&d[7:0])。 现在你可以重新考虑 4 输入门和 100 输入门了。

练习 奇偶校验常被用作通过不完美信道传输数据时检测错误的一种简单方法。创建一个电路,该电路将为 8 位字节计算奇偶校验位(这将在字节中添加第 9 位)。我们将使用“偶数”奇偶校验,其中奇偶校验位只是所有 8 个数据位的异或。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module (
   input [7:0] in,
   output parity);
   assign parity = ^in[7:0];
endmodule
复制成功

BB几句:确实比较向量中所有元素位运算时,这样写很方便。


38. Reduction: Even wider gates

这一次是对于100个元素的向量,方法一样。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
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
复制成功

BB几句:不了。


39. Combination for-loop: Vector reversal 2

给定一个100位向量的输入,将它翻转。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module(
   input [99:0] in,
   output [99:0] out
);
  always @(*) begin
       for (int i = 0; i < 100; i++) begin
           out[i] = in[100-i-1];
       end
   end
endmodule
复制成功

BB几句:翻转向量,最直接的用for循环。或许还有什么库函数可以直接使用。


40. Combination for-loop: 255-bit population count

“人口计数”电路计算输入向量中'1&#​39;的数量。构建一个用于 255 位输入向量的人口计数电路。本题就是计算向量中1的数量。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module(
   input [254:0] in,
   output [7:0] out );
   always@(*) begin
      out = 8'b0000_0000;
       for(int i=0;i<=254;i++)
             begin
                   if (in[i] == 1'b1)
                    out=out + 8'b1;
             end
    end
endmodule
复制成功

BB几句:没想到更好的办法,还是for循环遍历一遍去加,计算1的个数


41. Generate for-loop: 100-bit binary adder2

通过实例化 100 个全加器创建一个 100 位二进制连锁进位加法器。该加法器将两个 100 位数字和一个进位输入相加,生成一个 100 位和和进位输出。为了鼓励你实际实例化全加器,连锁进位加法器中的每个全加器的进位输出也会输出。cout[99]是最后一个全加器的最终进位输出,也是你通常看到的进位输出。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module(
   input [99:0] a, b,
   input cin,
   output [99:0] cout,
   output [99:0] sum );

   assign sum[0] = a[0]^b[0]^cin;
   assign cout[0] = a[0]&b[0] | a[0]&cin | b[0]&cin;
   always@(*) begin
    for(int i=1;i <=99;i++)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
复制成功

BB几句:在前面的25题,就进行过32位加法器的实例化,但是当时只写了add1的内部代码,如何串成add16的过程并没有写。实际上还是可以用for循环来串。


42. Generate for-loop: 100-bit BCD adder

已经提供了你一个名为 bcd_fadd 的 BCD 一位加法器,它可以将两个 BCD 数字和进位输入相加,并产生和以及进位输出。

代码块
clike
自动换行
复制代码
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 位的和以及进位输出。

答案(先做再看哦,且不唯一,仅供参考):

代码块
clike
自动换行
复制代码
module top_module(
   input [399:0] a, b,
   input cin,
   output cout,
   output [399:0] sum );
   
   wire [99:0] temp;
   bcd_fadd bcd_1 (a[3:0],b[3:0],cin,temp[0],sum[3:0]);
   assign cout = temp[99];
   
   generate
       genvar i;
       for (i=4;i<=399;i=i+4)
           begin : bcdadd
               bcd_fadd bcd_2 (a[i+3:i],b[i+3:i],temp[(i-4)/4],temp[i/4],sum[i+3:i]);
           end
   endgenerate

endmodule
复制成功

BB几句:这道题与上一题一样,也要先利用cin来初始化第一位的进位,但是还有一些要注意的点:

1.进位输出cout需要一个变量来,不妨假设一个temp当做中间变量来充当cout的中间值,整个过程我们将进行100次(0、4、8、…、396这99个数字); 2.使用模块进行实例化,就要用到generate这个函数的问题。 什么是generate语句?

生成语句可以动态的生成verilog代码,当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,或者根据参数的定义来确定程序中是否应该包含某段Verilog代码的时候,使用生成语句能大大简化程序的编写过程。

使用关键字generate 与 endgenerate来指定范围。generate语句有generate-for、generate-if、generate-case三种语句,本题中我们使用generate-for语句。generate-for语句: (1) 必须有genvar关键字定义for语句的变量。 (2)for语句的内容必须加begin和end(即使就一句)。 (3)for语句必须有个名字。