HDLBits(十 一)学习笔记——有限状态机(FSM onehot - Fsm serialdp)

FSM onehot

在这里插入图片描述
题目要求采用独热码的方式进行编写,其中state[0]和state[9]分别对应于S0和S9。除非另有指定,否则输出为零。并实现状态机的状态转换逻辑和输出逻辑部分,在状态[9:0]中,您将获得当前状态,并且必须产生next_state[9:0]和两个输出。
方法一:

module top_module(
    input in,
    input [9:0] state,
    output [9:0] next_state,
    output out1,
    output out2
);
  
    parameter S0 = 4'd0;
    parameter S1 = 4'd1;
    parameter S2 = 4'd2;
    parameter S3 = 4'd3;
    parameter S4 = 4'd4;
    parameter S5 = 4'd5;
    parameter S6 = 4'd6;
    parameter S7 = 4'd7;
    parameter S8 = 4'd8;
	parameter S9 = 4'd9;

   
    assign next_state[0] = ~in & (state[S0] | state[S1] | state[S2] | state[S3] | state[S4] | state[S7] | state[S8] | state[S9]);
    assign next_state[1] = in & (state[S0] | state[S8] | state[S9]);
    assign next_state[2] = in & state[S1];
    assign next_state[3] = in & state[S2];
    assign next_state[4] = in & state[S3];
    assign next_state[5] = in & state[S4];
    assign next_state[6] = in & state[S5];
    assign next_state[7] = in & (state[S6] | state[S7]);
    assign next_state[8] = ~in & state[S5];
    assign next_state[9] = ~in & state[S6];
    
    assign out1 = (state[S8] | state[S9]);
    assign out2 = (state[S7] | state[S9]);
  
endmodule

方法二:该方法逻辑正确,但时间上的仿真不能通过。因为题目给出采用 state[0] - state[9]分别对应于S0-S9的方式,无需比对每一位,仅对十位中的某一位进行状态分析,从而简化了表达,可节约时间。

