自己整理的FPGA题准备面试(复位,跨时钟域,FIFO深度,同异步FIFO实现)

自己整理的FPGA题准备面试

一:复位设计

为什么需要复位呢?
系统发生错误时,能够初始化后回到正常状态。

用同步复位还是异步复位和芯片有关
Altera:
同步复位:一个选择器和一个寄存器构成。
异步复位:只有一个寄存器构成。
Altera的最小逻辑单元LE中的寄存器结构决定
Altera器件中的基本单元就是由异步复位且低有效结构的寄存器组成的。

Xilinx芯片时规则,尽量避免使用异步复位,且如果使用复位就用高复位(同步高复位)

同步复位的D触发器和异步复位的D触发器的不同点是复位有效的条件是“立刻”执行还是等待“沿”再执行的区别。
异步复位有一个明显的问题就是会产生亚稳态的问题。

因为时钟沿变化和异步复位都可以引起Q端数据变化。如果异步复位信号跟时钟在一定时间间隔内发生变化(Removal time和Recovery time,类似于建立/保持时间),就是说只要复位信号不在时钟有效沿附近变化(复位信号远离时钟有效沿),就可以保证电路的正常复位和撤销,否则Q值将无法确定即产生亚稳态现象。同步复位就没有这种问题。
所以用异步复位同步释放机制。

只要存在复位都会增加布局布线的负担,因为复位会像时钟一样连接到每一个寄存器上,是相当复杂的工程,会增加时序收敛的难度,

1:异步复位,同步释放

