⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,联合Verilog HDL语言学习与仿真,次要对组合逻辑电路与时序逻辑电路进行剖析与设计,对状态机FSM进行分析与建模。
文章和代码已归档至【Github仓库:hardware-tutorial】,须要的敌人们自取。或者关注公众号【AIShareLab】,回复 FPGA 也可获取。

状态图的建设过程

设计一个序列检测器电路。性能是检测出串行输出数据Sin中的4位二进制序列0101(自左至右输出),当检测到该序列时,输入Out=1;没有检测到该序列时,输入Out=0。(留神思考序列重叠的可能性,如010101,相当于呈现两个0101序列)。

解:首先,确定采纳米利型状态机设计该电路。因为该电路在间断收到信号0101时,输入为1,其余状况下输入为0,所以采纳米利型状态机。

其次,确定状态机的状态图。依据设计要求,该电路至多应有四个状态,别离用S1、S2、S3、S4示意。若假如电路的初始状态用S0示意,则可用五个状态来形容该电路。依据剖析,能够画出图(a)所示的原始状态图。

察看该图能够看出,S2、S4为等价状态,可用S2代替S4,于是失去简化状态图。

而后,依据下面的状态图给出该状态机的输入逻辑。该状态机只有一个输入变量Out,其输入逻辑非常简单,间接标注在状态图中了。若输入变量较多,则能够列出输入逻辑真值表。

最初,就能够应用硬件描述语言对状态图进行形容了。

状态图形容办法

利用Verilog HDL语言形容状态图次要蕴含四局部内容:

  1. 利用参数定义语句parameter形容状态机中各个状态的名称,并指定状态编码。例如,对序列检测器的状态调配能够应用最简略的天然二进制码,其形容如下:

    parameter  S0=2'b00, S1=2'b01, S2 = 2'b10, S3 = 2'b11;

    或者,

    parameter [1:0] S0=2'b00, S1=2'b01, S2 = 2'b10, S3 = 2'b11;
  2. 用时序的always 块形容状态触发器实现的状态存储。
  3. 应用敏感表和case语句(也能够采纳if-else等价语句)形容的状态转换逻辑。
  4. 形容状态机的输入逻辑。

形容状态图的办法多种多样,上面介绍几种:

单个always块形容状态机的办法(尽量避免)

用一个always块对该例的状态机进行形容,其代码如下:

module Detector1 ( Sin, CP, nCR, Out) ;    input Sin, CP, nCR;    //申明输出变量    output Out ;                //申明输入变量    reg Out;     reg [1:0] state;  // 申明两个状态触发器变量state[1]和state[0],记忆电路现态//The state labels and their assignmentsparameter [1:0] S0=2'b00, S1=2'b01, S2 = 2'b10, S3 = 2'b11;always @(posedge CP or negedge nCR)   beginif (~nCR)state <= S0;  //在nCR跳变为0时,异步清零else           case(state)       S0: begin Out =1’b0; state <= (Sin==1)? S0 : S1; end      S1: begin Out = 1’b0; state <= (Sin==1)? S2 : S1; end      S2: begin Out = 1’b0; state <= (Sin==1)? S0 : S3; end            S3: if (Sin==1)  begin Out =1’b1; state <=  S2; end            else                          begin Out =1’b0; state <= S1; end                       endcaseendendmodule

严格地说,对序列检测器电路用单个always块的形容办法所形容的逻辑存在着一个隐含的谬误,即输入信号Out的形容。

case语句中对输入向量的赋值应是下一个状态输入,这点易出错;状态向量与输入向量都由寄存器实现,面积大,不能实现异步米勒状态机。因而,单个always块形容状态机的写法仅仅实用于穆尔型状态机。单个always块写法的电路构造框图能够用下图进行概括。

两个always块形容状态机的办法(举荐写法!)

用两个always块对该例的状态机进行形容,其代码如下:

