spinal HDL - 11 - 使用状态机语法编写“1001“序列检测

简介: spinal HDL - 11 - 使用状态机语法编写“1001“序列检测

前言


了解了在spinal HDL中如何利用scala语言进行状态机编写后,本文通过“1001”序列检测器的代码进行练习状态机的编写。

序列检测要求


检测1001序列,输入信号din依次输入1、0、0、1,当检测到完整序列后dout进行输出,使用mealy型状态机进行编写。

任务分析


对于使用Verilog的状态机编写,首先需要画状态转移图,然后根据状态转移图使用三段式状态机进行描述。

而对于spinal HDL来说,根据前文的讲述的语法规则,在完成状态转移图后,也需要根据状态转移图进行状态机描述。这里我根据三段式的思路,也大致分成三段:

  1. new 一个状态机 StateMachine 的val,new 需要的状态并指定状态机的入口。
  2. 根据spinal HDL的语法进行编写状态转移
  3. 根据实际需求,设计判断条件进行输出。

编写代码


为了对比spinal HDL的代码和Verilog代码的异同,首先给出一份手写的mealy型状态机的序列检测器。

module mealy_1001(clk,rst_n,din,dout,state_c,state_n
    );
  input           clk         ;
  input           rst_n   ;
  input           din         ;
  output      reg dout        ;
  output    [2:0] state_c     ;
  output    [2:0] state_n     ;
  reg       [2:0] state_c     ;//现态
  reg       [2:0] state_n     ;//次态
  //状态变量赋值
  parameter       S0 =3'b000,
          S1 =3'b001,
          S2 =3'b010,
          S3 =3'b100;
  //状态跳转
  always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      state_c <=S0;
    end
    else begin
      state_c <= state_n;
    end
  end
  //状态转移条件判断
  always@(*)begin
    case(state_c)
      S0:
        if(din==1'b1)begin
          state_n=S1;
        end
        else begin
          state_n=S0;
        end
      S1:
        if(din==1'b0)begin
          state_n=S2;
        end
        else begin
          state_n=S1;
        end
      S2:
        if(din==1'b0)begin
          state_n=S3;
        end
        else begin
          state_n=S1;
        end
      S3:
        if(din==1'b1)begin
          state_n=S1;
        end
        else begin
          state_n=S0;
        end
      default:state_n=S0;
    endcase
  end
  //输出模块
  always@(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
      dout<=1'b0;
    end
    else if((state_c==S3)&&(din==1'b1))begin
      dout<=1'b1;
    end
    else begin
      dout<=1'b0;
    end
  end
endmodule

然后,根据硬件状态机的设计思路,编写spinal HDL的状态机代码。下面代码为mealy型状态机。相比Verilog的代码版本更像伪代码。这里按照上述的三段式的思路,编写序列检测功能。

import spinal.core._
import spinal.lib._
import spinal.lib.fsm._
case class mealy_1001() extends Component {
  //定义一个输入输出的端口束
  val io = new Bundle {
    val din = in Bits (1 bit)
    val dout = out Bits (1 bit)
  }
  //消除掉生成的代码前缀
  noIoPrefix()
  //1-创建一个状态机
  val fsm = new StateMachine{
    //定义状态,并指定状态机的入口
    val S0 = new State with EntryPoint
    val S1 = new State
    val S2 = new State
    val S3 = new State
    //定义一个寄存器用于存储dout的值
    val reg1: Bits = Reg(Bits(1 bit)).init(0)
    //将输出的dout和寄存器reg1绑定一起
    io.dout := reg1
    //2-状态转移
    S0.whenIsActive{
        when(io.din === 1) {
          goto(S1)
        }.otherwise(goto(S0))
      }
    S1.whenIsActive {
        when(io.din === 0) {
          goto(S2)
        }.otherwise(goto(S1))
      }
    S2.whenIsActive {
      when(io.din === 0) {
        goto(S3)
      }.otherwise(goto(S1))
    }
    S3.whenIsActive {
      when(io.din===1){
        goto(S1)
      }.otherwise{
        goto(S0)
      }
    }
    //3-输出段
    when(io.din === 1&&isActive(S3)) {
      reg1:= 1
    }.otherwise(reg1:= 0)
  }
}
//生成Verilog代码
object mealy_1001APP extends App{
  SpinalConfig(
    anonymSignalPrefix = "tmp"
  ).generateVerilog(mealy_1001())

运行代码可以生成spinal HDL自动生成的Verilog版本的代码。在intelliJ中运行可编译生成v代码。

生成V代码分析


相比手写版本的代码,使用spinal语法生成的代码多了fsm_wantStart,fsm_wantKill,这两部分是和对应一段状态机的开始和停止。因为对于自动生成的代码中开始的默认状态是BOOT,该状态没有用户设计的状态转移的功能。其余部分的代码对比手写版本的状态机,基本和手写的效果相当。

`define fsm_enumDefinition_binary_sequential_type [2:0]
`define fsm_enumDefinition_binary_sequential_fsm_BOOT 3'b000
`define fsm_enumDefinition_binary_sequential_fsm_S0 3'b001
`define fsm_enumDefinition_binary_sequential_fsm_S1 3'b010
`define fsm_enumDefinition_binary_sequential_fsm_S2 3'b011
`define fsm_enumDefinition_binary_sequential_fsm_S3 3'b100
module mealy_1001 (
  input      [0:0]    din,
  output     [0:0]    dout,
  input               clk,
  input               reset
);
  wire                fsm_wantExit;
  reg                 fsm_wantStart;
  wire                fsm_wantKill;
  reg        [0:0]    fsm_reg1;
  wire                when_fsm_01_l45;
  reg        `fsm_enumDefinition_binary_sequential_type fsm_stateReg;
  reg        `fsm_enumDefinition_binary_sequential_type fsm_stateNext;
  wire                when_fsm_01_l23;
  wire                when_fsm_01_l29;
  wire                when_fsm_01_l34;
  wire                when_fsm_01_l39;
  `ifndef SYNTHESIS
  reg [63:0] fsm_stateReg_string;
  reg [63:0] fsm_stateNext_string;
  `endif
  `ifndef SYNTHESIS
  always @(*) begin
    case(fsm_stateReg)
      `fsm_enumDefinition_binary_sequential_fsm_BOOT : fsm_stateReg_string = "fsm_BOOT";
      `fsm_enumDefinition_binary_sequential_fsm_S0 : fsm_stateReg_string = "fsm_S0  ";
      `fsm_enumDefinition_binary_sequential_fsm_S1 : fsm_stateReg_string = "fsm_S1  ";
      `fsm_enumDefinition_binary_sequential_fsm_S2 : fsm_stateReg_string = "fsm_S2  ";
      `fsm_enumDefinition_binary_sequential_fsm_S3 : fsm_stateReg_string = "fsm_S3  ";
      default : fsm_stateReg_string = "????????";
    endcase
  end
  always @(*) begin
    case(fsm_stateNext)
      `fsm_enumDefinition_binary_sequential_fsm_BOOT : fsm_stateNext_string = "fsm_BOOT";
      `fsm_enumDefinition_binary_sequential_fsm_S0 : fsm_stateNext_string = "fsm_S0  ";
      `fsm_enumDefinition_binary_sequential_fsm_S1 : fsm_stateNext_string = "fsm_S1  ";
      `fsm_enumDefinition_binary_sequential_fsm_S2 : fsm_stateNext_string = "fsm_S2  ";
      `fsm_enumDefinition_binary_sequential_fsm_S3 : fsm_stateNext_string = "fsm_S3  ";
      default : fsm_stateNext_string = "????????";
    endcase
  end
  `endif
  assign fsm_wantExit = 1'b0;
  always @(*) begin
    fsm_wantStart = 1'b0;
    case(fsm_stateReg)
      `fsm_enumDefinition_binary_sequential_fsm_S0 : begin
      end
      `fsm_enumDefinition_binary_sequential_fsm_S1 : begin
      end
      `fsm_enumDefinition_binary_sequential_fsm_S2 : begin
      end
      `fsm_enumDefinition_binary_sequential_fsm_S3 : begin
      end
      default : begin
        fsm_wantStart = 1'b1;
      end
    endcase
  end
  assign fsm_wantKill = 1'b0;
  assign dout = fsm_reg1;
  assign when_fsm_01_l45 = ((din == 1'b1) && (fsm_stateReg == `fsm_enumDefinition_binary_sequential_fsm_S3));
  always @(*) begin
    fsm_stateNext = fsm_stateReg;
    case(fsm_stateReg)
      `fsm_enumDefinition_binary_sequential_fsm_S0 : begin
        if(when_fsm_01_l23) begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S1;
        end else begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S0;
        end
      end
      `fsm_enumDefinition_binary_sequential_fsm_S1 : begin
        if(when_fsm_01_l29) begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S2;
        end else begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S1;
        end
      end
      `fsm_enumDefinition_binary_sequential_fsm_S2 : begin
        if(when_fsm_01_l34) begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S3;
        end else begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S1;
        end
      end
      `fsm_enumDefinition_binary_sequential_fsm_S3 : begin
        if(when_fsm_01_l39) begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S1;
        end else begin
          fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S0;
        end
      end
      default : begin
      end
    endcase
    if(fsm_wantStart) begin
      fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_S0;
    end
    if(fsm_wantKill) begin
      fsm_stateNext = `fsm_enumDefinition_binary_sequential_fsm_BOOT;
    end
  end
  assign when_fsm_01_l23 = (din == 1'b1);
  assign when_fsm_01_l29 = (din == 1'b0);
  assign when_fsm_01_l34 = (din == 1'b0);
  assign when_fsm_01_l39 = (din == 1'b1);
  always @(posedge clk or posedge reset) begin
    if(reset) begin
      fsm_reg1 <= 1'b0;
      fsm_stateReg <= `fsm_enumDefinition_binary_sequential_fsm_BOOT;
    end else begin
      if(when_fsm_01_l45) begin
        fsm_reg1 <= 1'b1;
      end else begin
        fsm_reg1 <= 1'b0;
      end
      fsm_stateReg <= fsm_stateNext;
    end
  end
endmodule

仿真验证


编写一个验证代码,输入序列,观察输出能正常输出序列检测到的指示信号,证明spinal HDL生成的代码功能正常。

image.png

小结


  1. 使用spinal HDL的生成代码虽然有部分自认为“冗余”的部分,但是完全不影响实际的阅读,几乎和手写的代码相当。
  2. 极大地减小了在手动编写Verilog中时的冗余工作量。
  3. 经过仿真验证,代码功能和手写一致。
目录
相关文章
|
5月前
|
存储 C语言
【学习笔记】verilog HDL之二:数据类型与表达式
本文介绍了Verilog语言中的常量、变量和表达式。Verilog有四种基本值:0、1、x(未知)和z(高阻)。整型常量有十进制和基数两种格式,实数型常量包括浮点数,字符串常量由双引号括起的字符序列构成。变量分为线网型和寄存器型,线网型包括wire、tri等11种类型,寄存器型有reg、integer、time等,其中reg可声明存储器。表达式中的操作数包括常数、参数、线网等8种类型,操作符包括算术、关系、逻辑等9种类型。
|
异构计算
【数字电路】Y图 | 逻辑操作符 | 布尔函数 | Combinational systems
【数字电路】Y图 | 逻辑操作符 | 布尔函数 | Combinational systems
66 0
|
算法
如何将算法翻译成RTL(三):Verilog中的Signed本质及用法
如何将算法翻译成RTL(三):Verilog中的Signed本质及用法
278 0
|
算法 异构计算
m基于UW序列的数据帧检测,帧同步verilog实现,含testbench
m基于UW序列的数据帧检测,帧同步verilog实现,含testbench
356 0
|
算法 异构计算
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
通过状态机方法实现基于FPGA的维特比译码器,包含testbench测试文件
163 0
|
算法 异构计算
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
310 0
基于FPGA的直接序列扩频通信verilog设计,包括汉明编译码,扩频解扩,同步模块以及testbench
|
开发工具 芯片 异构计算
Verilog 设计方法
Verilog 的设计多采用自上而下的设计方法(top-down)。即先定义顶层模块功能,进而分析要构成顶层模块的必要子模块;然后进一步对各个模块进行分解、设计,直到到达无法进一步分解的底层功能块。这样,可以把一个较大的系统,细化成多个小系统,从时间、工作量上分配给更多的人员去设计,从而提高了设计速度,缩短了开发周期。
163 1
|
存储 Go C语言
编译原理,C语言实现LR(0)分析(扩展文法的生成、项目集规范簇的生成、ACTION GOTO表的生成、句子的分析)
注:代码生成的项目集规范簇、ACTION GOTO表的顺序可能和课本、教材、参考答案的顺序不同,但这并不影响分析过程的正确性,毕竟机器是按规律办事的😄
499 0
编译原理,C语言实现LR(0)分析(扩展文法的生成、项目集规范簇的生成、ACTION GOTO表的生成、句子的分析)
|
存储 算法 异构计算
基于Verilog HDL的状态机描述方法
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
156 0
基于Verilog HDL的状态机描述方法
|
算法 异构计算
Verilog HDL数据流建模与运算符
⭐本专栏针对FPGA进行入门学习,从数电中常见的逻辑代数讲起,结合Verilog HDL语言学习与仿真,主要对组合逻辑电路与时序逻辑电路进行分析与设计,对状态机FSM进行剖析与建模。
200 0
Verilog HDL数据流建模与运算符