| ||
定义:顾名思义,就是稳定发送一串序列出去。
分类:状态机、移位寄存器、移位寄存器+组合逻辑、计数器+组合逻辑
原理:
1.状态机
这个就不仔细说了,采用Moore状态机,寻找最短循环节,然后针对最短循环节中的每一位设置一个状态,再加上一个IDLE状态即可完成设计(其实完全用不上这个IDLE状态,不过如果想做控制逻辑,即需要加入使能信号,则应该尽量加上IDLE状态)。以下代码以生成“101011”为例。
verilog;toolbar:false">module sequence_generator_fsm ( //input clk , rstn , //output seq_o ); // ------------------ PARAM --------------------- localparam FSM_WD = 'd3; localparam IDLE = 3'd0; localparam T_1 = 3'd1; localparam T_2 = 3'd2; localparam T_3 = 3'd3; localparam T_4 = 3'd4; localparam T_5 = 3'd5; localparam T_6 = 3'd6; // ------------------ IO ----------------------- input clk; input rstn; output reg seq_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 ) cur_state_r <= T_1; else cur_state_r <= nxt_state_r; end // nxt_state_r always @(*) begin nxt_state_r = IDLE; case( cur_state_r ) IDLE: nxt_state_r = T_1; T_1 : nxt_state_r = T_2; T_2 : nxt_state_r = T_3; T_3 : nxt_state_r = T_4; T_4 : nxt_state_r = T_5; T_5 : nxt_state_r = T_6; T_6 : nxt_state_r = T_1; default : nxt_state_r = IDLE; endcase end //--- output ---- always @(*) begin case( cur_state_r ) IDLE: seq_o = 'd0; T_1 : seq_o = 'd1; T_2 : seq_o = 'd0; T_3 : seq_o = 'd1; T_4 : seq_o = 'd0; T_5 : seq_o = 'd1; T_6 : seq_o = 'd1; default : seq_o = 'd0; endcase end endmodule
2.移位寄存器
这个也是先寻找最短循环节,假设其有N位,则移位寄存器也设置为N位。然后在清零时写入待生成的序列,之后即可使用向左/向右移位完成生成序列的目的。注意这个逻辑同样会被应用于并转串模块的逻辑。
module sequence_generator_shift ( //input clk , rstn , //output seq_o ); // ------------------ PARAM --------------------- localparam SFT_WD = 'd6; // ------------------ IO ----------------------- input clk; input rstn; output seq_o; // ------------------ WIRE --------------------- // ------------------ REG ---------------------- reg [SFT_WD -1 :0] shift_r ; // ------------------ GVAR --------------------- // ---------------- MAIN BODY ------------------ always @(posedge clk or negedge rstn) begin if( !rstn ) shift_r <= 6'b101011; else shift_r <= {shift_r[SFT_WD-2:0], shift_r[SFT_WD-1]}; end assign seq_o = shift_r[SFT_WD-1]; endmodule
3.移位寄存器+组合逻辑
这个是本科阶段数电课上的内容,使用组合逻辑换取移位寄存器长度的方法。其基本结构与2的移位寄存器类似,不过其需要使用后续移位寄存器输出来定义一个组合逻辑作为第一级的输入。此时经过证明,移位寄存器长度至少为N/2[请求来源],而这里如果长度取3,则会出现两个“101”状态同时对应着0和1,故应该使用4长度。还是以101011为例,将每位寄存器的输出Q视作其状态,作出其状态转换表:(F->Q0->Q1->Q2->Q3)
F(第一级输入) | Q0 | Q1 | Q2 | Q3(输出) |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 1 | 0 |
1 | 1 | 1 | 0 | 1 |
0 | 1 | 1 | 1 | 0 |
1 | 0 | 1 | 1 | 1 |
0 | 1 | 0 | 1 | 1 |
于是可以作出第一级输入的卡诺图:
Q3Q2\Q1Q0 | 00 | 01 | 11 | 10 |
00 | x | x | x | x |
01 | x | 1 | 0 | x |
11 | x | 0 | x | 1 |
10 | x | x | 1 | 1 |
其中x代表不关心的值。根据数电处理卡诺图的方法,可以知道F=Q3Q1+(~Q3)(~Q1)=~(Q3^Q1)(其中^代表异或逻辑,~代表非逻辑)。因此便可得到其结构,并可根据结构写出代码:
module sequence_generator_shift_comb ( //input clk , rstn , //output seq_o ); // ------------------ PARAM --------------------- localparam SFT_WD = 'd4; // ------------------ IO ----------------------- input clk; input rstn; output seq_o; // ------------------ WIRE --------------------- wire shift_w ; // ------------------ REG ---------------------- reg [SFT_WD -1 :0] shift_r ; // ------------------ GVAR --------------------- // ---------------- MAIN BODY ------------------ always @(posedge clk or negedge rstn) begin if( !rstn ) shift_r <= 4'b1010; else shift_r <= {shift_r[2:0], shift_w}; end assign shift_w = ~(shift_r[3] ^ shift_r[1]); assign seq_o = shift_r[3]; endmodule
注意在上面的代码中,初始化使用的是第一个状态Q3Q2Q1Q0=1010,其实使用任何一个状态转换表中的状态都可以自启成功。
4.计数器+组合逻辑
这个基本的逻辑也很直观,计数器会一直进行计数,其长度为ceil(log2(N))(ceil()代表向上取整)位,并根据计数器输出通过组合逻辑设置其最终输出。还是以101011为例,由于ceil(log2(6))=3,故需要三位计数器。计数器从0计数到5,然后清零计数器。其输出设置如下:
Q2 | Q1 | Q0 | Y |
0 | 0 | 0 | 1 |
0 | 0 | 1 | 0 |
0 | 1 | 0 | 1 |
0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 |
1 | 0 | 1 | 1 |
于是可以作出输出的卡诺图:
Q2\Q1Q0 | 00 | 01 | 11 | 10 |
0 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | x | x |
根据数电处理卡诺图的方法,可以知道Y=~(Q0)+Q2。因此便可得到其结构,并可根据结构写出代码:
module sequence_generator_count_comb ( //input clk , rstn , //output seq_o ); // ------------------ PARAM --------------------- localparam SFT_WD = 'd3; // ------------------ IO ----------------------- input clk; input rstn; output seq_o; // ------------------ WIRE --------------------- // ------------------ REG ---------------------- reg [SFT_WD -1 :0] cnt_r ; // ------------------ GVAR --------------------- // ---------------- MAIN BODY ------------------ always @(posedge clk or negedge rstn) begin if( !rstn ) cnt_r <= 'd0; else begin if( cnt_r == 'd5) cnt_r <= 'd0; else cnt_r <= cnt_r + 'd1; end end assign seq_o = (~cnt_r[0]) | cnt_r[2]; endmodule
仿真:
祖传testbench:
`timescale 1ns / 1ns module tst_ripe_code; // ------------------ IO ----------------------- reg clk; reg rstn; wire seq_w1_o; wire seq_w2_o; wire seq_w3_o; wire seq_w4_o; // ------------------ WIRE --------------------- // ------------------ REG ---------------------- // ------------------ GVAR --------------------- // ------------------ INTEGAR --------------------- // ---------------- MAIN BODY ------------------ always #5 clk = ~clk; initial begin rstn = 'd0; clk = 'd0; #10 rstn = 'd1; end // ------------------ INST --------------------- sequence_generator_fsm test_module_1( // input .clk (clk ), .rstn (rstn ), //output .seq_o (seq_w1_o ) ); sequence_generator_shift test_module_2( // input .clk (clk ), .rstn (rstn ), //output .seq_o (seq_w2_o ) ); sequence_generator_shift_comb test_module_3( // input .clk (clk ), .rstn (rstn ), //output .seq_o (seq_w3_o ) ); sequence_generator_count_comb test_module_4( // input .clk (clk ), .rstn (rstn ), //output .seq_o (seq_w4_o ) ); endmodule
仿真结果: