• HDLBits: 在线学习 SystemVerilog(三)-Problem 10-14


    HDLBits: 在线学习 SystemVerilog(三)-Problem 10-14

    b6ff41d58173b0a5932a17e2ceb48184.jpeg

    HDLBits 是一组小型电路设计习题集,使用 Verilog/SystemVerilog 硬件描述语言 (HDL) 练习数字硬件设计~

    网址如下:

    https://hdlbits.01xz.net/

    关于HDLBits的Verilog实现可以查看下面专栏:

    https://www.zhihu.com/column/c_1131528588117385216

    缩略词索引:

    • SV:SystemVerilog

    向量

    从这个题目到后面八道题目左右是关于向量的,所以我们先回顾一下向量。

    SystemVerilog有两种类型的数组:压缩数组和非压缩数组。压缩数组是连续存储的位的集合,通常称为向量。非压缩数组是网络或变量的集合。

    集合中的每个网络或变量称为数组元素。未压缩数组的每个元素的类型、数据类型和向量大小都完全相同。每个未压缩的数组元素可以独立于其他元素存储;这些元素不需要连续存储。软件工具,如仿真器和综合编译器,可以以工具认为最佳的任何形式组织未压缩数组的存储。

    未压缩数组的基本声明语法为:

    ed78a8508f8423222f6a504062968d7a.png

    数组的维度定义了数组可以存储的元素总数。未压缩的数组可以用任意数量的维度声明,每个维度存储指定数量的元素。声明数组维度有两种编码样式:显式地址和数组大小。

    显式地址样式指定方括号之间数组维度的起始地址和结束地址,格式为:

    c2d0c262da68b4939b09dd6cc4cb54e2.png

    Start_address 和 end_address可以是任何整数值,数组可以以地址0、地址512或被建模硬件所需的任何地址开始。起始地址和结束地址之间的范围表示数组维度的大小(元素数)。

    数组大小样式定义要存储在方括号中的元素数(类似于C语言数组声明样式)。

    [size]

    使用array_size样式,起始地址始终为0,结束地址始终为size-1

    以下是一些未压缩的数组声明示例:

    53e9b0102bbf3926019a26509d3b9c25.png

    前面的mem声明是16位logic变量的一维数组。一维阵列有时被称为内存阵列,因为它通常用于仿真硬件内存设备(如RAM和ROM)的存储。

    访问数组元素

    可以使用数组索引引用未压缩数组的每个元素,索引紧跟在数组名称之后,并且位于方括号中,多维数组需要多组方括号才能从数组中选择单个元素:

    4ddcf5c7fccc68dd715f7ee5c603018b.png

    数组索引也可以是网络或变量的值,如下一个示例所示:

    9e21cec79c9ac193c08c7ee5afd1119e.png

    复制数组(阵列)

    如果两个数组(阵列)具有相同的布局,则可以使用赋值语句将一个未压缩的数组(阵列)复制到另一个未压缩的数组(阵列)。也就是说,这两个数组(阵列)必须存储相同向量大小的相同数据类型,必须具有相同的维度数,并且每个维度的大小都相同-

    数组(阵列)复制会将源数组(赋值的右侧)的每个元素复制到目标数组(阵列)(赋值的左侧)中相应的元素。两个数组(阵列)的索引编号不需要相同。数组(阵列)的布局和类型必须完全匹配。

    17f73af4ca1dd23c8b745a2d45c8f1e8.png

    与复制数组(阵列)的方式类似,如果两个切片的布局相同,则可以将数组(阵列)的一部分(称为数组(阵列)切片)复制到另一个数组(阵列)的切片。切片是数组(阵列)一维内的一个或多个连续编号的元素。

    在成为SystemVerilog之前,最初的Verilog语言将对数组(阵列)的访问限制为一次只能访问数组中的一个元素。不允许对数组(阵列)的多个元素进行数组(阵列)复制和读/写操作。

    数组列表赋值

    可以为未压缩的数组或数组的一个片段分配一个值列表,这些值包含在每个数组维度的’{and}大括号之间。

    ea62ef628cc8f385fa7d87521eff935c.png

    列表语法类似于在C中为数组指定值列表,但在大括号前添加了撇号使用’-“作为开头分隔符”表明,所包含的值是表达式列表,而不是SystemVerilog连接运算符(后面会详细介绍)。

    还可以使用嵌套列表为多维数组分配值列表。嵌套的列表集必须与数组的维度完全匹配。

    78da2c0b6d79d9d9ffdd51546f9b1160.png

    此数组分配相当于以下各项的单独分配:

    54b353a6a540bf3ef7a8feebb060df5c.png

    通过指定默认值,可以为未压缩数组的所有元素指定相同的值。默认值是使用

    e346c4ebe283de9dd30283a8b5d93c85.png

    指定的,如以下代码段所示:

    13da73e8a4605f57916b164539d05962.png

    数组元素的位选择和部分选择

    可以从数组元素向量中选择一位或一组位。必须首先选择数组的单个元素,然后进行位选择或部分选择。

    ea26d35808f6c86f442883bef977ddc7.png

    通过端口将数组传递给任务和函数。任何类型和任意数量的未压缩数组都可以通过模块端口传递,也可以传递到任务和函数参数。端口或任务/函数形式参数也必须声明为数组,端口或参数数组必须与要传递的数组具有相同的布局(与数组复制的规则相同)。

    48e18cf8e1e4793d8ad3a4ba0b4d6933.png

    最初的Verilog语言只允许简单的向量通过模块端口,或传递到任务或函数参数。要传递上述示例中表数组的值,需要256个端口,数组的每个元素一个端口。

    Problem 10-Vector0

    题目说明

    构造一个电路,拥有 1 个 3 bit 位宽的输入端口,4 个输出端口。其中一个输出端口直接输出输入的向量,剩下 3 个输出端口分别各自输出 3 bit 中的 1 bit。

    eda5c1b087bbe909d2ac590829198621.png图片来自 HDLBits

    这个题目的核心就是上面的图片,模块和端口已经被定义好了,黑色的框图以及箭头代表模块和端口。我们需要做的工作是完成图中绿色的部分,其中箭头上的小斜杠旁边的数字代表该向量(总线)的位宽,用于将向量同 wire 信号区别开来。

    模块端口声明

    1. module top_module ( 
    2.     input wire [2:0] vec,
    3.     output wire [2:0] outv,
    4.     output wire o2,
    5.     output wire o1,
    6.     output wire o0  );

    题目解析

    这个题目重点是向量的选取和赋值,在文章最前面已经介绍过了,大家仔细看过应该很好理解。

    1. module top_module ( 
    2.     input  logic [2:0] vec,
    3.     output logic [2:0] outv,
    4.     output logic  o2,
    5.     output logic  o1,
    6.     output logic  o0  
    7. ); 
    8.     assign outv = vec;
    9.     assign o0   = vec[0];
    10.     assign o1   = vec[1];
    11.     assign o2   = vec[2];
    12.     
    13. endmodule
    4b2f4c0aa8773138b14b9c90d9bfa5a3.png

    点击Submit,等待一会就能看到下图结果:

    c4db877d081f9794df622b9219f934a0.png

    注意图中的Ref是参考波形,Yours是你的代码生成的波形,网站会对比这两个波形,一旦这两者不匹配,仿真结果会变红(后面会展示)。

    这一题就结束了。

    Problem 11-Vector1

    题目说明

    16 位输入信号分别输出 高 8 位 和低 8 位。

    模块端口声明

    1. `default_nettype none     // Disable implicit nets. Reduces some types of bugs.
    2. module top_module( 
    3.     input wire [15:0] in,
    4.     output wire [7:0] out_hi,
    5.     output wire [7:0] out_lo );
    通过添加 `default_nettype none 宏定义会关闭隐式声明功能,那么这样一来,使用未声明的变量就会变成一个 Error 而不再只是 Warning。

    题目解析

    这道题难度不大主要是向量的选择,但是要掌握的知识很多,这题前面由很多这方面的知识点,包括压缩数组和非压缩数组,隐式转换等,建议大家在做题前仔细阅读题目前的知识点。

    也可以查看下面的文章:

    https://zhuanlan.zhihu.com/p/57452966

    1. `default_nettype none     // Disable implicit nets. Reduces some types of bugs.
    2. module top_module( 
    3.     input wire logic [15:0] in,
    4.     output var logic [7:0] out_hi,
    5.     output var logic [7:0] out_lo );
    6.     assign out_hi = in[15:8] ;
    7.     assign out_lo = in[7:0] ;
    8. endmodule
    913ef9e647f8cee4c54b3613c828528d.png

    点击Submit,等待一会就能看到下图结果:

    4637562dea4f3ad2e8b276324166605d.png

    注意图中的Ref是参考波形,Yours是你的代码生成的波形,网站会对比这两个波形,一旦这两者不匹配,仿真结果会变红(后面会展示)。

    这一题就结束了。

    Problem 12-Vector2

    题目说明

    一个 32 位的向量可以看做由 4 个字节组成(bits[31:24],[23:16],等等)。构建一个电路,将输入向量的字节顺序颠倒,也就是字节序大小端转换。

    如:

    AaaaaaaaBbbbbbbbCcccccccDddddddd => DdddddddCcccccccBbbbbbbbAaaaaaaa

    模块端口声明

    1. module top_module( 
    2.     input [31:0] in,
    3.     output [31:0] out );

    题目解析

    还是在联系向量的提取和赋值。

    1. module top_module( 
    2.     input  logic [31:0] in,
    3.     output logic[31:0] out 
    4. );
    5.     assign out[31:24] = in[7:0] ;
    6.     assign out[23:16] = in[15:8] ;
    7.     assign out[15:8] = in[23:16] ;
    8.     assign out[7:0] = in[31:24] ;
    9. endmodule
    01e8ef058a5330198aa93ef5635b2a1c.png

    点击Submit,等待一会就能看到下图结果:

    97be4fc15c4ca444a23b5e1f869e0eb9.png

    注意图中的Ref是参考波形,Yours是你的代码生成的波形,网站会对比这两个波形,一旦这两者不匹配,仿真结果会变红(后面会展示)。

    这一题就结束了。

    PS:下面的答案也是对的

    assign out = {in[7-:8],in[15-:8],in[23-:8],in[31-:8]};

    可以自行查找知识学习上面的写法。

    Problem 13-Vectorgates

    题目说明

    a,b是两个 3bit 宽的输入变量,要求输出 a,b 逐位或,a,b 逻辑或以及 a,b 按位取反的结果,其中 b 在高位。

    6245bf59b200fd3c2ae959a553d73899.png图片来自 HDLBits

    问题的核心就是上面的图片,模块和端口已经被定义好了,黑色的框图以及箭头代表模块和端口。我们需要做的工作是完成图中绿色的部分。

    模块端口声明

    1. module top_module( 
    2.     input [2:0] a,
    3.     input [2:0] b,
    4.     output [2:0] out_or_bitwise,
    5.     output out_or_logical,
    6.     output [5:0] out_not
    7. );

    题目解析

    这个题目还是挺绕的,难度不是很大,理解题目就能写出来,这里除了上一个题目的向量提取和赋值以外,还由一个知识点-逻辑求反运算符(!)和按位反转运算符(~)之间的区别~

    之前简单介绍了以下,这次就详细介绍,后续就不展开说了。

    应注意不要混淆逻辑求反运算符(!)以及按位反转运算符(~)。求反运算符对其操作数执行真/假求值,并返回表示真、假或未知结果的1位值。按位反转运算符对操作数的每一位(补码)执行逻辑反转,并返回与操作数相同位宽的值。

    在某些操作中,这些操作的结果恰好相同,但在其他操作中,它们返回的值非常不同。当运算符与决策语句一起被错误使用时,这种差异可能导致错误代码。考虑下面的例子:

    3b5008bd7ab1f027f5b468b5c5650941.png

    前面代码片段的最后两行之所以不同,是因为这两个运算符的工作方式不同——逻辑求反运算符(!)通过将两位相加或相减,对2位选择执行真/假计算,然后反转1位结果,按位反转运算符(~)只反转2位选择向量的每一位的值,并返回2位结果。if语句然后对2位向量进行真/假测试,该向量的计算结果为真,因为反转后的值仍有一位设置为1。

    最佳实践指南5-1
    使用按位反转运算符反转值的位,不要使用按位反转运算符对逻辑求反运算符求反。相反,使用逻辑求反运算符来否定真/假测试的结果。不要使用逻辑求反运算符反转值。
    最佳实践指南5-2
    仅使用逻辑求反运算符求反来测试标量(1位)值,而不是 对向量执行真/假测试。

    如果向量的任何位为1,逻辑运算将返回true,这可能会导致在测试特定位时出现设计错误。计算向量值时,使用等式或关系运算符测试可接受的值。

    1. module top_module( 
    2.     input logic [2:0] a,
    3.     input logic [2:0] b,
    4.     output logic [2:0] out_or_bitwise,
    5.     output logic out_or_logical,
    6.     output logic [5:0] out_not
    7. );
    8.  assign out_or_bitwise = a | b ;
    9.     assign out_or_logical = a || b;
    10.     assign out_not = {~b , ~a}; 
    11. endmodule
    519b78dcac2e29d2059709e2d1394aa2.png

    点击Submit,等待一会就能看到下图结果:

    49c719fb102c86cd2f6a8b22a3f13473.png

    注意图中的Ref是参考波形,Yours是你的代码生成的波形,网站会对比这两个波形,一旦这两者不匹配,仿真结果会变红(后面会展示)。

    这一题就结束了。

    Problem 14-Gates4

    题目说明

    分别构建一个 4 输入与门,或门以及异或门。

    模块端口声明

    1. module top_module( 
    2.     input [3:0] in,
    3.     output out_and,
    4.     output out_or,
    5.     output out_xor
    6. );

    题目解析

    这一题的关键是多输入与、或以及异或门的实现,学过数电的化难度就不是很大了。

    768c11e45dca7c0bc4912b008bafe74e.png
    1. module top_module( 
    2.     input  logic [3:0] in,
    3.     output logic out_and,
    4.     output logic out_or,
    5.     output logic out_xor
    6. );
    7.     assign out_and = &in ;
    8.     assign out_or = |in ;
    9.     assign out_xor = ^in ;
    10. endmodule

    或者

    1. module top_module( 
    2.     input  logic [3:0] in,
    3.     output logic out_and,
    4.     output logic out_or,
    5.     output logic out_xor
    6. );
    7.     assign out_and = in[0] & in[1] & in[2] & in[3] ;
    8.     assign out_or = in[0] | in[1] | in[2] | in[3] ;
    9.     assign out_xor = in[0] ^ in[1] ^ in[2] ^ in[3] ; ;
    10. endmodule

    第二种方式更易于理解,第一种方式就是上面的按位操作符,可以翻看上面的说明。

    5e1a3f4ee19240e8793e885e9fa7cb5a.png e1cec33b75b8b664bfb3bf53f4df7a0b.png

    点击Submit,等待一会就能看到下图结果:

    45da641503fa48238220f3214c210a17.png

    注意图中的Ref是参考波形,Yours是你的代码生成的波形,网站会对比这两个波形,一旦这两者不匹配,仿真结果会变红。

    总结

    今天的5道题就结束了,整体难度不大,后面的题目难度会越来越大~

    最后我这边做题的代码也是个人理解使用,有错误欢迎大家批评指正,祝大家学习愉快~

    代码链接:

    https://github.com/suisuisi/SystemVerilog/tree/main/SystemVerilogHDLBits

    往期推荐

    HDLBits: 在线学习 SystemVerilog(二)-Problem 7-9

    YYDS!牛客还是强呀~

  • 相关阅读:
    vue3的api解读-VUE Reactivity
    问题排查利器:Linux 原生跟踪工具 Ftrace 必知必会
    正则验证用户名和跨域postmessage
    比肩Midjourney-v6!没有GPU也能跑可图Kolors
    uni-app 分包
    第五章 创建、查看和编辑文本
    【Bluetooth|蓝牙开发】二、蓝牙开发入门
    【数据结构篇】堆
    分析key原理
    原型模式--深复制/浅复制
  • 原文地址:https://blog.csdn.net/Pieces_thinking/article/details/126516097