目录
这是本人在实际工作中遇到的设计任务,借此研究状态机设计的优化策略
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'd0
时rx_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'd0
和cur_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_num
到rx_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
为低即可。