module top_module(
    input in,
    input [9:0] state,
    output [9:0] next_state,
    output out1,
    output out2
);
//独热码
parameter S0 = 10'b0000000001;
parameter S1 = 10'b0000000010;
parameter S2 = 10'b0000000100;
parameter S3 = 10'b0000001000;
parameter S4 = 10'b0000010000;
parameter S5 = 10'b0000100000;
parameter S6 = 10'b0001000000;
parameter S7 = 10'b0010000000;
parameter S8 = 10'b0100000000;
parameter S9 = 10'b1000000000;
//第二段 组合逻辑进行状态跳转
        always @ (*)begin
            case(state)
                S0:begin
                    if(in == 1'b0)
                        next_state = S0;
                    else
                      next_state = S1;  
                end
                S1:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S2;  
                end
                S2:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S3;  
                end
                S3:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S4;  
                end
                S4:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S5;  
                end  
                S5:begin
                    if(in == 1'b0)
                      next_state = S8;
                    else
                      next_state = S6;  
                end
                S6:begin
                    if(in == 1'b0)
                      next_state = S9;
                    else
                      next_state = S7;  
                end
                S7:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S7;  
                end
                S8:begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S1;  
                end
                S9: begin
                    if(in == 1'b0)
                      next_state = S0;
                    else
                      next_state = S1;  
                end
                default:next_state = 10'b0;
          
            endcase

    end
//第三段 描述输出
    assign out1 = (state == S8 | state == S9) ? 1:0;
    assign out2 = (state == S7 |state == S9) ? 1:0;

Fsm ps2

PS/2 鼠标协议发送长度为 3 个字节的消息。但是,在连续字节流中,消息的开始和结束位置并不明显。唯一的指示是,每个三字节消息的第一个字节始终具有 bit[3]=1(但其他两个字节的位 [3] 可能是 1 或 0,具体取决于数据)。

我们想要一个有限的状态机,当给定输入字节流时,它将搜索消息边界。我们将使用的算法是丢弃字节,直到我们看到一个位[3]=1的字节。然后,我们假设这是消息的字节 1,并在收到所有 3 个字节(完成)后发出接收消息的信号。

FSM 应在成功接收到每条消息的第三个字节后立即在周期内发出信号。在这里插入图片描述
题目分析:
该题是对接收的每三个字节产生一个完成信号。字节流是连续的,因此只需要检测边界,即可判断出三个字节的接收,题目中第一个字节更好判断始终为1,而另外两个字节可能为1也可能为0,因此我们就以检测第一个字节作为边界。设置一个等待状态,只要检测到1,那么即可跳转到第一个字节,紧接着依次接收,直到第三个字节完成,否则就一直等待。在这里插入图片描述

module top_module(
    input clk,
    input [7:0] in,
    input reset,    // Synchronous reset
    output done
); 
    reg [3:0] state;
    reg [3:0] next_state;

    parameter BYTE_FIRST = 4'b0001; //第1-3个字节
    parameter BYTE_SECOND = 4'b0010; 
    parameter BYTE_THIRD = 4'b0100; 
    parameter WAIT = 4'b1000; //等待状态
   
//第一段 用时序逻辑描述状态寄存器
    always @ (posedge clk or posedge reset)begin
        if(reset)
            state <= WAIT;
        else
           state <= next_state; 
    end
//第二段 用组合逻辑描述状态转换
    always @ (*)begin
        case(state)
            BYTE_FIRST:begin
                next_state = BYTE_SECOND;
            end
            BYTE_SECOND:begin
                next_state = BYTE_THIRD;
            end
            BYTE_THIRD:begin
                if(in[3]==1'b1)
                    next_state = BYTE_FIRST;
                else
                    next_state = WAIT;
            end
            WAIT: begin
                if(in[3]==1'b1)
                    next_state = BYTE_FIRST;
                else
                    next_state = WAIT;
            end   
            default:next_state = WAIT;
        endcase
    end

//第三段 用组合逻辑描述输出
    assign done = (state == BYTE_THIRD);
endmodule

Fsm ps2data

根据上题,现在,您已经有了一个状态机,它将识别 PS/2 字节流中的三字节消息,请添加一个数据路径,该数据路径还将在收到数据包输出 24 位(3 字节)消息(out_bytes[23:16] 是第一个字节,out_bytes[15:8] 是第二个字节,依此类推)。
done信号时,out_bytes都需要有效。您可以在其他时间输出任何内容。在这里插入图片描述

module top_module(
    input clk,
    input [7:0] in,
    input reset,    // Synchronous reset
    output [23:0] out_bytes,
    output done
); 
    reg [3:0] state;
    reg [3:0] next_state;
    reg [23:0] out_bytes_reg;

    parameter BYTE_FIRST = 4'b0001; //第1-3个字节
    parameter BYTE_SECOND = 4'b0010; 
    parameter BYTE_THIRD = 4'b0100; 
    parameter DONE = 4'b1000; //完成状态
   
//第一段 用时序逻辑描述状态寄存器
    always @ (posedge clk or posedge reset)begin
        if(reset)
            state <= BYTE_FIRST;
        else
           state <= next_state; 
    end
//第二段 用组合逻辑描述状态转换
    always @ (*)begin
        case(state)
            BYTE_FIRST:begin
                if(in[3]==1'b1)
                    next_state = BYTE_SECOND;
                else
                    next_state = BYTE_FIRST;
            end
            BYTE_SECOND:begin
                next_state = BYTE_THIRD;
            end
            BYTE_THIRD:begin
                    next_state = DONE;
            end
            DONE: begin
                if(in[3]==1'b1)
                    next_state = BYTE_SECOND;
                else
                    next_state = BYTE_FIRST;
            end   
            default:next_state = BYTE_FIRST;
        endcase
    end

//第三段 用组合逻辑描述输出
    assign done = (state == DONE);

//输出24位数据
    always @ (posedge clk)begin
        if(next_state == BYTE_SECOND)
            out_bytes_reg[23:16] <= in;
        else if(next_state == BYTE_THIRD)
            out_bytes_reg[15:8] <= in;
        else if (next_state == DONE)
            out_bytes_reg[7:0] <= in;
        else
            out_bytes_reg <= 24'd0;
 end

    assign out_bytes = out_bytes_reg;

endmodule

Fsm serial(不需要将数据输出)

在许多(较旧的)串行通信协议中,每个数据字节都与一个起始位和一个停止位一起发送,以帮助接收方将字节与位流分隔开来。一种常见的方案是使用一个起始位 (0)、8 个数据位和 1 个停止位 (1)。当没有任何内容被传输(空闲)时,该行也位于逻辑 1 处。

设计一个有限的状态机,该状态机将在给定位流时识别何时正确接收字节。它需要识别起始位,等待所有8个数据位,然后验证停止位是否正确。**如果停止位未按预期出现,**则 FSM 必须等到找到停止位后再尝试接收下一个字节。
在这里插入图片描述
题目分析:该题有五个状态,空闲状态、开始状态、数据状态、停止状态和等待状态。其中空闲状态的时候根据输入in信号来判断,如果in=0,说明进入开始状态,否则仍然是空闲状态,从开始状态到数据状态,数据状态中需要特别注意,这里其实包含0-7 八位数据的接收,采用计数的方式来进行8个数据位的依次接收,当接收完成后仍然根据in信号,判断是否进入停止状态。如果数据位接收完成,但是输入停止位信号一直没过来,那就进去等待状态。
在这里插入图片描述

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output done
); 
 	parameter IDLE = 5'd00001;
    parameter START= 5'd00010;
    parameter DATA = 5'd00100;
    parameter STOP = 5'd01000;
    parameter WAIT = 5'd10000;
    
    reg [4:0]	state;
    reg [4:0]	next_state;
    reg [3:0]	count;

        
// 第一段 状态寄存器
    always @(posedge clk)begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
//第二段 组合逻辑描述状态转移
    always @(*)begin
        case(state)
            IDLE :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            START:begin
                next_state = DATA;
            end
            DATA :begin
                if(count == 4'd7)begin //计数8次,说明8个数据接收完成
                    if(in == 1'b1) //出现停止位
                        next_state = STOP;
                    else
                        next_state = WAIT; 
                end
                else
                       next_state = DATA;
            end
            STOP :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            WAIT :begin
                if(in == 1'b0)
                    next_state = WAIT;
                else
                    next_state = IDLE;
            end
            default:begin 
                next_state = IDLE;   
            end
        endcase
    end
//第三段 组合逻辑描述输出
    always @(*)begin
        if(reset)
            done = 1'b0;
        else if(state == STOP)
            done = 1'b1;
        else
            done = 1'b0;
    end
    
    
 //计数器设计
    always @(posedge clk)begin
        if(reset)
            count <= 4'b0;
        else if(state == DATA)begin
            count <= count + 1'b1;
        end
        else
            count <= 4'b0;
    end

endmodule

由此可看到在数据状态比较复杂,需要设计计数器,进行八次的计数,来确定八个数据位的接收,

Fsm serialdata 状态机串行数据

该题是在上题的基础上,将数据进行输出。也就是说该题的数据状态更复杂,不仅需要设计计数器,进行八次的计数,来确定八个数据位的接收,另外数据状态还要进行数据的输出,每一次计数,将一位的输入给数据输出缓存的对应位, par_in[count] <= in; 最后还要将将缓存的数据的八位给到输出。
在这里插入图片描述

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output [7:0] out_byte,
    output done
); 
 	parameter IDLE = 5'd00001;
    parameter START= 5'd00010;
    parameter DATA = 5'd00100;
    parameter STOP = 5'd01000;
    parameter WAIT = 5'd10000;
    
    reg [4:0]	state;
    reg [4:0]	next_state;
    reg [3:0]	count;
    reg [7:0]	par_in; //数据位是一位一位给的 共八位

        
// 第一段 状态寄存器
    always @(posedge clk)begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
//第二段 组合逻辑描述状态转移
    always @(*)begin
        case(state)
            IDLE :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            START:begin
                next_state = DATA;
            end
            DATA :begin
                if(count == 4'd8)begin //计数8次,说明8个数据接收完成
                    if(in == 1'b1) //出现停止位
                        next_state = STOP;
                    else
                        next_state = WAIT; 
                end
                else
                       next_state = DATA;
            end
            STOP :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            WAIT :begin
                if(in == 1'b0)
                    next_state = WAIT;
                else
                    next_state = IDLE;
            end
            default:begin 
                next_state = IDLE;   
            end
        endcase
    end
//第三段描述输出,可以采用一个always块来包含不同状态下所有的输出情况,这里为了
//对每个输出信号都清楚理解,分成不同三个多个always块来写。
 
 //输出数据接收寄存
    always @(posedge clk)begin
        if(reset)
            par_in[count] <= 1'd0;
        else if(next_state == DATA)
            par_in[count] <= in;
        else
            par_in[count] <= 1'd0;
    end
 //输出数据
    always @(posedge clk)begin
        if(reset)
            out_byte <= 8'd0;
        else if(next_state == STOP)
            out_byte <= par_in;  
        else
            out_byte <= 8'd0;
    end    
    
 //done信号
    always @(posedge clk)begin
        if(reset)begin
            done <= 1'd0;
        end
        else if(next_state == STOP)
            done <= 1'd1;
        else
            done <= 1'd0;
    end
    
 //设计计数器   
    always @(posedge clk)begin
        if(reset)begin
            count <= 4'd0;
        end   
        else if (next_state == DATA)
             count <= count + 1'd1;
         else
             count <= 4'd0;
     end
endmodule

Fsm serialdp(具有奇偶校验的串行接收器)

我们希望向串行接收器添加奇偶校验。奇偶校验在每个数据字节后增加一个额外的位。我们将使用奇数奇偶校验,其中收到的9位中的1的数量必须是奇数。例如,101001011满足奇数奇偶校验(有 5 个 1),但001001011不满足。

更改 FSM 和数据路径以执行奇数奇偶校验。仅当正确接收字节并且其奇偶校验通过时,才可确定done完成信号。另外此 FSM 需要标识起始位,等待所有 9 个(数据和奇偶校验)位,然后验证停止位是否正确。如果停止位未按预期出现,则 FSM 必须等到找到停止位后再尝试接收下一个字节。

为您提供了以下模块,可用于计算输入流的奇偶校验(这是一个具有重置功能的TFF)。预期的用途是,它应该被赋予输入位流,并在适当的时间复位,以便它计算每个字节中的1位数。

module parity (
    input clk,
    input reset,
    input in,
    output reg odd
    );

    always @(posedge clk)
        if (reset) odd <= 0;
        else if (in) odd <= ~odd;

endmodule

请注意,串行协议首先发送最低有效位,奇偶校验位在 8 个数据位之后发送。(最高位表示奇偶校验位)

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output [7:0] out_byte,
    output done
); 
 	parameter IDLE = 5'd00001;
    parameter START= 5'd00010;
    parameter DATA = 5'd00100;
    parameter STOP = 5'd01000;
    parameter WAIT = 5'd10000;
    
    reg [4:0]	state;
    reg [4:0]	next_state;
    reg [3:0]	count;
    reg [7:0]	par_in; //数据位是一位一位给的 共八位
    wire    Isdone;
    reg odd_temp;

        
// 第一段 状态寄存器
    always @(posedge clk)begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
//第二段 组合逻辑描述状态转移
    always @(*)begin
        case(state)
            IDLE :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            START:begin
                next_state = DATA;
            end
            DATA :begin
                if(count == 4'd9)begin //计数8次,说明8个数据接收完成
                    if(in == 1'b1) //出现停止位
                        next_state = STOP;
                    else
                        next_state = WAIT; 
                end
                else
                       next_state = DATA;
            end
            STOP :begin
                if(in == 1'b0)
                    next_state = START;
                else
                    next_state = IDLE;
            end
            WAIT :begin
                if(in == 1'b0)
                    next_state = WAIT;
                else
                    next_state = IDLE;
            end
            default:begin 
                next_state = IDLE;   
            end
        endcase
    end
//第三段描述输出,可以采用一个always块来包含不同状态下所有的输出情况,这里为了
//对每个输出信号都清楚理解,分成不同三个多个always块来写。
 
 //输出数据接收寄存
    always @(posedge clk)begin
        if(reset)
            par_in[count] <= 1'd0;
        else if(next_state == DATA)
            par_in[count] <= in;
        else
            par_in[count] <= 1'd0;
    end
 //输出数据
    always @(posedge clk)begin
        if(reset)
            out_byte <= 8'd0;
        else if(next_state == STOP)begin
            if(odd_temp) //满足奇偶校验将接收的数据给输出
                out_byte <= par_in;
            else
                out_byte <= 8'd0;
          end  
        else
            out_byte <= 8'd0;
    end    
    
 //done信号
    always @(posedge clk)begin
        if(reset)begin
            done <= 1'd0;
        end
        else if(next_state == STOP)begin //STOP仅能说明接收了九位数据,但是还要进行检测看是否为奇数校验
            if(odd_temp)
                done <= 1'd1;
            else
                done <= 1'd0;
        end
        else
            done <= 1'd0;
    end
    

    assign Isdone = (next_state == START);
    parity u0(clk,Isdone,in,odd_temp);  
        
 //设计计数器   
    always @(posedge clk)begin
        if(reset)begin
            count <= 4'd0;
        end   
        else if (next_state == DATA)
             count <= count + 1'd1;
         else
             count <= 4'd0;
     end
     
endmodule
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值