| ||
定义:用于检测一段固定序列的单元,当输入符合这串序列直接拉高输出。
分类:主要采用状态机实现,也有采用移位寄存器+比较器的方案。
原理:以重复检测序列“101”为例,其本质是一个拥有4个状态的Moore状态机或拥有3个状态的Mealy状态机。(Moore状态机定义为输出仅与当前状态有关的状态机;Mealy状态机定义为输出既与当前状态有关,又与输入有关的状态机。)
Moore状态机:设置4个状态。
状态IDLE:初始状态,检测“101”的第1个“1”。输入为0,则下一状态仍为IDLE;若为1,则跳转至T1状态。
状态T1:检测“101”中的第1个“0”。输入为0,则跳转至T2状态;若为1,则下一状态仍为T1。
状态T2:检测“101”中的第2个“1”。输入为0,则为“100”,破坏了全部序列,跳转至IDLE状态;若为1,则跳转至ATV状态。
状态ATV:输出为1。若输入为0,则跳转至IDLE状态;若输入为1,则跳转至T1状态。

照着三段式状态机设计,参考代码如下。
module sequence_detector_101_moore ( //input clk , rstn , dat_i , //output active_o ); // ------------------ PARAM --------------------- localparam FSM_WD = 'd2; localparam IDLE = 2'd0; localparam T_1 = 2'd1; localparam T_2 = 2'd2; localparam ATV = 2'd3; // ------------------ IO ----------------------- input clk; input rstn; input dat_i; output reg active_o; // ------------------ WIRE --------------------- // ------------------ REG ---------------------- reg [FSM_WD -1 :0] cur_state_r ; reg [FSM_WD -1 :0] nxt_state_r ; // ------------------ GVAR --------------------- // ---------------- MAIN BODY ------------------ //--- FSM ---- // cur_state_r always @(posedge clk or negedge rstn ) begin if( !rstn ) begin cur_state_r <= IDLE ; end else begin cur_state_r <= nxt_state_r ; end end // nxt_state_r always @(*) begin nxt_state_r = IDLE ; case( cur_state_r ) IDLE : nxt_state_r = dat_i ? T_1 : IDLE ; T_1 : nxt_state_r = dat_i ? T_1 : T_2 ; T_2 : nxt_state_r = dat_i ? ATV : IDLE ; ATV : nxt_state_r = dat_i ? T_1 : IDLE ; default : nxt_state_r = IDLE ; endcase end //--- OUTPUT ---- always @(*) begin if( cur_state_r == ATV ) active_o = 'd1; else active_o = 'd0; end endmodule
Mealy状态机:设置3个状态。将状态ATV取消,然后若状态T2输入为1,则令输出为1,跳转至IDLE状态;其他情况下输出均为0。状态图略。代码可类似设计,如下:
module sequence_detector_101_mealy ( //input clk , rstn , dat_i , //output active_o ); // ------------------ PARAM --------------------- localparam FSM_WD = 'd2; localparam IDLE = 2'd0; localparam T_1 = 2'd1; localparam T_2 = 2'd2; // ------------------ IO ----------------------- input clk; input rstn; input dat_i; output reg active_o; // ------------------ WIRE --------------------- // ------------------ REG ---------------------- reg [FSM_WD -1 :0] cur_state_r ; reg [FSM_WD -1 :0] nxt_state_r ; // ------------------ GVAR --------------------- // ---------------- MAIN BODY ------------------ //--- FSM ---- // cur_state_r always @(posedge clk or negedge rstn ) begin if( !rstn ) begin cur_state_r <= IDLE ; end else begin cur_state_r <= nxt_state_r ; end end // nxt_state_r always @(*) begin nxt_state_r = IDLE ; case( cur_state_r ) IDLE : nxt_state_r = dat_i ? T_1 : IDLE ; T_1 : nxt_state_r = dat_i ? T_1 : T_2 ; T_2 : nxt_state_r = IDLE ; default : nxt_state_r = IDLE ; endcase end //--- OUTPUT ---- always @(*) begin if( cur_state_r == T_2 ) active_o = dat_i; else active_o = 'd0; end endmodule
代码的一些注意事项:
有限状态机需要定义cur_state_r和nxt_state_r,前一个是时序固定写法,即!rstn重置,否则cur<=nxt;
nxt则是组合逻辑,敏感列表是*,不受!rstn控制,里面只有一个case(cur)。为了避免综合出latch,case需要存在default分支。
移位寄存器:简单向左移位就可以,同时与待测序列比较。
module sequence_detector_101_shift (
//input
clk ,
rstn ,
dat_i ,
//output
active_o
);
// ------------------ PARAM ---------------------
localparam SEQ_WD = 'd3;
localparam TARGET = 3'b101;
// ------------------ IO -----------------------
input clk;
input rstn;
input dat_i;
output reg active_o;
// ------------------ WIRE ---------------------
// ------------------ REG ----------------------
reg [SEQ_WD -1 :0] shift_r ;
// ------------------ GVAR ---------------------
// ---------------- MAIN BODY ------------------
// shift_r
always @(posedge clk or negedge rstn ) begin
if( !rstn ) begin
shift_r <= 'd0 ;
end
else begin
if(active_o)
shift_r <= {{SEQ_WD{1'b0}}, dat_i};
else
shift_r <= {shift_r[SEQ_WD - 'd1:0], dat_i} ;
end
end
//--- OUTPUT ----
always @(*) begin
if( shift_r == TARGET )
active_o = 'd1;
else
active_o = 'd0;
end
endmodule顺便练一下Python生成测试向量,写的很dumb,很多地方可以搞点格式化输出的:
import numpy as np
a = open("./data.txt", "w")
b = open("./ans.txt", "w")
b.write("0\n")
for i in range(0, 100):
n = np.random.randint(low=0, high=8)
n_b = bin(n)[2:]
if len(n_b) == 1:
n_b = "0\n0\n" + n_b + "\n"
elif len(n_b) == 2:
n_b = "0\n" + n_b[0] + "\n" + n_b[1] + "\n"
else:
n_b = n_b[0] + "\n" + n_b[1] + "\n" + n_b[2] + "\n"
a.write(n_b)
a.close()
c = open("./data.txt", "r")
ck = c.readlines()
TAL = 0
for lines in ck:
TAL = (TAL * 2 + int(lines[0])) % 8
if TAL == 5:
b.write("1\n")
TAL = 0
else:
b.write("0\n")祖传testbench:
`timescale 1ns / 1ns
module tst_ripe_code;
// ------------------ IO -----------------------
reg clk;
reg rstn;
reg dat_i;
wire active_o;
// ------------------ WIRE ---------------------
// ------------------ REG ----------------------
reg result_soft;
// ------------------ GVAR ---------------------
// ------------------ INTEGAR ---------------------
integer fa;
integer fo;
integer flog_i;
integer flog_o;
integer ret;
integer tmp;
// ---------------- MAIN BODY ------------------
always #5 clk = ~clk;
initial begin
rstn = 'd0;
clk = 'd0;
dat_i = 'd0;
fa = $fopen("./data/data.txt", "r");
fo = $fopen("./data/ans.txt", "r");
flog_i = $fopen("./data/log_data.txt", "w");
flog_o = $fopen("./data/log_ans.txt", "w");
#10 rstn = 'd1;
end
always @(negedge clk) begin
if(rstn) begin
ret = $fscanf(fa, "%b", dat_i);
$fdisplay(flog_i, "%8d : %10d", $time, dat_i);
end
end
always @(negedge clk) begin
if(rstn) begin
tmp = $fscanf(fo, "%b", result_soft);
$fdisplay(flog_o, "%8d : %10d", $time, active_o);
if(active_o != result_soft)
$display("at %08d ns, %10d --> %10d", $time, result_soft, active_o);
end
end
// ------------------ INST ---------------------
sequence_detector_101_moore test_module(
// input
.clk (clk ),
.rstn (rstn ),
.dat_i (dat_i ),
// output
.active_o (active_o )
);
endmodule
/2