RTL协议包拼凑 - 逻辑优化部分、代码优化部分

这是本人在实际工作中遇到的设计任务,借此研究状态机设计的优化策略


4. 逻辑优化——rx_fifo_rd_en与tx_fifo_wr_en的 流水协同

读使能和写使能的本质上不是流水,其实还是在GET_HEAD、GET_DATA和GET_DATA_NUM中的小状态机,即先读出再写入。

如下图所示,红色部分表示读出,绿色部分表示写入

在这里插入图片描述

可以借鉴AHB的原理,address phase给出控制信息,下一拍data phase给出数据,同时给出下次写的address phase的控制信息。

在这里插入图片描述

对应本文内容其实就是向TX_FIFO写入数据的同时向RX_FIFO读出新的数据,思路就变成如下图。

在这里插入图片描述

所以说读写流水协同本质上还是状态机,只不过是

1. 任意时刻不同信号从属的状态机不同。2. 不同状态机内部的状态转移条件相互影响。

例如本文中读相关信号和写相关信号做的事情就不同,属于不同的状态机。而流水协同就需要保证旧数据成功写入并且新数据成功读出,才能继续写or读

下面详细介绍如何实现rx_fifo_rd_en与tx_fifo_wr_en的 流水协同

首先还是状态机的思路,上图中已经给出读信号和写信号的状态机

4.1. GET_HEAD

如何开始流水呢?我们需要先读出一包要被写入的数据,那就是帧头,最快的时序如下

在这里插入图片描述

这里有一个细节,就是读出帧头之后rx_fifo_rd_en还是高,也就是说GET_DATA_NUM的第一拍就可能会读出数据个数,因此更新流水状态机

在这里插入图片描述

4.2. GET_DATA_NUM

在该状态下确定能够读出数据之后rx_fifo_rd_en拉低,同时在GET_HEAD的最后一拍也拉高tx_fifo_wr_en写入数据,只有当帧头成功写入且数据长度成功读出才转移到下一个状态。

在这里插入图片描述

注意读出数据有效标志是rx_fifo_rdata_val_dly会一直拉高,直到在下一个状态给到tx_fifo_wdata。从时序图中可以看到,当写入成功后tx_fifo_wr_en会拉低,所以读出新数据、写完旧数据的标志是rx_fifo_rdata_val_dly && !tx_fifo_wr_en

在这里插入图片描述

4.3. GET_DATA

之后就可以参照GET_DATA_NUM的时序流水起来,如下图读写同时进行

在这里插入图片描述

注意流水状态机的状态转移条件是rx_fifo_rdata_val_dly && !tx_fifo_wr_en,所以必须待tx_fifo_wr_en拉低才能开始新一轮流水。
如果要更快一点,在写恰好完成or读恰好完成的那一拍开始新一轮流水,那么就要考虑是写先完成等待读、还是读先完成等待写还是同时完成,三个条件相或就是流水状态转移条件,即就为(!tx_fifo_wr_en && rx_fifo_rdata_val_dly) || (tx_fifo_wr_en && !tx_fifo_full &&rx_fifo_rdata_val_dly)

流水状态机就为

在这里插入图片描述

RD_CHECK_SUM与WR_CHECK_SUM

这里要讨论的就是边界条件了

如下图所示,即使满足流水状态转移条件当data_num == 3'd0rx_fifo_rd_en也不再拉高了。

在这里插入图片描述

那么GET_DATA转回IDLE的条件是data_num == 3'd0 && tx_fifo_wr_en && !tx_fifo_full吗?如下图所示,如果tx_fifo_wr_en一直有效延长至data_num == 3'd0的时刻,那么就会直接跳至IDLE状态,而此时校验和还未写入!rx_fifo_rdata_val_dly将一直为高!

所以转回IDLE的条件应该是RX_FIFO没读、TX_FIFO写完,即data_num == 3'd0 && !rx_fifo_rdata_val_dly && !tx_fifo_wr_en

在这里插入图片描述

因此最终得到的流水状态机就为

在这里插入图片描述

4.4. 代码

逻辑设计优化完就可以写代码了,注意第一遍写代码的逻辑要足够清晰严谨,if、elseif要写全保证功能正确,然后仿真。确定没问题了,再去优化,然后再仿真确定优化的没毛病。

