当前位置: 首页 > news >正文

【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循环一般写在testbench中做测试用,而不是写在module中。
因为for循环是不能被综合(synthesis)的,不能写在常规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循环会报错(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

报错的原因其实在上一个部分中就讲过了:for循环是不能被综合的,不能写在常规的module中


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

描述

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

请编写一个子模块,将输入两个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的数据。

相关文章:

  • Hive表删除数据不支持使用Delete From...
  • 数据库-进阶-存储引擎
  • LeetCode题目笔记——459. 重复的子字符串,python从700ms到60ms
  • C++ | 12天学好C++ (Day 12)->结构图可视化、代码加通俗理解
  • 【深入理解Kafka系列】第五章 日志存储
  • 想做好数据可视化?手把手教你正确选择图表类型
  • C#【高级篇】 IntPtr是什么?怎么用?
  • 软考知识点---01计算机的基本组成---02存储系统
  • Day09JavaWeb第九次笔记---Request和Response学习
  • 第三章 Flink基础理论之内存优化及常见内存报错解决方案
  • 分数阶粒子群算法-附代码
  • springboot(三)
  • Kubernetes 常见面试题(六)
  • Linux安装禅道最新版
  • 【Bluetooth|蓝牙开发】十一、一文秒懂 | 超详细的Bluez交叉编译
  • 【翻译】babel对TC39装饰器草案的实现
  • Centos6.8 使用rpm安装mysql5.7
  • Invalidate和postInvalidate的区别
  • Java多态
  • js 实现textarea输入字数提示
  • PHP面试之三:MySQL数据库
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Vue官网教程学习过程中值得记录的一些事情
  • Wamp集成环境 添加PHP的新版本
  • 从tcpdump抓包看TCP/IP协议
  • 排序算法学习笔记
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 微信开放平台全网发布【失败】的几点排查方法
  • 新书推荐|Windows黑客编程技术详解
  • ​2020 年大前端技术趋势解读
  • #QT(串口助手-界面)
  • $ git push -u origin master 推送到远程库出错
  • (30)数组元素和与数字和的绝对差
  • (C++20) consteval立即函数
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (vue)页面文件上传获取:action地址
  • (二开)Flink 修改源码拓展 SQL 语法
  • (附源码)计算机毕业设计ssm电影分享网站
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • *2 echo、printf、mkdir命令的应用
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET上SQLite的连接
  • .NET与 java通用的3DES加密解密方法
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • @Transactional 竟也能解决分布式事务?
  • [.NET]桃源网络硬盘 v7.4
  • [2018/11/18] Java数据结构(2) 简单排序 冒泡排序 选择排序 插入排序
  • [acm算法学习] 后缀数组SA
  • [Bzoj4722]由乃(线段树好题)(倍增处理模数小快速幂)
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法