module  Detector2 ( Sin, CP, nCR, Out) ;input Sin, CP, nCR;    //定义输出变量output Out ;                //定义输入变量reg Out; reg [1:0] Current_state, Next_state;parameter [1:0] S0=2'b00, S1=2'b01, S2 = 2'b10, S3 = 2'b11;//状态转换,时序逻辑  always @(posedge CP or negedge nCR ) beginif (~nCR)    Current_state <= S0;   //异步清零else     Current_state <= Next_state;      //在CP回升沿触发器状态翻转   end//下一状态产生和输入信号,组合逻辑always @( Current_state or Sin)    begin         Next_state =2’bxx;                                                                                                                                                                                                                           Out=1’b 0;   case(Current_state )     S0: begin Out =1’b0; Next_state = (Sin==1)? S0 : S1; end     S1: begin Out =1’b0; Next_state = (Sin==1)? S2 : S1; end     S2: begin Out =1’b0; Next_state = (Sin==1)? S0 : S3; end          S3: if (Sin==1)            begin Out =1’b1; Next_state = S2; end        else            begin Out =1’b0; Next_state = S1; end          endcase  end    endmodule

用两个always块形容状态机的写法是值得举荐的办法之一,两个always块写法的电路构造框图能够用下图进行概括。

两个always块写法的电路构造框图概括。

第一个always模块采纳同步时序逻辑形式形容状态转移(两头方框); 第二个always模块采纳组合逻辑形式形容状态转移法则(第一个方框)和形容电路的输入信号(第三个方框)。

应用三个always块别离形容

即第一个always模块采纳同步时序逻辑形式形容状态转移(两头方框); 第二个always模块采纳组合逻辑形式形容状态转移法则(第一个方框); 第三个always模块形容电路的输入信号,在时序容许的状况下,通常让输入信号通过一个寄存器再输入,保障输入信号中没有毛刺。

用三个always块对该例的状态机进行形容,其代码如下:

module  Detector3 ( Sin, CP, nCR, Out) ;    input Sin, CP, nCR;    //定义输出变量    output Out ;                //定义输入变量    reg Out;     reg [1:0] Current_state, Next_state;     parameter [1:0] S0=2'b00, S1=2'b01, S2 = 2'b10, S3 = 2'b11;//状态转换,时序逻辑always @(posedge CP or negedge nCR )  begin     if (~nCR)       Current_state <= S0;                 //异步清零     else       Current_state <=  Next_state; //在CP回升沿触发器状态翻转  end  //下一状态产生,组合逻辑always @( Current_state or Sin)   begin           Next_state =2’bxx;                                                                                                                                                                                                                             case(Current_state )         S0: begin Next_state = (Sin==1)? S0 : S1; end         S1: begin Next_state = (Sin==1)? S2 : S1; end         S2: begin Next_state = (Sin==1)? S0 : S3; end              S3: if (Sin==1)                begin Next_state = S2; end                    else            begin Next_state = S1; end             endcase  end     /* 输入逻辑: 让输入信号通过一个寄存器再输入,能够打消Out信号中的毛刺,时序逻辑*/always @ (posedge CP or negedge nCR )    begin    if (~nCR)    Out <= 1’b 0;              else                       begin                    case(Current_state )               S0, S1, S2:          Out <= 1’b0;                                   S3:               if (Sin==1)                                                   Out <= 1’b1;                               else                                                                        Out <= 1’b0;                          endcase                    end        end    endmodule

三种形容办法比拟

1-always2-always3-always
结构化设计
代码编写/了解不宜,了解难
输入信号寄存器输入组合逻辑输入寄存器输入
不产生毛刺产生毛刺不产生毛刺
面积耗费最小
时序束缚不利无利无利
可靠性、可维护性较高最高
后端物理设计不利无利无利

参考文献:

  1. Verilog HDL与FPGA数字零碎设计,罗杰,机械工业出版社,2015年04月
  2. Verilog HDL与CPLD/FPGA我的项目开发教程(第2版), 聂章龙, 机械工业出版社, 2015年12月
  3. Verilog HDL数字设计与综合(第2版), Samir Palnitkar著,夏宇闻等译, 电子工业出版社, 2015年08月
  4. Verilog HDL入门(第3版), J. BHASKER 著 夏宇闻甘伟 译, 北京航空航天大学出版社, 2019年03月

欢送关注公众号【AIShareLab】,一起交换更多相干常识,前沿算法,Paper解读,我的项目源码,面经总结。