module	frame_combination(
	input			clk,
	input			rstn,
	
	input	[15:0]	rx_fifo_rdata,
	input			rx_fifo_rdata_val,
	input			rx_fifo_empty,
	output			rx_fifo_rd_en,
	
	output	[15:0]	tx_fifo_wdata,
	output			tx_fifo_wr_en,
	input			tx_fifo_full,	
	
	input			en_trans,
	input	[15:0]	work_mode,
	input	[15:0]	compensation_num
);

localparam	IDLE 			= 2'b00;
localparam	GET_HEAD 		= 2'b01;
localparam	GET_DATA_NUM 	= 2'b10;
localparam	GET_DATA 		= 2'b11;

reg		[1:0]		cur_state;
reg		[1:0]		nxt_state;
reg		[15:0]		data_num;
reg		[7:0]		check_sum;
reg					rx_fifo_rd_en_r;
reg					rx_fifo_rdata_val_dly;
reg		[15:0]		tx_fifo_wdata_r;
reg					tx_fifo_wr_en_r;
//wire				tx_fifo_wr_done;
wire				data_num_zero;

//assign tx_fifo_wr_done = tx_fifo_wr_en && !tx_fifo_full;
assign data_num_zero = ~(|data_num);

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		cur_state <= IDLE;
	else
		cur_state <= nxt_state;
end

always@(*) begin
	case(cur_state)
	
		IDLE:
			if(en_trans)
				nxt_state = GET_HEAD;
			else
				nxt_state = IDLE;
				
		GET_HEAD:
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val)
				nxt_state = GET_DATA_NUM;
			else
				nxt_state = GET_HEAD;
				
		GET_DATA_NUM:
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				nxt_state = GET_DATA;
			else
				nxt_state = GET_DATA_NUM;
		
		GET_DATA:
			if(data_num_zero && tx_fifo_wr_en && !tx_fifo_full)
				nxt_state = IDLE;
			else
				nxt_state = GET_DATA;
				
		default:	nxt_state = IDLE;
	endcase
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		data_num <= 16'd0;
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				data_num <= rx_fifo_rdata + 16'd1;
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rd_en_r && !rx_fifo_empty)
				data_num <= data_num - 16'd1;
		end
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		check_sum <= 8'd0;
	else if(cur_state == GET_HEAD)							
		check_sum <= 8'd0;
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				check_sum <= check_sum + rx_fifo_rdata[7:0];
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en) begin
				if(data_num == 16'd4)
					check_sum <= check_sum + work_mode;
				else if(data_num == 16'd2)
					check_sum <= check_sum + compensation_num;
				else if(data_num_zero)
					check_sum <= check_sum;
				else
					check_sum <= check_sum + rx_fifo_rdata[7:0];
			end
				
		end
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rdata_val_dly <= 1'b0;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val) begin
				if(rx_fifo_rd_en_r) begin
					if(rx_fifo_empty)
						rx_fifo_rdata_val_dly <= 1'b0;
					else
						rx_fifo_rdata_val_dly <= 1'b1;
				end
			end
		end
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rdata_val_dly) begin
				if(!tx_fifo_wr_en)
					rx_fifo_rdata_val_dly <= 1'b0;
				else
					rx_fifo_rdata_val_dly <= 1'b1;
			end
			else if(rx_fifo_rd_en_r && !rx_fifo_empty)
				rx_fifo_rdata_val_dly <= 1'b1;
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rdata_val_dly) begin
				if(!tx_fifo_wr_en)
					rx_fifo_rdata_val_dly <= 1'b0;
				else
					rx_fifo_rdata_val_dly <= 1'b1;
			end
			else if(rx_fifo_rd_en_r && !rx_fifo_empty)
				rx_fifo_rdata_val_dly <= 1'b1;
		end
end


always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rd_en_r <= 1'b0;
	else if(cur_state == IDLE && en_trans)
		rx_fifo_rd_en_r <= 1'b1;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
		end
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rd_en_r) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
			else if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				rx_fifo_rd_en_r <= 1'b1;
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rd_en_r) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
			else if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en && !data_num_zero)
				rx_fifo_rd_en_r <= 1'b1;
		end	
end

assign rx_fifo_rd_en = rx_fifo_rd_en_r;


