| ||
定义:用于检测一段固定序列的单元,当输入符合这串序列直接拉高输出。
分类:主要采用状态机实现,也有采用移位寄存器+比较器的方案。
原理:以重复检测序列“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状态。
照着三段式状态机设计,参考代码如下。
verilog;toolbar:false">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