always @ (posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		rst_s1 <= 1'b0;
		rst_s2 <= 1'b0;
	end
	else begin
	   rst_s1 <= 1'b1	;
	   rst_s2 <= rst_s1	;
	end
end

为了避免亚稳态,让拉高的复位信号打两拍,达到与时钟clk边沿同步的目的
在这里插入图片描述

1.2:各种复位的优缺点

1.2.1同步复位
实际电路是把复位信号rst_n作为输入逻辑的使能信号,所以同步复位导致额外增加FPGA内部的资源消耗。

1.2.2异步复位
结束于breg0和creg0的{tsu+th:建立时间+保持时间}之外
复位信号rst_n的撤销(由低电平变为高电平)出现在建立时间和保持时间内,此时clk检测到rst_n的状态就会是一个亚稳态(不确定是0还是1)。

1.2.3异步复位,同步释放
推荐

二 跨时钟域

2.1 单bit信号和多bit信号的跨时钟域处理

慢到快采用打两拍
 慢时钟域信号寄存器输出
	always @(posedge clk_1 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
			src_state <= 1'b0;
		else
			src_state <= din;
	end
	
同步至快时钟域
	always @(posedge clk_2 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
		begin
			src_state_d0 <= 1'b0;
			src_state_d1 <= 1'b0;		
		end
		else
		begin
			src_state_d0 <= src_state;
			src_state_d1 <= src_state_d0;
		end
	end
	
	assign dout = src_state_d1;
快时钟域到慢时钟域,脉冲同步器

将原时钟域下的脉冲信号,转化为电平信号,再进行同步,同步完成之后再把新时钟域下的电平信号转化为脉冲信号

原时钟域下脉冲信号转变为电平信号
	always @(posedge clk_1 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
			src_state <= 1'b0;
			
		else if(din == 1'b1) //MUX完成翻转,脉冲到来,从脉冲到电平的转换
			src_state <= ~src_state;

		else 
			src_state <= din ^ src_state;		//异或
	end
	
	同步至新时钟域
	always @(posedge clk_2 or negedge rst_n)
	begin
		if(rst_n == 1'b0)
		begin
			src_state_d0 <= 1'b0;
			src_state_d1 <= 1'b0;
			src_state_d2 <= 1'b0;		
		end
		else
		begin
			src_state_d0 <= src_state;
			src_state_d1 <= src_state_d0;
			src_state_d2 <= src_state_d1;
		end
	end
	
	电平转换成脉冲(边沿检测产生新的脉冲)
	assign dout = src_state_d1 ^ src_state_d2;
**输入脉冲的最小间隔必须等于两个新时钟的时钟周期。**否则会展宽
快时钟域到慢时钟域,先展宽再同步再变脉冲
/在clka下生成展宽信号singal_a/
always @ (posedge clka or negedge rst_n)
begin
  if (!rst_n)
      singal_a <= 1'b0;
  else if (pulse_ina)
      singal_a <= 1'b1;
  else if (singal_a_r1)
      singal_a <= 1'b0;
  else 
      singal_a <= singal_a;
end

/在clkb下同步singal_a/
always @ (posedge clkb or negedge rst_n)
begin
  if (!rst_n)
      singal_b <= 1'b0;
  else 
      singal_b <= singal_a ;
end

/在clkb下生成脉冲信号和输出信号/
always @ (posedge clkb or negedge rst_n)
begin
  if (!rst_n)
      singal_b_r0 <= 1'b0;
      singal_b_r1 <= 1'b0;
  else 
     singal_b_r0 <= singal_b;
     singal_b_r1 <= singal_b_r0 ;
end

assign pulse_outb = ~singal_b_r1 & singal_b_r0 ;
assign singal_outb = singal_b_r1 ;

/在clka下同步singal_b_r1;生成singal_a_r1,用于反馈拉低singal_a /
always @ (posedge clka or negedge rst_n)
begin
  if (!rst_n)
      singal_a_r0 <= 1'b0;
      singal_a_r1 <= 1'b0;
  else 
      singal_a_r0 <= singal_b_r1 ;
      singal_a_r1 <= singal_a_r0 ;
end

握手反馈机制

原理:双方电路在声明或终止各自的握手信号信号前都要等待对方的相应。

1:请求信号的产生:
2:请求信号的跨越与应答信号的产生:
3:应答信号的跨越与请求信号的清除:
4:应答信号的清除:

同步器空闲状态的判断:原时钟下:请求和应答信号同时无效

assign sync_idle = ~(src_sync_req | src_sync_ack );

同步失败的判断
always @(posedge clk_A or negedge rst_n_A)
begin
	if(rst_n_A == 1'b0) 
		sync_fail <= 1'b0;
	else if(din & (~sync_idle))		//源时钟脉冲到来,此时同步器不空闲,给出同步失败
		sync_fail <= 1'b1;
	else
		sync_fail <= 1'b0;
end

原时钟产生请求信号,请求信号的产生相当于将脉冲转化为了电平
always @(posedge clk_A or negedge rst_n_A)
begin
	if(rst_n_A == 1'b0)
		src_sync_req <= 1'b0;
	else if(din & sync_idle)			//源时钟脉冲到来,且源时钟空闲,传递请求。同时完成了脉冲转电平
		src_sync_req <= 1'b1;
	else if(src_sync_ack)						//检测到应答以后,清除请求
		src_sync_req <= 1'b0;
end

同步原时钟请求信号到目的时钟,利用请求信号跨时钟域
always @(posedge clk_B or negedge rst_n_B)
begin
	if(rst_n_B == 1'b0)
	begin
		req_state_dly1 <= 1'b0;
		req_state_dly2 <= 1'b0;
		req_state_dly3 <= 1'b0;
	end
	else
	begin
		req_state_dly1 <= src_sync_req;
		req_state_dly2 <= req_state_dly1;		//打两拍结束
		req_state_dly3 <= req_state_dly2;		//再外接一个寄存器,以保证脉冲输出
	end
end

上升沿检测,产生输出脉冲
assign dout = (~req_state_dly3) & req_state_dly2;		//完成输出脉冲

目的时钟产生应答信号
always @(posedge clk_B or negedge rst_n_B)
begin
	if(rst_n_B == 1'b0)
		dst_sync_ack <= 1'b0;
	else if (req_state_dly2)		//同步高电平已到达
		dst_sync_ack <= 1'b1;
	else
		dst_sync_ack <= 1'b0;
end

同步目的时钟产生的应答信号到原时钟
always @(posedge clk_A or negedge rst_n_A)
begin
	if(rst_n_A == 1'b0)
	begin
		ack_state_dly1 <= 1'b0;
		ack_state_dly2 <= 1'b0;
	end
	else
	begin
		ack_state_dly1 <= dst_sync_ack;
		ack_state_dly2 <= ack_state_dly1;			
	end
end
assign src_sync_ack = ack_state_dly2;
多bit跨时钟域

异步fifo

三:FIFO

fifo深度计算

fifo深度/(写入速率 - 读出速率) > 写入数据/写入速率

写时钟频率wclk 读时钟频率rclk
写时钟每B个时钟周期会有A个数据写入FIFO
读时钟每Y个时钟周期会有X个数据读出FIFO
depth = burst_length - (burst_length /wclk)(rclkX/Y)
burst_length = 2A

同步FIFO的Verilog实现

输入数据位宽16位,深度256

module(
input clk,
input rst_n,
input wr_en,
input [150] data_in,
input rd_en,

output [150] data_out,
output empty,
output full
);

reg [7:0] wr_cnt;
reg [7:0] rd_cnt
reg [7:0] status_cnt ;
reg [150] fifo_memory [70];

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
	   wr_cnt <= 8'd0;
	else if (wr_en)
	   wr_cnt <= wr_cnt  + 1'b1;
	else 
	   wr_cnt <= wr_cnt ;
end

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
	   rd_cnt <= 8'd0;
	else if (rd_en)
	   rd_cnt <= rd_cnt+ 1'b1;
	else 
	   rd_cnt <= rd_cnt;
end

interger i ;
always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
	  begin
		for(i=0; i<=255; i=i+1)
		   fifo_memory [i] <= 16'd0;
      end
    else if(wr_en)
      begin
         fifo_memory [wr_cnt] <= data_in;
      end
    else
         fifo_memory  <= fifo_memory ;      
end

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
	   data_out <= 16'd0;
	else if (rd_en)
	   data_out <= fifo_depth[rd_cnt];
	else 
	   data_out <= data_out ;
end

always @ (posedge clk or negedge rst_n)
begin
	if (!rst_n)
	   status_cnt <= 8'd0;
	else if (wr_en && (!rd_en) && (!full  ))
	   status_cnt <= status_cnt + 1'b1;
	else if (rd_en && (!wr_en) && (!empty ))
	   status_cnt <= status_cnt - 1'b1;
	else 
	   status_cnt  <= status_cnt ;
end

asssign full  =(status_cnt == 8'd255 )?1'b1:1'b0;
asssign empty =(status_cnt == 8'd0 )?1'b1:1'b0;

endmodule

异步FIFO的Verilog实现

数据位宽16位,深度为256

复位,读写指针相等,为空
写指针多写一圈,追上读指针,读写指针再次相等,则FIFO为满
读指针追上写指针,读写指针再次相等,则FIFO为空

指针多增加一位,最高位为循环圈数,读写指针完全相等,则FIFO为空,除了最高位外,读写指针其他完全相等,则FIFO位满

module(
input clk_1,
input clk_2;
input rst_n,
input wr_en,
input [15:0] data_in,
input rd_en,

output [15:0] data_out,
output full,
output empty
);

reg [8:0] wr_cnt_ptr;      //写地址指针多一位,表示循环次数
reg [7:0] wr_cnt;
reg [8:0] rd_cnt_ptr;      //读地址指针多一位,表示循环次数
reg [7:0] rd_cnt;
assign wr_cnt = wr_cnt_ptr[7:0];
assign rd_cnt = rd_cnt_ptr[7:0];

always @ (posedge clk_1 or negedge rst_n) //写指针加
begin
  if (!rst_n)
     wr_cnt_ptr <= 8'd0;
  else if (wr_en)
     wr_cnt_ptr <= wr_cnt_ptr+ 1'b1;
  else 
     wr_cnt_ptr <= wr_cnt_ptr;
end

always @ (posedge clk_2 or negedge rst_n) //读指针加
begin
  if (!rst_n)
     rd_cnt_ptr <= 8'd0;
  else if (rd_en)
     rd_cnt_ptr <= rd_cnt_ptr+ 1'b1;
  else 
     rd_cnt_ptr <= rd_cnt_ptr;
end

reg [15:0] fifo_memory [255:0]; //写入
interger i;
always @ (posedge clk_1 or negedge rst_n)
begin
  if (!rst_n)
     for(i=0;i<=255;i=i+1)
       fifo_memory[i] <= 16'd0;
  else if ((wr_en) && (~full))
  	   fifo_memory[wr_cnt] <= data_in;
  else 
       fifo_memory[wr_cnt] <= fifo_memory[wr_cnt]; 
end

always @ (posedge clk_2 or negedge rst_n) //读出
begin
  if (!rst_n)
     data_out<= 16'd0;
  else if ((rd_en) && (~empty))
     data_out<= fifo_memory[rd_cnt];
  else 
     data_out<= data_out;
end

//格雷码转换
reg [8:0] gray_wr_ptr;
reg [8:0] gray_rd_ptr;
assign gray_wr_ptr = (wr_cnt_ptr>>1) ^(wr_cnt_ptr);
assign gray_rd_ptr = (rd_cnt_ptr >>1) ^(rd_cnt_ptr );

reg [8:0] gray_wr_ptr_d1;
reg [8:0] gray_wr_ptr_d2;
reg [8:0] gray_rd_ptr_d1;
reg [8:0] gray_rd_ptr_d2;

//写地址指针同步到读
//读地址指针同步到写
always @ (posedge clk_2 or negedge rst_n) 
begin
  if (!rst_n)
    begin
     gray_wr_ptr_d1 <= 8'd0;
     gray_wr_ptr_d2 <= 8'd0;
    end
  else 
    begin
     gray_wr_ptr_d1 <= gray_wr_ptr;
     gray_wr_ptr_d2 <= gray_wr_ptr_d1;
    end
end

always @ (posedge clk_1 or negedge rst_n) 
begin
  if (!rst_n)
    begin
     gray_rd_ptr_d1 <= 8'd0;
     gray_rd_ptr_d2 <= 8'd0;
    end
  else 
    begin
     gray_rd_ptr_d1 <= gray_rd_ptr;
     gray_rd_ptr_d2 <= gray_rd_ptr_d1;
    end
end

//写侧满,读侧空
assign full = (gray_wr_ptr =={~(gray_rd_ptr_d2[15:13]),gray_rd_ptr_d2[13:0]} )?1'b1:1'b0;
assign empty = (gray_rd_ptr == gray_wr_ptr_d2)?1'b1:1'b0;

{~gray_wr_ptr_d2[depth-1-:2];gray_wr_ptr_d2[depth-3:0]}

endmodule
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值