【Verilog】10.10练习题——Verilog语法知识点补充 和 一些注意点

verilog基础练习题

Verilog也是能写for循环的!

①基本方法:

integer i;
for(i=0; i<=SIZE-1; i=i+1)
begin
	/* 代码 */
end 

②使用generate…for块(生成迭代器):

genvar i;  //即generate variable
generate
	for(i=0; i<=SIZE-1; i=i+1)
	begin
		/* 代码 */
	end
endgenerate

值得注意的是:

for循环的实质:Verilog中的for循环起电路复制的作用
for循环一般写在testbench中做测试用,而不是写在module中。


Verilog模块内部也是能写函数的!

Verilog Function函数语法说明

function [3:0]FUCTION_NAME;
input [SIZE-1:0] input_data;
input [SIZE-1:0] other_input;
begin
    reverse[0] = data[3];
    reverse[1] = data[2];
    reverse[2] = data[1];
    reverse[3] = data[0];
end
endfunction

还有一点需要注意,verilog中函数的书写在module整体偏后的位置,并且在使用之后书写:

调用function在书写function之前。

如下:

`timescale 1ns/1ns
module function_mod(
    input [3:0]a,
    input [3:0]b,
     
    output [3:0]c,
    output [3:0]d
);
 
assign c = reverse(a);
assign d = reverse(b);
 
function [3:0]reverse;
input [3:0] data;
begin
    reverse[0] = data[3];
    reverse[1] = data[2];
    reverse[2] = data[1];
    reverse[3] = data[0];
end
endfunction
 
endmodule

函数function中书写for循环的注意点

在function内不加任何特殊处理地写for循环,会产生报错(error: Variable declaration in unnamed block requires SystemVerilog.),比如下面这个程序:

function [3:0]reverse;
input [3:0] data;
begin
	integer i;
	for(i=0; i<4; i=i+1)
	begin
		assign reverse[i] = data[3-i];
	end
end
endfunction

这个代码有两种修改办法:
①integer i 的声明必须要写在function之外。

integer i;   //整形i的声明写在function之外
function [3:0]reverse;
input [3:0] data;
begin
 for(i=0; i<4; i=i+1)
 begin
  reverse[i] = data[3-i];
 end
end
endfunction

②integer i 如果声明成“局部变量”(即在function内),这个循环的进程块(begin…end)必须要起名字,如下:

function [3:0]reverse;
input [3:0] data;
begin:myloop   //begin....end进程块现在被取名为myloop
 integer i;
 for(i=0; i<4; i=i+1)
 begin
  reverse[i] = data[3-i];
 end
end
endfunction

值得注意的是:

函数在Verilog里很少用,并大多数情况下是推荐不用的。要用也是用在testbench中。(即“验证”时使用)


简单的组合逻辑模块实例化也要注意时序

描述

在数字芯片设计中,通常把完成特定功能且相对独立的代码编写成子模块,在需要的时候再在主模块中例化使用,以提高代码的可复用性和设计的层次性,方便后续的修改。

请编写一个子模块,将输入两个8bit位宽的变量data_a,data_b,并输出data_a,data_b之中较小的数。并在主模块中例化,实现输出三个8bit输入信号的最小值的功能。
子模块的信号接口图如下:
在这里插入图片描述
主模块的信号接口图如下:

使用Verilog HDL实现以上功能并编写testbench验证。
输入描述:
clk:系统时钟
rst_n:异步复位信号,低电平有效
a,b,c:8bit位宽的无符号数
输出描述:
d:8bit位宽的无符号数,表示a,b,c中的最小值

VL9 使用子模块实现三输入数的大小比较

错误代码:

`timescale 1ns/1ns
module main_mod(
    input clk,
    input rst_n,
    input [7:0]a,
    input [7:0]b,
    input [7:0]c,
     
    output [7:0]d
);
 
wire [7:0] compareline;
comparator tb_cpt1(
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(b),
 
    .c(compareline)
);
comparator tb_cpt2(
    .clk(clk),
    .rst_n(rst_n),
    .a(compareline),
    .b(c),
 
    .c(d)
);
 
endmodule
 
 
module comparator(
    input clk,
    input rst_n,
    input [7:0]a,
    input [7:0]b,
 
    output reg [7:0]c
);
 
always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0)   c <= 0;
    else begin
        c <= (a<b) ? a : b;
    end
end
 
endmodule

错误分析:

该电路为时序逻辑,先选择a、b最小的输出m,然后再将m与c比较输出d,因此需要2拍延迟。如果a、b最小值m直接与c进行比较,则时序错误,c永远比较的都是m的前一拍的值。

正确的代码:

`timescale 1ns/1ns
module main_mod(
	input clk,
	input rst_n,
	input [7:0]a,
	input [7:0]b,
	input [7:0]c,
	
	output [7:0]d
);

wire [7:0]result1;
wire [7:0]result2;
comparator tb_cpt1(
	.clk(clk),
	.rst_n(rst_n),
	.a(a),
	.b(b),

	.c(result1)
);
comparator tb_cpt2(
    .clk(clk),
	.rst_n(rst_n),
	.a(a),
	.b(c),

	.c(result2)
);
comparator tb_cpt3(
	.clk(clk),
	.rst_n(rst_n),
	.a(result1),
	.b(result2),

	.c(d)
);

endmodule


module comparator(
	input clk,
	input rst_n,
	input [7:0]a,
	input [7:0]b,

	output reg [7:0]c
);

always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)	c <= 0;
	else begin
		c <= (a<b) ? a : b;
	end
end

endmodule

时序逻辑——非状态机的Verilog代码实现(通过Q0Q1状态真值表)

描述
某同步时序电路转换表如下,请使用D触发器和必要的逻辑门实现此同步时序电路,用Verilog语言描述。
在这里插入图片描述
电路的接口如下图所示。
在这里插入图片描述
输入描述:
input A ,
input clk ,
input rst_n
输出描述:
output wire Y

画状态真值表,写各个输出(Y,Qn+1)的逻辑表达式:
在这里插入图片描述
代码:

`timescale 1ns/1ns