always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wdata_r <= 16'd0;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val)
				tx_fifo_wdata_r <= rx_fifo_rdata;
		end
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				tx_fifo_wdata_r <= rx_fifo_rdata;
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en) begin	
				if(data_num == 16'd4)
					tx_fifo_wdata_r <= work_mode;
				else if(data_num == 16'd2)
					tx_fifo_wdata_r <= compensation_num;
				else if(data_num_zero)
					tx_fifo_wdata_r <= check_sum;
				else
					tx_fifo_wdata_r <= rx_fifo_rdata;
			end
		end
end

assign tx_fifo_wdata = tx_fifo_wdata_r;


always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wr_en_r <= 1'b0;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val) 
				tx_fifo_wr_en_r <= 1'b1;
		end
	else if(cur_state == GET_DATA_NUM) begin
			if(tx_fifo_wr_en_r) begin
				if(tx_fifo_full)
					tx_fifo_wr_en_r <= 1'b1;
				else
					tx_fifo_wr_en_r <= 1'b0;
			end
			else if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				tx_fifo_wr_en_r <= 1'b1;
		end
	else if(cur_state == GET_DATA) begin
			if(tx_fifo_wr_en_r) begin
					if(tx_fifo_full)
						tx_fifo_wr_en_r <= 1'b1;
					else
						tx_fifo_wr_en_r <= 1'b0;
				end
				else if(rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
					tx_fifo_wr_en_r <= 1'b1;
		end
end

assign tx_fifo_wr_en = tx_fifo_wr_en_r;

endmodule	

6. 代码优化

一定要先仿真再优化代码!先仿真再优化代码!先仿真再优化代码!

可以看到有的变量在GET_HEAD、GET_DATA_NUM和GET_DATA三个状态下,做的事情都是类似的,都是先读再写,所以合并相同逻辑、去除额外约束降低不少资源消耗。

6.1. tx_fifo_wr_en与tx_fifo_wdata

例如tx_fifo_wr_en_r,显然有了帧头之后的时序就完全相同,所以根本不需要每次都对cur_state进行判断

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wr_en_r <= 1'b0;
	else if(nxt_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val) 
				tx_fifo_wr_en_r <= 1'b1;
			else if(tx_fifo_wr_en) begin
					if(tx_fifo_full)
						tx_fifo_wr_en_r <= 1'b1;
					else
						tx_fifo_wr_en_r <= 1'b0;
			end
		end
	else if(nxt_state == GET_DATA_NUM) begin
			if(rx_fifo_rdata_val) 
				tx_fifo_wr_en_r <= 1'b1;
			else if(tx_fifo_wr_en) begin
					if(tx_fifo_full)
						tx_fifo_wr_en_r <= 1'b1;
					else
						tx_fifo_wr_en_r <= 1'b0;
			end
		end
	else if(nxt_state == GET_DATA) begin
			if(rx_fifo_rdata_val) 
				tx_fifo_wr_en_r <= 1'b1;
			else if(tx_fifo_wr_en) begin
					if(tx_fifo_full)
						tx_fifo_wr_en_r <= 1'b1;
					else
						tx_fifo_wr_en_r <= 1'b0;
			end
		end
end

因此重新改为

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wr_en_r <= 1'b0;
	else if(nxt_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val) 
				tx_fifo_wr_en_r <= 1'b1;
			else if(tx_fifo_wr_en) begin
					if(tx_fifo_full)
						tx_fifo_wr_en_r <= 1'b1;
					else
						tx_fifo_wr_en_r <= 1'b0;
			end
		end
	else if(rx_fifo_rdata_val) 
		tx_fifo_wr_en_r <= 1'b1;
	else if(tx_fifo_wr_en) begin
			if(tx_fifo_full)
				tx_fifo_wr_en_r <= 1'b1;
			else
				tx_fifo_wr_en_r <= 1'b0;
	end
end

再如tx_fifo_wdata,有了帧头之后再根据data_num判断要写入什么值,所以在GET_DATA_NUM和GET_DATA状态下根据data_num判断写入。

但是在cur_state ==GET_DATA_NUM && data_num == 16'd0cur_state ==GET_DATA && data_num == 16'd0赋的值不同,前者是rx_fifo_rdata后者是check_sum,如何判断?区别就在于cur_state,因此可以重写为

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wdata_r <= 16'd0;
	else if(nxt_state == GET_HEAD) begin
			if(rx_fifo_rdata == 16'hABCD && rx_fifo_rdata_val)							
				tx_fifo_wdata_r <= rx_fifo_rdata;
		end
	else if(rx_fifo_rdata_val) begin
			if(data_num == 16'd4)
				tx_fifo_wdata_r <= work_mode;
			else if(data_num == 16'd6)
				tx_fifo_wdata_r <= compensation_num;
			else if(|data_num && cur_state[0])
				tx_fifo_wdata_r <= check_sum;
			else
				tx_fifo_wdata_r <= rx_fifo_rdata;
		end
end

实际上,tx_fifo_wdata能否写入取决于tx_fifo_wr_en,所以我们只需控制tx_fifo_wr_en什么写入即可,因此上述代码还可以继续简化

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wdata_r <= 16'd0;
	else if(data_num == 16'd4)
			tx_fifo_wdata_r <= work_mode;
	else if(data_num == 16'd6)
		tx_fifo_wdata_r <= compensation_num;
	else if(data_num_zero && &cur_state)
		tx_fifo_wdata_r <= check_sum;
	else
		tx_fifo_wdata_r <= rx_fifo_rdata;
end

6.2. check_sum

校验和如果初始化为0时,那么就可以在GET_DATA_NUM和GET_DATA两个状态中均进行check_sum <= check_sum + rx_fifo_rdata[7:0];,所以重写为

/*
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		check_sum <= 8'd0;
	else if(cur_state == GET_DATA_NUM && rx_fifo_rdata_val)							
		check_sum <= rx_fifo_rdata[7:0];
	else if(nxt_state == GET_DATA && (|data_num) && rx_fifo_rdata_val)
		check_sum <= check_sum + rx_fifo_rdata[7:0];
end
*/
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		check_sum <= 8'd0;
	else if(cur_state == GET_HEAD)							
		check_sum <= 8'd0;
	else if(rx_fifo_rdata_val)							
		check_sum <= check_sum + rx_fifo_rdata[7:0];
end

6.3. data_num

这个也很简单,在data_num == 16'd0时读出的数据就是数据长度,此时赋给data_num即可,否则data_num减1

/*
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		data_num <= 16'd0;
	else if(cur_state == GET_DATA_NUM && rx_fifo_rdata_val)							
		data_num <= rx_fifo_rdata;
	else if(nxt_state == GET_DATA && rx_fifo_rd_en && !rx_fifo_empty)
		data_num <= data_num - 16'd1;
end
*/

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		data_num <= 16'd0;
	else if(nxt_state[1]) begin
			if(data_num_zero && rx_fifo_rdata_val)							
				data_num <= rx_fifo_rdata;
			else if(rx_fifo_rd_en && !rx_fifo_empty)
				data_num <= data_num - 16'd1;
	end
end

抛去rstn

原逻辑消耗 9与门、2MUX、1非门、2触发器、1减法器

新逻辑消耗 2与门、3MUX、1非门、2触发器、1减法器,新逻辑比旧逻辑少了5与门、多了1非门1或门。

6.4. rx_fifo_rd_en

这个太需要优化了。。。。。

/*

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rd_en_r <= 1'b0;
	else if(cur_state == IDLE && en_trans)
		rx_fifo_rd_en_r <= 1'b1;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rd_en) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
			else if(rx_fifo_rdata_val) begin
					if(rx_fifo_rdata == 16'hABCD)
						rx_fifo_rd_en_r <= 1'b0;
					else
						rx_fifo_rd_en_r <= 1'b1;
		end
	else if(cur_state == GET_DATA_NUM) begin
			if(rx_fifo_rd_en) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
			else if(tx_fifo_wr_done)
					rx_fifo_rd_en_r <= 1'b1;
		end
	else if(cur_state == GET_DATA) begin
			if(rx_fifo_rd_en) begin
				if(rx_fifo_empty)
					rx_fifo_rd_en_r <= 1'b1;
				else
					rx_fifo_rd_en_r <= 1'b0;
			end
			else if(tx_fifo_wr_done) begin
					if(!data_num_zero)
						rx_fifo_rd_en_r <= 1'b1;
					else
						rx_fifo_rd_en_r <= 1'b0;
			end
		end	
end
*/

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rd_en_r <= 1'b0;
	else if(cur_state == IDLE && en_trans)
		rx_fifo_rd_en_r <= 1'b1;
	else if(cur_state == GET_HEAD) begin
			if(rx_fifo_rd_en) 
				rx_fifo_rd_en_r <= rx_fifo_empty;
			else if(rx_fifo_rdata_val) 
				rx_fifo_rd_en_r <= !(rx_fifo_rdata == 16'hABCD);
			else
				rx_fifo_rd_en_r <= tx_fifo_wr_done;
		end
	else if(rx_fifo_rd_en) 
		rx_fifo_rd_en_r <= rx_fifo_empty;
	else if(tx_fifo_wr_done) 
		rx_fifo_rd_en_r <= !data_num_zero;
end

这里面涉及到一个转换

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		a <= 1'b0;
	else if(condi)
		a <= 1'b1;
	else
		a <= 1'b0;
end
//``````````````````````````````````````````````````````````
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		a <= 1'b0;
	else
		a <= condi;
end

除去rstn,前者需要MUX而后者则不需要MUX

6.5 代码

主要是消除嵌套if,可通过真值表的方法消除,优化后代码如下

module	frame_combination_v1(
	input			clk,
	input			rstn,
	
	input	[15:0]	rx_fifo_rdata,
	input			rx_fifo_rdata_val,
	input			rx_fifo_empty,
	output			rx_fifo_rd_en,
	
	output	[15:0]	tx_fifo_wdata,
	output			tx_fifo_wr_en,
	input			tx_fifo_full,	
	
	input			en_trans,
	input	[15:0]	work_mode,
	input	[15:0]	compensation_num
);

localparam	IDLE 			= 2'b00;
localparam	GET_HEAD 		= 2'b01;
localparam	GET_DATA_NUM 	= 2'b10;
localparam	GET_DATA 		= 2'b11;

reg		[1:0]		cur_state;
reg		[1:0]		nxt_state;
reg		[2:0]		data_num;
reg		[7:0]		check_sum;
reg					rx_fifo_rd_en_r;
reg					rx_fifo_rdata_val_dly;
reg		[15:0]		tx_fifo_wdata_r;
reg					tx_fifo_wr_en_r;
wire				rx_fifo_rd_head;						//帧头被读出
wire				rx_fifo_rd_head_GET_HEAD;				
//wire				tx_fifo_wr_done;
wire				rx_fifo_rd_done;						//下一拍读出有效数据
wire				rd_wr_ppl_done;							//读写并行流水时状态转移条件
wire				data_num_zero;

assign rx_fifo_rd_head = (rx_fifo_rdata == 16'hABCD) && rx_fifo_rdata_val;
assign rx_fifo_rd_head_GET_HEAD = (cur_state == GET_HEAD) && rx_fifo_rd_head;
assign rx_fifo_rd_done = rx_fifo_rd_en && !rx_fifo_empty;
assign rd_wr_ppl_done = rx_fifo_rdata_val_dly && !tx_fifo_wr_en;
//assign tx_fifo_wr_done = tx_fifo_wr_en && !tx_fifo_full;
assign data_num_zero = ~(|data_num);

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		cur_state <= IDLE;
	else
		cur_state <= nxt_state;
end

always@(*) begin
	case(cur_state)
	
		IDLE:
			if(en_trans)
				nxt_state = GET_HEAD;
			else
				nxt_state = IDLE;
				
		GET_HEAD:
			if(rx_fifo_rd_head)
				nxt_state = GET_DATA_NUM;
			else
				nxt_state = GET_HEAD;
				
		GET_DATA_NUM:
			if(rd_wr_ppl_done)
				nxt_state = GET_DATA;
			else
				nxt_state = GET_DATA_NUM;
		
		GET_DATA:
			if(data_num_zero && !rx_fifo_rdata_val_dly && !tx_fifo_wr_en)
				nxt_state = IDLE;
			else
				nxt_state = GET_DATA;
				
		default:	nxt_state = IDLE;
	endcase
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		data_num <= 3'd0;
	else if(cur_state == GET_DATA_NUM && rd_wr_ppl_done) 
		data_num <= rx_fifo_rdata[2:0] + 3'd1;
	else if(&cur_state && rx_fifo_rd_done) 
		data_num <= data_num - 3'd1;
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		check_sum <= 8'd0;
	else if(cur_state == GET_HEAD)							
		check_sum <= 8'd0;
	else if(cur_state[1] && rd_wr_ppl_done) begin				// (cur_state == GET_DATA_NUM || cur_state == GET_DATA) && rd_wr_ppl_done
			case(data_num)
				3'd2:	check_sum <= check_sum + compensation_num;
				3'd4:	check_sum <= check_sum + work_mode;
				default:	check_sum <= check_sum + rx_fifo_rdata[7:0];
			endcase
		end
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rdata_val_dly <= 1'b0;
	else if(rx_fifo_rd_head_GET_HEAD)
		rx_fifo_rdata_val_dly <= rx_fifo_rd_done;
	else if(cur_state[1]) begin
			if(rx_fifo_rdata_val_dly) 
				rx_fifo_rdata_val_dly <= tx_fifo_wr_en;
			else if(rx_fifo_rd_done)
				rx_fifo_rdata_val_dly <= 1'b1;
		end
end

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rx_fifo_rd_en_r <= 1'b0;
	else if(cur_state == IDLE && en_trans)
		rx_fifo_rd_en_r <= 1'b1;
	else if(rx_fifo_rd_head_GET_HEAD) 
			rx_fifo_rd_en_r <= rx_fifo_empty;
	else if(cur_state[1]) begin
			if(rx_fifo_rd_en_r) 
				rx_fifo_rd_en_r <= rx_fifo_empty;
			else if(rd_wr_ppl_done)
				rx_fifo_rd_en_r <= (!cur_state[0]) || (cur_state[0] && !data_num_zero);
		end
end

assign rx_fifo_rd_en = rx_fifo_rd_en_r;

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wdata_r <= 16'd0;
	else if(rx_fifo_rd_head_GET_HEAD || rd_wr_ppl_done) begin
		case(data_num)
			3'd2:	tx_fifo_wdata_r <= compensation_num;
			3'd4:	tx_fifo_wdata_r <= work_mode;
			3'd0:	tx_fifo_wdata_r <= (&cur_state)?check_sum:rx_fifo_rdata;
			default:	tx_fifo_wdata_r <= rx_fifo_rdata;
		endcase
	end
end

assign tx_fifo_wdata = tx_fifo_wdata_r;

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		tx_fifo_wr_en_r <= 1'b0;
	else if(rx_fifo_rd_head_GET_HEAD || rd_wr_ppl_done)  
		tx_fifo_wr_en_r <= 1'b1;
	else if(tx_fifo_wr_en_r)
		tx_fifo_wr_en_r <= tx_fifo_full;
end

assign tx_fifo_wr_en = tx_fifo_wr_en_r;

endmodule	

6.5. rx_fifo_rd_en的时序违规

本模块组合逻辑较多,很有可能会出现时序违规,如果出现了时序违规该如何处理呢?

最终代码里rx_fifo_rd_en这个信号经过的组合逻辑有点长,部分代码如下

...
assign rd_wr_ppl_done = rx_fifo_rdata_val_dly && !tx_fifo_wr_en;
assign data_num_zero = ~(|data_num);
...
always@(posedge clk or negedge rstn) begin
	...
	else if(cur_state[1]) begin
			if(rx_fifo_rd_en_r) 
				rx_fifo_rd_en_r <= rx_fifo_empty;
			else if(rd_wr_ppl_done)
				rx_fifo_rd_en_r <= (!cur_state[0]) || (cur_state[0] && !data_num_zero);
		end
end

使用vivado综合出来就成了

在这里插入图片描述
从图中可见,从data_numrx_fifo_rd_en经过的组合逻辑最多,这条路径也最危险,一旦出现了建立时间违规,该如何处理呢?

解决办法很简单,插入触发器

但是插入触发器会影响之前逻辑设计的时序,这种影响是未知的,所以需要仿真,判断是否需要对其他时序进行修正,代码就变成这样

...
reg		rx_fifo_rd_en_ctrl; 
...
assign rd_wr_ppl_done = rx_fifo_rdata_val_dly && !tx_fifo_wr_en;
assign data_num_zero = ~(|data_num);
...
always@(posedge clk or negedge rstn) begin
	...
	else if(cur_state[1]) begin
			if(rx_fifo_rd_en_r) 
				rx_fifo_rd_en_r <= rx_fifo_empty;
			else if(rd_wr_ppl_done)
				rx_fifo_rd_en_r <= rx_fifo_rd_en_ctrl ;
		end
end

always@(posedge clk or negedge rstn) begin
	...
	rx_fifo_rd_en_ctrl <= (!cur_state[0]) || (cur_state[0] && !data_num_zero);
end

经过仿真时序就变成

在这里插入图片描述

从仿真波形中可以看出时序图依然是正确的,分析如下:

cur_state == 2'b10时:该状态要完成写帧头,所以只要在写完成前rx_fifo_rd_en_ctrl为高即可,允许慢一拍

cur_state == 2'b11 && data_num == 3'd0时:此时校验和已经读出,rx_fifo_rd_en不能再读了,所以只要在写完成前rx_fifo_rd_en_ctrl为低即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Starry丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值