HDBits刷题1: Verilog Language

HDBits刷题1: Verilog Language本文介绍了 Verilog 的基本概念 包括门电路如或非门 同或门的使用 以及矢量的操作如位宽声明 按位操作和向量串联

大家好,欢迎来到IT知识分享网。

1.basics(基础)

5).或非门

nor的用法,不是out = nor(a,b), nor是一个定义好gate元件,通过例化使用.

HDBits刷题1: Verilog Language

6).同或门:

可写作xnor, 异或门为xor:

HDBits刷题1: Verilog Language

8).7458chip
HDBits刷题1: Verilog Language

2.vectors(矢量)

1).vectors矢量

多位信号的assign表明位数:outv[2:0] = vec[2:0], 也可以是outv = vec.

HDBits刷题1: Verilog Language

2)vectors in more detail 一些注意点

i). declaring vectors:

wire [0:7] a 和 wire [7:0] a的区别是前者最高位为a[0],后者为a[7],两种写法都可以,但同一个设计中最好用相同的形式, 后者常用.

HDBits刷题1: Verilog Language

ii). 隐含的网络带来的错误:

未声明宽度的时候默认位宽为1,下图中a到b的连接截取了最低位,b到c的连接中位宽不匹配,则报错:`default_nettype none

HDBits刷题1: Verilog Language

iii). vector array 向量阵列.

下图为255个位宽为8bit的寄存器阵列和29个位宽为1bit的寄存器阵列

HDBits刷题1: Verilog Language

4)bitwise operators:

& | ~ 为按位操作, && || !为逻辑操作:如3’b101:~结果是3’b010, !的结果是1’b0.

HDBits刷题1: Verilog Language

5)four input gates
HDBits刷题1: Verilog Language

6)vector concatenation(串联) operator 连接操作, 示例如下:
HDBits刷题1: Verilog Language

HDBits刷题1: Verilog Language

7)vector reversal 1 矢量逆序
HDBits刷题1: Verilog Language

是否可以assign out[7:0] = in[0:7] ?? 答案是不可以: verilog不允许这样操作.

HDBits刷题1: Verilog Language

正解如下:(或者逐位拼接也可以, 但很麻烦). 注意:for循环仅在always和initial块中使用, 直接定义for循环会报错.

HDBits刷题1: Verilog Language

8)replication operator 复制操作

vector复制示例:

HDBits刷题1: Verilog Language

常见的复制实例: 符号位扩展

sign extending:

4’b0101 –> 8’b0000_0101 (8’d5)

4’b1101 –> 8’b1111_1101 (-8’d3)

HDBits刷题1: Verilog Language

9)more replication 其他复制的情况

注意:{5{a}, 5{b}, … }这种拼接方式是错误的, 5个a连接应该表示为{5{a}}.

HDBits刷题1: Verilog Language

解答如下:

HDBits刷题1: Verilog Language

3.modules: hierarchy(模块:层次化)

1)modules 例化一个模块
HDBits刷题1: Verilog Language

2)例化模块(by pos)
HDBits刷题1: Verilog Language

3)例化模块(by name)
HDBits刷题1: Verilog Language

4)three module(3个D触发器模块例化连接)
HDBits刷题1: Verilog Language

5)modules and vectors

下面模块的作用是选择不同延迟周期数(从0到3个周期), dff用于延迟, 选择器用来选择.

HDBits刷题1: Verilog Language

解答:

module top_module ( input clk, input [7:0] d, input [1:0] sel, output [7:0] q ); //wire between 3 D-ff wire d1_d2[7:0]; wire d2_d3[7:0]; //wire to selector, delay 0/1/2/3 cycle wire [7:0] dly_0; wire [7:0] dly_1; wire [7:0] dly_2; wire [7:0] dly_3; assign dly_0 = d; my_dff8 u1_my_diff8( .clk(clk), .d(d), .q(dly_1) ); my_dff8 u2_my_diff8( .clk(clk), .d(dly_1), .q(dly_2) ); my_dff8 u3_my_diff8( .clk(clk), .d(dly_2), .q(dly_3) ); always @(*)//一开始还加了{}, 都忘了用begin end了, 长点记性 begin case(sel)// no " : " 2'b00: q = dly_0; 2'b01: q = dly_1; 2'b10: q = dly_2; 2'b11: q = dly_3; default: q = 2'b0; endcase end endmodule
6)adder1

用两个16bit 加法器做一个32bit加法器, 低位进位:0, 最高位进位不考虑

HDBits刷题1: Verilog Language

module top_module( input [31:0] a, input [31:0] b, output [31:0] sum ); wire carry; //1 bit /* 一开始把[12:0]放在 add_out后面了,结果报错说例化的时候不能把array type赋值给非array type, 才发现的.. 长长记性 */ wire [15:0] add_out1; wire [15:0] add_out2; add16 u1_add(.a(a[15:0]), .b(b[15:0]), .cin(1'b0), .sum(add_out1), .cout(carry));//cin = 0 add16 u2_add(.a(a[31:16]), .b(b[31:16]), .cin(carry), .sum(add_out2), .cout());// cout ignored assign sum[31:0] = {add_out2, add_out1}; endmodule
7)adder2

这英文题目说啥没看懂, 看了别人的中文帖子懂了: 就是需要自己写一个1位的全加器模块, 上题说到的add16是这个add1的上层模块. add16不用自己写, 但可以在top_module中直接用(还是有些绕).算是上一题的补充.

module add1 ( input a, input b, input cin, output sum, output cout ); /* //assign cout = (a^b)&cin + a&b 本来是用的上面这个逻辑表达式, 仿真不通过, 最后发现是或( | 写成了 + ), 不愧是我 */ assign cout = a&b | a&cin | b&cin; assign sum = a^b^cin; endmodule
8)carry selected adder

因为纹波进位加法器(前面那种), 高位的运算需要等待低位的进位结果出来后才能开始进行. 在计算的位数过多时延迟会很大. 本题的进位选择加法器可以减少延迟. 3delay 减少到 1delay.

HDBits刷题1: Verilog Language

module top_module( input [31:0] a, input [31:0] b, output [31:0] sum ); wire [15:0] sum1; wire [15:0] sum2; wire sel; add16 u0_add(a[15:0], b[15:0], 1'b0, sum[15:0], sel); add16 u1_add(a[31:16], b[31:16], 1'b0, sum1, ); add16 u2_add(a[31:16], b[31:16], 1'b1, sum2, ); assign sum[31:16] = sel ? sum2 : sum1; endmodule

9 )adder-subtractor:加法减法器
HDBits刷题1: Verilog Language

通过一个输入的控制信号来决定是让两个数进行加法还是减法.

减法的原理是,把被减数b转换成补码形式, 即对b按位取反再+1. 所加的1在最低的cin处.

按位取反: 将b与1按位异或即可, 如10101与1按位异或得到01010

故要进行加法则sub = 0(不转补码), 进行减法则sub = 1(转补码)

module top_module( input [31:0] a, input [31:0] b, input sub, output [31:0] sum ); wire [31:0] rvs_b; wire carry; assign rvs_b = b^{32{sub}}; add16 u1_add(a[15:0], rvs_b[15:0], sub, sum[15:0], carry); add16 u2_add(a[31:16], rvs_b[31:16], carry, sum[31:16], ); endmodule

4.procedures(进程)

1)always块:组合逻辑

assign赋值的左侧必须是网络类型(如wire), 过程语句(always)中赋值的左侧必须是变量类型(如reg), 与综合得到的硬件无关, 仅为verilog中的语法规定.

练习:使用 assign 语句和组合逻辑的always块构建 AND 门.

// synthesis verilog_input_version verilog_2001 module top_module( input a, input b, output wire out_assign, output reg out_alwaysblock ); assign out_assign = a&b; always @(*) out_alwaysblock = a&b; endmodule
2)always块:时序逻辑

带有时钟的always块可以创建组合逻辑块,同时也可以在输出端建立一套触发器(或寄存器). 输出的改变不是立即发生的, 要等到下一个时钟的上升沿.

连续赋值: assign x = y;

阻塞赋值: x = y; (组合逻辑always块中)

非阻塞赋值: x <= y; (时序逻辑always块中)

遵循以上规则很重要, 否则会在仿真/综合的时候引起不必要的麻烦.

还有一点:在sv中组合逻辑和时序逻辑块的表示方式为: always_comb和always_ff.

练习:使用 assign 语句、always块(组合逻辑、时序逻辑)三种方式构建异或门(xor gate)a

module top_module( input clk, input a, input b, output wire out_assign, output reg out_always_comb, output reg out_always_ff ); assign out_assign = a^b; always @(*) out_always_comb = a^b; always @(posedge clk) out_always_ff <= a^b;//delay 1 cycle endmodule
3)if语句

在always块中使用. if示例如下, 一个2输入数据选择器, 也给出了等效的assign语句:

HDBits刷题1: Verilog Language

练习: 用assign 和if 两种方式实现4输入的数据选择器, 其真值表如下:

HDBits刷题1: Verilog Language

module top_module( input a, input b, input sel_b1, input sel_b2, output wire out_assign, output reg out_always ); assign out_assign = (sel_b1&&sel_b2) ? b : a; always @(*)begin if(sel_b1&&sel_b2)begin out_always = b; end else begin out_always = a; end end endmodule
4)if 语句latch

在进行if判断的时候要考虑到所有可能的情况,不然没未考虑到的情况出现时, 会导致输出不变, 这就是latch, 给出的示例如下:

HDBits刷题1: Verilog Language

HDBits刷题1: Verilog Language

生成的电路存在latch(在指定的情况之外,output不变), 所以应该加上其他条件:

module top_module ( input cpu_overheated, output reg shut_off_computer, input arrived, input gas_tank_empty, output reg keep_driving ); // always @(*) begin if (cpu_overheated) shut_off_computer = 1; else shut_off_computer = 0; //if not overheated then working end always @(*) begin if (~arrived) keep_driving = ~gas_tank_empty; else keep_driving = 0; //if arrived the stop end endmodule
5)case语句

将一条表达式与一堆其他的xx相比较的时候, case的功能与if-else if-else近乎相等.与c语言中的switch语句不同.

需要注意的一点是:用default避免latch

练习: 6选1数据选择器, sel在0到5之间时选择相应的数据输入, 否则输出0. 输入和输出位宽均为4.

module top_module ( input [2:0] sel, input [3:0] data0, input [3:0] data1, input [3:0] data2, input [3:0] data3, input [3:0] data4, input [3:0] data5, output reg [3:0] out );// always@(*) begin // This is a combinational circuit case(sel) 3'd0: out = data0; 3'd1: out = data1; 3'd2: out = data2; 3'd3: out = data3; 3'd4: out = data4; 3'd5: out = data5; default: out = 3'd0; endcase end endmodule
6)优先编码器

优先编码器是一个组合逻辑电路, 给定一个矢量输入, 输出为该矢量的第一个’1’出现的位置. 例如, 一个8位优先编码器, 给定输入为8’b1001_0000, 输出为8’d4, 因为4为第一个出现的’1′(这里应该是从0算起).

练习: 设计一个4位优先编码器, 如果没有输入bit为1, 则输出为0. 注意一个4bit数有16种可能的组合.

我的解决方法是casex.

casex: x和z出现的时候都不管它.

casez: z出现的时候不管它

module top_module ( input [3:0] in, output reg [1:0] pos ); always @(*)begin casex(in) 4'bxxx1: pos = 2'd0; 4'bxx10: pos = 2'd1; 4'bx100: pos = 2'd2; 4'b1000: pos = 2'd3; default: pos = 2'd0; endcase end endmodule
7)用casez的优先编码器

显式指定优先级, 这样不管case的顺序如何都能实现优先编码.

module top_module ( input [7:0] in, output reg [2:0] pos ); always @(*)begin casez(in) 8'bzzzz_zzz1: pos = 3'd0; 8'bzzzz_zz10: pos = 3'd1; 8'bzzzz_z100: pos = 3'd2; // try not use 4'bz1zz because it must be the 3rd case 8'bzzzz_1000: pos = 3'd3; // try not use 4'b1zzz because it must be the 4th case 8'bzzz1_0000: pos = 3'd4; 8'bzz10_0000: pos = 3'd5; 8'bz100_0000: pos = 3'd6; 8'b1000_0000: pos = 3'd7; default: pos = 3'd0; endcase end endmodule
8)avoiding latching

练习:根据下图实现键盘上下左右四个键位的映射

HDBits刷题1: Verilog Language

对于每个case项目, 不光要指定对应的键位为1, 还要指定其他键位为0, 以下是第一种方法.但这个方法其实不好, 如果键位特别多的话, 需要在每个case里面都给未映射到的键位赋值, 代码量很大.

 module top_module ( input [15:0] scancode, output reg left, output reg down, output reg right, output reg up ); always @(*)begin case(scancode) 16'he06b: begin left = 1'b1; right = 1'b0; up = 1'b0; down = 1'b0; end 16'he074: begin right = 1'b1; left = 1'b0; up = 1'b0; down = 1'b0; end 16'he072: begin down = 1'b1; left = 1'b0; right = 1'b0; up = 1'b0; end 16'he075: begin up = 1'b1; left = 1'b0; right = 1'b0; down = 1'b0; end default: begin left = 1'b0; right = 1'b0; up = 1'b0; down = 1'b0; end endcase end endmodule

于是可以在一开始就给所有键位赋默认值, 当其中某一个键位被映射到了后, 单独给其赋1. 有了如下的代码.

module top_module ( input [15:0] scancode, output reg left, output reg down, output reg right, output reg up ); always @(*)begin left = 1'b0; right = 1'b0; up = 1'b0; down = 1'b0; case(scancode) 16'he06b: left = 1'b1; 16'he074: right = 1'b1; 16'he072: down = 1'b1; 16'he075: up = 1'b1; default: begin left = 1'b0; right = 1'b0; up = 1'b0; down = 1'b0; end endcase end endmodule

5.more verilog features(更多特性)

1)conditional ternary operator: 三元运算符

练习: 给定4个无符号数, 找出最小值. 先用条件运算符分别比较两个数, 然后再将它们组合起来形成4路求最小值电路.(5行代码左右)

wire只能被assign连续赋值,reg只能在initial和always中赋值

module top_module ( input [7:0] a, b, c, d, output [7:0] min);// // assign intermediate_result1 = compare? true: false; wire [7:0] min_2 [1:0]; assign min_2[1] = (a < b) ? a : b; assign min_2[0] = (c < d) ? c : d; assign min = (min_2[1] < min_2[0]) ? min_2[1] : min_2[0]; endmodule
2)reduction operators: 缩减运算符

有时候需要对一个长矢量所有的位进行操作, 比如a[0] & a[1] & a[2] & … , 如果完全写出来会很麻烦. reduction操作可以对矢量的位进行and/or/xor操作, 最终得到一位的输出. 有如下示例:

HDBits刷题1: Verilog Language

注: (a[3:0] == 4’hf)输出为1位, 即矢量a为1111时, 括号内为true, 输出为1. 相当于&a[3:0].

这些一元操作只有一个操作符(类似于not操作!和~). 还可以将输出进行反相来构建一个nand(与非)门, nor(或非门)和xnor(同或门): 比如~&d[7:0].

练习: 奇偶校验常用于在不完美信道上传输数据的错误检测, 建立一个可以计算8bit字节奇偶的电路. 使用偶校验(对所有位进行xor处理). 期望代码行数为1.

注:偶校验码指在数据发送前检查1的个数,一共有偶数个1则头部填充0,奇数个1则头部填充1,整体保持偶数个1,接收数据时,重新检查1的个数。数据所有位进行XOR处理, 若有偶数个1则XOR结果为0, 否则结果为1.

module top_module ( input [7:0] in, output parity); assign parity = ^in[7:0]; endmodule
3)reduction: even wider gates

搭建一个有100个输入的组合逻辑电路,in[99:0].实现and/or/xor.

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)combinational for-loop: vector reversal 2

练习:将一个100位的vector进行倒序处理.

方法为for循环, 先定义一个整数integer i. 不是genvar(只能在generate里面用)

module top_module( input [99:0] in, output [99:0] out ); integer i; always @(*)begin for(i = 0; i < 100; i++)begin out[i] = in[99-i]; end end endmodule
5)combinational for-loop: 255-bit population count

练习: 此电路对vector中的1的数量进行计数, 输入为255bit的vector.

module top_module( input [254:0] in, output [7:0] out ); integer i; always @(*)begin out = 8'b0; for(i = 0; i < 255; i++)begin out = in[i] ? (out+1'b1) : out; end end endmodule
6)generate for-loop: 100-bit binary adder 2

练习:使用generate for 循环结构搭建一个100位的二进制加法器, 自己的解法如下:

module top_module( input [99:0] a, b, input cin, output [99:0] cout, output [99:0] sum ); fulladder fulader0(a[0], b[0], cin, sum[0], cout[0]); genvar i; generate for(i = 1; i < 100; i = i + 1) begin: fulladder fulladder u_fulladder(a[i], b[i], cout[i-1], sum[i], cout[i]); end endgenerate endmodule module fulladder( input a, input b, input cin, output sum, output cout); assign sum = a^b^cin; assign cout = ((a^b)&cin)|(a&b); endmodule

generate for-loop: 100-digit CBD adder

练习:使用generate for-loop构建一个100位加法器, 已经提供一个一位的BCD加法器.

注:BCD的加法是以4bit位为一个BCD位, 实际输入400bit的数据相当于100位的BCD码, 关于BCD码的说明,见二进制转BCD码原理及verilog实现一文.

module top_module( input [399:0] a, b, input cin, output cout, output [399:0] sum ); wire [99:-1] carry; assign carry[-1] = cin; assign cout = carry[99]; genvar i; generate for(i = 0; i < 400; i=i+4)begin: bcd_fulladd100 bcd_fadd bcd_fadd100( .a(a[i+3:i]), .b(b[i+3:i]), .cin(carry[i/4 - 1]), .cout(carry[i/4]), .sum(sum[i+3:i]) ); end endgenerate endmodule

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/114547.html

(0)
上一篇 2025-12-08 12:33
下一篇 2025-12-08 13:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信