module seq_circuit(
      input                A   ,
      input                clk ,
      input                rst_n,
 
      output   wire        Y   
);

reg Q0,Q1;
always@(posedge clk or negedge rst_n)
begin
    if(rst_n==1'b0) begin
        Q0 <=0; //状态初始化
        Q1 <=0;
    end
    else begin
        Q0 <= ~Q0;   //下一个状态 <= 当前状态的逻辑表达式
        Q1 <= A & (~Q1) & (~Q0) | (~A) & (~Q1) & Q0 | (~A) & Q1 & (~Q0) | A & Q1 & Q0;
    end
end

assign Y = Q1 & Q0;

endmodule

时序逻辑——状态机的Verilog代码实现

描述
某同步时序电路的状态转换图如下,→上表示“C/Y”,圆圈内为现态,→指向次态。
请使用D触发器和必要的逻辑门实现此同步时序电路,用Verilog语言描述。
在这里插入图片描述
电路的接口如下图所示,C是单bit数据输入端。

输入描述:
input C ,
input clk ,
input rst_n
输出描述:
output wire Y

代码:

`timescale 1ns/1ns

module seq_circuit(
   input                C   ,
   input                clk ,
   input                rst_n,
 
   output   reg        Y   
);

reg [1:0]STATE;
parameter AA = 2'b00;
parameter BB = 2'b01;
parameter CC = 2'b10;
parameter DD = 2'b11;
//状态跳转(当前状态----[条件]---->下一个状态)
always@(posedge clk or negedge rst_n)
begin
    if(rst_n == 1'b0)   STATE <= AA;
    else begin
        case(STATE)
        AA: if(C == 0)   STATE <= AA;
            else        STATE <= BB;
        BB: if(C == 1)   STATE <= BB;
            else        STATE <= DD;
        CC: if(C == 1)   STATE <= CC;
            else        STATE <= AA; 
        DD: if(C == 0)   STATE <= DD;
            else        STATE <= CC;
        default:    STATE <= AA;
        endcase
    end
end
//不同状态对应的输出[注意:敏感列表必须是全部变量]
always@(*)
begin
    case(STATE)
    AA: Y <= 0; 
    BB: Y <= 0;
    CC: if(C == 1)  Y <= 1;
            else    Y <= 0;
    DD: Y <= 1;
    default: Y <= 0;
    endcase
end

endmodule

值得注意的是:

控制不同状态时的输出的always块,其敏感列表必须是全体(即*),而不是posedge clk or negedge rst_n !


always中a_temp<=a可锁存上一个时钟时刻a的数据

描述
有一个缓慢变化的1bit信号a,编写一个程序检测a信号的上升沿给出指示信号rise,当a信号出现下降沿时给出指示信号down。
注:rise,down应为单脉冲信号,在相应边沿出现时的下一个时钟为高,之后恢复到0,一直到再一次出现相应的边沿。
在这里插入图片描述
使用Verilog HDL实现以上功能并编写testbench验证。
输入描述:
clk:系统时钟信号
rst_n:异步复位信号,低电平有效
a:单比特信号,作为待检测的信号
输出描述:
rise:单比特信号,当输入信号a出现上升沿时为1,其余时刻为0
down:单比特信号,当输入信号a出现下降沿时为1,其余时刻为0

代码:

`timescale 1ns/1ns
module edge_detect(
	input clk,
	input rst_n,
	input a,
	
	output reg rise,
	output reg down
);

reg a_before;  //记录当前clk^前一个^的数据
always@(posedge clk or negedge rst_n)
begin
	if(rst_n == 1'b0)	begin
		rise <= 0;
		down <= 0;
		a_before <= 0;
	end
	else
		a_before <= a;
end

always@(posedge clk or negedge rst_n)
begin
	if(a_before == 1'b0 && a == 1'b1)	begin
		rise <= 1;
		down <= 0;
	end
	else if (a_before == 1'b1 && a == 1'b0)	begin
		rise <= 0;
		down <= 1;
	end
	else	begin
		rise <= 0;
		down <= 0;
	end
end

endmodule

结合这段代码,其实很好理解这个原理:

a_before每一个clk^ 都锁存这个clk^ 检测到的a值。
当下一个clk^ 到来时,赋值操作均未开始,因为要先进行if判断。
因此,在进行if判断时,a_before存储的还是a在上一个clk^ 的值,而此时clk^ 又会监测到一个新的a。

由此,我们可以说:

always中a_temp<=a可锁存上一个时钟上升沿检测的a的数据,直到当前时钟上升沿的赋值操作完成为止。
在当前的时钟上升沿,a将监测到一个新的值,a_temp此时即表示了上一个时钟上升沿a的数据。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值