西南交大EDA实验——Sequence Detector
一、实验内容及要求
1. 目的
设计一种能够根据配置模式检测四个一致性数据并输出标志的序列检测器。
·支持增量模式:可以检测到四个一致性递增的数据
·支持减少模式:可以检测到四个一致性递减的数据
·支持稳定模式:可以检测到四个一致性相同的数据
2. 说明
·文件名:SEQUENCE.v
·模块名:SEQ_DETECTOR
·I/O端口:
RST_B:位宽为1bit的输入。系统异步复位,低电平有效。
SYSCLK:位宽为1bit的输入。系统时钟
IN_VALID:位宽为1bit的输入。高电平有效。输入信号MODE和DATA_IN只有在IN_VALID = 1的时候有效。当IN_VALID = 0时,MODE和DATA_IN应被忽略,并且状态应与IN_VALID = 1时相同。
MODE:位宽为2bit的输入。检测模式,这个信号决定了检测模式。00:增量检测模式,其中四个一致性增长数据(如1,2,3,4或15,0,1,2)可以置OUT_VALID为逻辑1。01:递减检测模式,其中4个一致性递减数据(如5,4,3,2或0,15,14,13)可以置OUT_VALID为逻辑1。10:稳定检测模式,其中4个相同的数据(如0,0,0,0)可以置OUT_VALID为逻辑1。11:与IN_VALID=1作用相同。
DATA_IN:位宽为4bit的输入。输入数据,该信号只有在IN_VALID = 1并且MODE != 2’b11时有效。
OUT_VALID:位宽为1bit的输出。输出有效位,高电平有效。当根据检测模式接收到四个一致性数据是,该信号才生效。
DATA_OUT:位宽为4bit的输出。当OUT_VALID生效时,DATA_OUT应等于DATA_IN,其他情况都等于0。
3. 要求
4. 波形
二、内容讲解及代码编写
首先,先把顶层模块的输入和输出写上。
module SEQ_DETECTOR
(
input wire SYSCLK ,
input wire RST_B ,
input wire IN_VALID ,
input wire [1:0] MODE ,
input wire [3:0] DATA_IN ,
output reg OUT_VALID ,
output reg [3:0] DATA_OUT
);
endmodule
我们需要用到Mealy型状态机,先将需要的状态想好。初始状态S0,以增量模式(MODE=2’b00)为例,假设检测到第一个数0进入状态INCREMEN_STATE1,下一个检测到1进入状态INCREMENT_STATE2,再检测到2进入状态INCREMENT_STATE3,此时已经检测到3个增量,这时如果再检测到3,就要输出OUT_VALID = 1和DATA_OUT,同时保持状态在INCREMENT_STATE3,因为1,2,3又是三个连续递增的序列,下一个数据如果是4,也可以构成一个递增的序列1,2,3,4。类似,我们还需要减少模式的状态DECREMENT_STATE1 、DECREMENT_STATE2 、DECREMENT_STATE3和稳定模式的状态EQUAL_STATE1、EQUAL_STATE2、 EQUAL_STATE3。因此我们共需要10个状态,对这10个状态进行编码:
parameter S0 = 4'd0 ,
INCREMEN_STATE1 = 4'd1 ,
INCREMEN_STATE2 = 4'd2 ,
INCREMEN_STATE3 = 4'd3 ,
DECREMENT_STATE1 = 4'd4 ,
DECREMENT_STATE2 = 4'd5 ,
DECREMENT_STATE3 = 4'd6 ,
EQUAL_STATE1 = 4'd7 ,
EQUAL_STATE2 = 4'd8 ,
EQUAL_STATE3 = 4'd9 ;
对状态进行编码后,我们就要考虑状态的转换了,状态转换就需要判断输入的序列是递增、递减还是相等的。如何判断呢?因为使用时序逻辑赋值,会使数据慢一个周期,这样可以比较输入的数据了。
reg [3:0] data_in_delay;
always@(posedge SYSCLK, negedge RST_B)
begin
if(!RST_B)
data_in_delay <= 4'd0;
else if(IN_VALID && MODE != 4'd3)//IN_VALID = 0或MODE = 2'b11时,输入信号是无效的
data_in_delay <= DATA_IN;
else
data_in_delay <= data_in_reg;
end
考虑完如何判断输入的序列是递增、递减还是相等后,终于到了状态转换的部分了。
在前面,我们已经考虑了增量模式下,状态的转换,另外两个模式类似。但是,我们似乎漏掉了某些特殊的情况,比如说,开始时MODE = 2’b00(增量状态),在状态INCREMENT_STATE2时,MODE发生了改变,这时,下一状态应该什么呢?所以我们现在要考虑半途MODE变化的情况(MODE它叛变了)。继续刚才的例子,假如MODE在状态INCREMENT_STATE2时变成了2’b01,那么这时我们应该检测递减的序列,是不是要回到初始状态S0重新开始呢?不,这个时候,我们已经接受到了数据,我们应该把当前接受到的数据当做递减序列的第一个数,所以我们的状态应该是DECREMENT_STATE1。同理,MODE跳变为2’b00和2’b10对应着INCREMENT_STATE1和EQUAL_STATE1两个状态。所以,状态转换部分,我们应该这样写:
reg [3:0] state;
always@(posedge SYSCLK, negedge RST_B)
begin
if(!RST_B)
state <= S0 ;
else if(IN_VALID && MODE != 2'b11)
begin
case(state)
S0 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
INCREMEN_STATE1 :begin
if(MODE == 2'b00)
begin
if(DATA_IN == data_in_delay + 4'd1)
state <= INCREMEN_STATE2;
else
state <= S0;
end
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
INCREMEN_STATE2 :begin
if(MODE == 2'b00)
begin
if(DATA_IN == data_in_delay + 4'd1)
state <= INCREMEN_STATE3;
else
state <= S0;
end
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
INCREMEN_STATE3 :begin
if((MODE == 2'b00) && (DATA_IN == data_in_delay + 4'd1))
state <= INCREMEN_STATE3;
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
DECREMENT_STATE1 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
begin
if(DATA_IN + 4'd1 == data_in_delay)
state <= DECREMENT_STATE2;
else
state <= S0;
end
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
DECREMENT_STATE2 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
begin
if(DATA_IN + 4'd1 == data_in_delay)
state <= DECREMENT_STATE3;
else
state <= S0;
end
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
DECREMENT_STATE3 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if((DATA_IN + 1'b1 == data_in_delay) && (MODE == 2'b01))
state <= DECREMENT_STATE3;
else if(MODE == 2'b10)
state <= EQUAL_STATE1;
else
state <= S0;
end
EQUAL_STATE1 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
begin
if(DATA_IN == data_in_delay)
state <= EQUAL_STATE2;
else
state <= S0;
end
else
state <= S0;
end
EQUAL_STATE2 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if(MODE == 2'b10)
begin
if(DATA_IN == data_in_delay)
state <= EQUAL_STATE3;
else
state <= S0;
end
else
state <= S0;
end
EQUAL_STATE3 :begin
if(MODE == 2'b00)
state <= INCREMEN_STATE1;
else if(MODE == 2'b01)
state <= DECREMENT_STATE1;
else if((DATA_IN == data_in_delay) && (MODE == 2'b10))
state <= EQUAL_STATE3;
else
state <= S0;
end
default :state <= S0;
endcase
end
else
state <= state;
end
在这里,我们没有用到常用的state和next_state来进行状态的转移。因为作者最开始就是用的这种方法,结果仿真的时候发现,我们理想的情况下,state和next_state是每个周期变化一次,但是因为使用的是时序逻辑,而在时序逻辑了,state <= next_state, next_state是在state的前提进行转换。第一个周期内state = S0,next_state = S0,第二个周期内,next_state会在state = S0的条件下进行变换,next_state = INCREMENT_STATE1,第三个周期内,next_state还是会在state = S0的状态下进行变换,所以next_state = INCREMENT_STATE1,而state会在next_state = INCREMENT_STATE1的条件下变换,因为我们使用的时序逻辑。第四个周期内,next_state在state = INCREMENT_STATE1的条件下变换,next_state = INCREMENT_STATE2,state在next_state = INCREMENT_STATE1的条件下变换。这样,我们发现,stage和next_state从S0到INCREMENT_STATE1,从INCREMENT_STATE1到INCREMENT_STATE2都需要两个周期,这与我们想要的情况不符,所以我们不采用state和next_state这种方法进行状态转移。大家也可以自己去试一试这种情况。
OK!我们已经完成了最难的部分了,接下来就是输出信号了!
首先是OUT_VALID
在状态INCREMENT_STATE3时,如果再检测到一个递增的数,就输出OUT_VALID为高电平,其他检测模式类似。
always@(posedge SYSCLK, negedge RST_B)
begin
if(!RST_B)
OUT_VALID <= 1'b0;
else if(!IN_VALID || MODE == 2'b11)//IN_VALID = 0或者MODE = 2'b11时,无效
OUT_VALID <= 1'b0;
else
begin
case(state)
INCREMEN_STATE3 :begin
if((DATA_IN == data_in_delay + 1'b1) && (MODE == 2'b00))
OUT_VALID <= 1'b1;
else
OUT_VALID <= 1'b0;
end
DECREMENT_STATE3 :begin
if((DATA_IN + 1'b1 == data_in_delay) && (MODE == 2'b01))
OUT_VALID <= 1'b1;
else
OUT_VALID <= 1'b0;
end
EQUAL_STATE3 :begin
if((DATA_IN == data_in_delay) && (MODE == 2'b10))
OUT_VALID <= 1'b1;
else
OUT_VALID <= 1'b0;
end
default : OUT_VALID <= 1'b0;
endcase
end
end
最后是DATA_OUT了
当OUT_VALID生效时,DATA_OUT应等于DATA_IN,其他情况都等于0,因此DATA_OUT的逻辑与OUT_VALID相同
always@(posedge SYSCLK, negedge RST_B)
begin
if(!RST_B)
DATA_OUT <= 1'b0;
else if(!IN_VALID || MODE == 2'b11)//IN_VALID = 0或者MODE = 2'b11时,无效
DATA_OUT <= 1'b0;
else
begin
case(state)
INCREMEN_STATE3 :begin
if((DATA_IN == data_in_delay + 1'b1) && (MODE == 2'b00))
DATA_OUT <= DATA_IN;
else
DATA_OUT <= 1'b0;
end
DECREMENT_STATE3 :begin
if((DATA_IN + 1'b1 == data_in_delay) && (MODE == 2'b01))
DATA_OUT <= DATA_IN;
else
DATA_OUT <= 1'b0;
end
EQUAL_STATE3 :begin
if((DATA_IN == data_in_delay) && (MODE == 2'b10))
DATA_OUT <= DATA_IN;
else
DATA_OUT <= 1'b0;
end
default : DATA_OUT <= 1'b0;
endcase
end
end
这样,我们的RTL代码就写完了,编译没有报错后就可以写testbench了。
也可以看一下生成的状态机。
三、testbench
按照波形图编写就行
`timescale 1ns/1ns
module tb();
reg RST_B ;
reg SYSCLK ;
reg IN_VALID ;
reg [1:0] MODE ;
reg [3:0] DATA_IN ;
wire OUT_VALID ;
wire [3:0] DATA_OUT ;
initial
begin
RST_B <= 1'b0;
SYSCLK <= 1'b1;
IN_VALID <= 1'b0;
MODE <= 2'd0;
DATA_IN <= 4'd0;
#200
RST_B <= 1'b1;
#20 DATA_IN <= 4'd1;
#20 DATA_IN <= 4'd2;
#20 DATA_IN <= 4'd3;
#20 DATA_IN <= 4'd4;
#20 DATA_IN <= 4'd5;
#20 IN_VALID <= 1'b1;
DATA_IN <= 4'd6;
#20 DATA_IN <= 4'd7;
#20 DATA_IN <= 4'd8;
#20 DATA_IN <= 4'd9;
#20 DATA_IN <= 4'd9;
#20 IN_VALID <= 1'b0;
#20 IN_VALID <= 1'b1;
MODE <= 2'd1;
DATA_IN <= 4'd6;
#20 DATA_IN <= 4'd5;
#20 DATA_IN <= 4'd4;
#20 DATA_IN <= 4'd3;
#20 DATA_IN <= 4'd0;
#20 IN_VALID <= 1'b0;
#20 IN_VALID <= 1'b1;
MODE <= 2'd2;
DATA_IN <= 4'd11;
#80 DATA_IN <= 4'd0;
#20 IN_VALID <= 1'b0;
#20 IN_VALID <= 1'b1;
MODE <= 2'd0;
DATA_IN <= 4'd4;
#20 DATA_IN <= 4'd5;
#20 DATA_IN <= 4'd6;
#20 MODE <= 2'd1;
DATA_IN <= 4'd7;
#20 DATA_IN <= 4'd6;
#20 DATA_IN <= 4'd5;
#20 DATA_IN <= 4'd4;
#20 MODE <= 2'd0;
DATA_IN <= 4'd5;
#20 DATA_IN <= 4'd6;
#20 DATA_IN <= 4'd7;
#20 DATA_IN <= 4'd8;
#20 MODE <= 2'd2;
#80 IN_VALID <= 1'b0;
MODE <= 2'b0;
#80 DATA_IN <= 4'd0;
#20 IN_VALID <= 1'b1;
#20 IN_VALID <= 1'b0;
DATA_IN <= 4'd1;
#20 IN_VALID <= 1'b1;
#20 DATA_IN <= 4'd2;
IN_VALID <= 1'b0;
#20 DATA_IN <= 4'd3;
#20 DATA_IN <= 4'd2;
IN_VALID <= 1'b1;
#20 IN_VALID <= 1'b0;
MODE <= 2'd2;
#20 DATA_IN <= 4'd3;
MODE <= 2'd1;
#20 IN_VALID <= 1'b1;
MODE <= 2'd0;
#20 IN_VALID <= 1'b0;
DATA_IN <= 4'd1;
MODE <= 2'd1;
#20 MODE <= 2'd2;
DATA_IN <= 4'd2;
#20 IN_VALID <= 1'b1;
MODE <= 2'd0;
DATA_IN <= 4'd4;
#20 MODE <= 2'd2;
DATA_IN <= 4'd0;
#20 MODE <= 2'd0;
#20 MODE <= 2'd3;
DATA_IN <= 4'd1;
#20 MODE <= 2'd0;
#20 MODE <= 2'd3;
DATA_IN <= 4'd2;
#20 DATA_IN <= 4'd3;
#20 MODE <= 2'd0;
DATA_IN <= 4'd2;
#20 MODE <= 2'd3;
#20 DATA_IN <= 4'd3;
#20 MODE <= 2'd0;
#20 DATA_IN <= 4'd1;
end
always#10 SYSCLK <= ~SYSCLK;
SEQ_DETECTOR SEQ_DETECTOR_inst
(
.RST_B (RST_B),
.SYSCLK (SYSCLK),
.IN_VALID (IN_VALID),
.MODE (MODE),
.DATA_IN (DATA_IN),
.OUT_VALID (OUT_VALID),
.DATA_OUT (DATA_OUT)
);
endmodule
任务完成!
欢迎在评论区留言,感谢你的支持!!!