FPGA学习笔记01——基本模块

本文介绍了移位寄存器的原理及在右移和左移操作中的实现,边沿检测模块如何消除信号波动,以及按键消抖电路设计,以确保稳定的输入。同时,文章还讨论了时钟分频器的实现,包括偶数和奇数分频,并给出了状态机的概念和在复杂控制流程中的应用。
摘要由CSDN通过智能技术生成

移位寄存器

基本的移位寄存器由D触发器构成,前级输出接到后级输入。移位寄存器分为左移和右移。在某些情况下,移位寄存器可以实现数据的串并转换。

parameter N = 4;
reg [N-1:0]q;
always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		q <= 0;
	else begin     //右移
		q[0] <= a;
		q[N-1:1] <= q[N-2:0];    
		
		/*         //左移
		q[N-2:0] <= q[N-1:1];
		q[N-1] <= 0;
		*/
	end
end

边沿检测

边沿检测模块用于检测输入或FPGA内部信号的上升沿和下降沿,可监控信号的状态变化。一般用三个移位寄存器存储数据(q[0]、q[1]、q[2] = data_in),这里延迟了三个时钟周期,可消除信号上升时间导致的亚稳态。
上升沿:posedge = q[1] & ~q[2];
下降沿:negedge = ~q[1] & q[2];

always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
		q <= 0;
	else begin
	q[0] <= data_in;
	q[1] <= q[0];
	q[2] <= q[1];

	end
end
	
assign pos_edge = q[1] & ~q[2];
assign neg_edge = ~q[1] & q[2];

按键消抖

机械按键按下时会经过十几毫秒的震荡才逐渐稳定下来,因此在使用按键控制某信号时一般要添加按键消抖模块用于消除震荡,基本思路是比较前后时钟沿时刻输入的值,当值不变时再延时20ms,将稳定的结果作为输出。

//设时钟频率为50MHz
parameter CNT_MAX = 20'd100_0000;  //根据时钟频率确定

reg [19:0]cnt;
reg data_in_reg1;
reg data_in_reg2;

always @(posedge clk or negedge rst_n) begin  //震荡检测
	if(!rst_n)
		data_in_reg1 <= 0;
		data_in_reg2 <= 0;
	else 
		data_in_reg1 <= data_in;			//延时两个时钟周期
		data_in_reg2 <= data_in_reg1;
		
end


always @(posedge clk or negedge rst_n) begin   //20ms延时模块
	if(!rst_n)
		cnt <= 0;
	else if(data_in_reg1 != data_in_reg2 )  
		cnt <= CNT_MAX;
	else if(cnt > 0)
		cnt <= cnt - 1;
	else
		cnt <= 0;
end

always @(posedge clk or negedge rst_n) begin   //输出模块
	if(!rst_n)
		data_out <= 0;
	else if(cnt == 1)
		data_out <= data_in_reg2;
	else
		data_out <= data_out;
end

时钟分频

根据分频器的分频比例(分频前的频率和分频后的频率比值)是偶数还是奇数,将分频器分为偶数分频器和奇数分频器。
偶数分频器:计数器计数到N/2-1时信号翻转,如6分频电路,cnt计数到2时让clk_6div翻转,即0、1、2为高,3、4、5为低,实现6分频。
奇数分频器:需要两个中间变量,一个对上升沿计数到N/2-1时赋值高电平,一个对下降沿计数到N/2-1时赋值高电平,再对两个时钟变量进行与操作即可。
在这里插入图片描述

parameter	N = 5;							//分频系数
parameter	EDGE = N/2-1;					//取沿数

reg	 [2:0]	cnt		;						//3位计数器
reg			out_clk1;						
reg			out_clk2;						

//计数器模块
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		cnt <= 3'd0;
	else if(cnt == N - 1'b1)
		cnt <= 3'd0;
	else
		cnt <= cnt + 'd1;
end

//out_clk1在上升沿进行翻转
always@(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		out_clk1 <= 1'b0;
	else if(cnt <= EDGE)
		out_clk1 <= 1'b0;
	else
		out_clk1 <= 1'b1;
end		

//out_clk1在下降沿进行翻转
always@(negedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		out_clk2 <= 1'b0;
	else if(cnt <= EDGE)
		out_clk2 <= 1'b0;
	else
		out_clk2 <= 1'b1;
end
				
assign out_clk = out_clk1 & out_clk2;

endmodule

状态机

对于某一任务,如果是按顺序执行,例如其步骤是1-2-3-4-5,这个时候用计数器实现是最方便的。但在一些比较复杂的控制场合,例如步骤是2-5-1-3-4,其跳转顺序是乱序的,就要用状态机来实现。
状态机分为Mealy型和Moore型,其中,Moore型的输出与输入无关,而Mealy型有关。一般来讲,Mealy型会比Moore型少一个状态。
在这里插入图片描述
在这里插入图片描述
标准的三段式状态机写法步骤为:
0、状态空间定义
1、(时序)状态的转换
2、(组合)下个状态的判断
3、(时序)各状态下的动作

//检测序列“1101”
module FSM_sequence(
	input clk,
	input rst_n,
	input a,
	
	output reg z
);

reg [4:0]current_state;
reg [4:0]next_state;

//状态空间定义
parameter S0 = 5'b00001;
parameter S1 = 5'b00010;
parameter S2 = 5'b00100;
parameter S3 = 5'b01000;
parameter S4 = 5'b10000;

 //状态跳转
always @(posedge clk or negedge rst_n) begin   
	if(!rst_n) begin
		current_state <= S0;
		end
	else 
		next_state <= current_state;
end

  //下个状态的判断
always @(*) begin   
	case(current_state):
		S0:next_state = x ? S1 : S0;//1
		S1:next_state = x ? S2 : S0;//1
		S2:next_state = x ? S2 : S3;//0
		S3:next_state = x ? S4 : S0;//1
		S4:next_state = x ? S2 : S0;
	default:next_state = S0;
	endcase	
end

//各状态下动作
always @(posedge clk or negedge rst_n) begin   
	if(!rst_n) 
		z <= 0;
	else begin
		if(next_state == S4)
			z <= 1;
		else
			z <= 0;
	end
endmodule
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值