目录 |
- 1、绘制状态转移图 - 1.1、在Sublime 中用Graphviz绘制状态转移图 - 1.2、在Notepad++ 中用Graphviz绘制状态转移图 - 1.2、Morrer与Mealy型状态机的状态转移图对比 - 2、Morrer状态机代码 - 3、仿真TestBench代码 - 4、Modelsim仿真 - 5、调试修改代码 - 6、仿真和调试问题以及解决思路
|
【参考文章】
1、第6章 如何写好状态机 节选自《Verilog设计与验证》 作者:吴继华、王诚
下载PDF看原文 http://read.pudn.com/downloads328/ebook/1442931/A.pdf
或者看总结 Verilog FSM设计的学习心得(二)
来自
2、【 FPGA 】序列检测器的Moore状态机实现
https://blog.csdn.net/Reborn_Lee/article/details/85763185
3、【 FPGA 】序列检测器的Mealy状态机实现
https://blog.csdn.net/Reborn_Lee/article/details/85798105
4、Verilog实现--序列检测器、自动饮料售卖机
https://blog.csdn.net/qq_34070723/article/details/100737225
5、第一次verilog实验——序列检测器的实现
来自
【注意】《如何写好状态机》中的模板有适用范围
Altera模块声明如下: module state ( nrst,clk, i1,i2, o1,o2, ); input nrst,clk; input i1,i2, output o1,o2; reg o1,o2;
Xilinx的模块声明如下: module state ( input nrst,clk, input i1,i2, output reg o1,o2,err ); |
【目标】设计一个序列检测器(无重叠检测),检测序列1101,检测到输出1,否则输出0
1、绘制状态转移图
1.1、在Sublime 中用Graphviz绘制状态转移图
【参考】Graphviz 安装及结合sublime
来自
错误是file or directory not found, 但报告的内容path看起来有些混乱-即它不包括macports放置可执行文件的目录。 看着https://forum.sublimetext.com/t/how-to-set-path-on-os-x-so-sublime-can-see-it/11842 重新启动Sublime并解决了问题。 来自 |
1.2、在Notepad++ 中用Graphviz绘制状态转移图
【参考】NppGraphViz,一个Notepad ++插件,可将当前选项卡的文档发送到GraphViz预览窗口。
来自
【参考】NppGraphViz不添加语法突出显示,但是可以在此处使用该功能: https://github.com/signmotion/graphviz-syntax-highlighting
1.2、Morrer与Mealy型状态机的状态转移图对比
Morrer型状态机:摩尔状态机的输出仅仅依赖于当前状态,而与输入条件无关
Graphviz 代码Sequencer-Moore.gv 如下
digraph fsm { "IDLE" -> "IDLE" [label= "0"] "IDLE" -> "S1" [label= "1"]
"S1" -> "IDLE" [label= "0"] "S1" -> "S2" [label= "1"]
"S2" -> "S2" [label= "1"] "S2" -> "S3" [label= "0"]
"S3" -> "S4" [label= "1"] "S3" -> "IDLE" [label= "0"]
"S4" -> "IDLE" [label= "0"] "S4" -> "S1" [label= "1"] } |
绘图
米勒状态机:空闲态+中间3态,输出时由当前态S3+输入态,共同决定
Graphviz 代码Sequencer-Mealy.gv 如下
digraph fsm { "IDLE" -> "IDLE" [label= "0"] "IDLE" -> "S1" [label= "1"]
"S1" -> "IDLE" [label= "0"] "S1" -> "S2" [label= "1"]
"S2" -> "S2" [label= "1"] "S2" -> "S3" [label= "0"]
"S3" -> "IDLE" [label= "1"] "S3" -> "IDLE" [label= "0"] } |
绘图
2、Morrer状态机代码
Sequencer_Morrer.v 源代码如下:
`timescale 1ns / 1ps ////////////////////////////////SourceCode:Logic//////////////////////////////////////// // Company: // Engineer: // // Create Date: 2019/01/04 11:16:29 // Design Name: // Module Name: seq_det_moore // Project Name: // Target Devices: // Tool Versions: // Description: Sequencer Detection - Moore FSM // 设计一个序列检测器,检测序列1101,检测到输出1,否则输出0 // 摩尔状态机的输出仅仅依赖于当前状态,而与输入条件无关 // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// `define UD #1 //定义变量,可跨工程调用 UD user delay,初始化#1 module seq_det_moore( input i_clk, input i_rst_n, //系统复位,低电平有效 input i_data, output reg o_out );
//================================================= //input dalay:输入打一拍 //================================================= reg i_data_reg = 1'b0; always @ (posedge i_clk) begin // if(i_rst_n) i_data_reg <= i_data; end //================================================= // state FSM:状态声明,4个有效状态+1个空闲状态 //================================================= reg [3:0] CS,NS; //CS:Current State; NS:Next State localparam [3:0] //one hot with zero idle IDLE = 4'b0000, S1 = 4'b0001, S2 = 4'b0010, S3 = 4'b0100, S4 = 4'b1000;
//================================================= // FSM input //================================================= //1st always block:sequential state transition //现态与次态转换 时序电路 always @(posedge i_clk) begin if (!i_rst_n) begin CS <= IDLE; // reset // i_data_reg <= 1'b0; //复位态,数据首位,赋值为0,则状态判断延时2clk end else CS <= NS; end
//2nd always block:combinational condition judgment //第二段:各种状态间的跳变。 组合电路 always @ (*) begin case(CS) //状态转移,判断当前状态"CS" IDLE:begin if (i_data_reg) NS = S1; //第一位1,din == 1'b1 else NS = IDLE; //din == 1'b0 end S1: begin if (i_data_reg) NS = S2; //1, 11 else NS = IDLE; //10,回到原点 end S2: begin if (~i_data_reg) NS = S3; //0, 110 else NS = S2; //111,等待/循环,11101 end S3: begin if (i_data_reg) NS = S4; //1, 1101 else NS = IDLE; //1100,回到原点 end S4: begin if (i_data_reg) NS = S1; //下一组,第一位1 else NS = IDLE; //下一组,第一位0,回原点 end default: NS = IDLE; endcase end
//================================================= // FSM output: Moore型,输出只与当前状态有关 //================================================= //3rd always block:the sequential FSM output //第三段:确定最终的状态 always @(posedge i_clk) begin if ( CS == S4) o_out <= 1; else o_out <= 0; end
endmodule |
3、仿真TestBench代码
TB_Sequencer_Morrer.v 源代码如下:
`timescale 1ns / 1ps //////////////////////////SimulationCode:Test Bench///////////////////////////////////// // Company: // Engineer: // // Create Date: 2019/01/04 15:24:59 // Design Name: // Module Name: seq_det_moore_tb // Project Name: // Target Devices: // Tool Versions: // Description: // 数据一开始就发送过去,第一个clk上升沿就接到信号,开始判断,但当前状态一直循环为空 //就等解除复位态,下一个数据过来,进入下一状态 //最终输出没有延时,相当于前4个clk上升沿输入信号,第5个clk上升沿 输出结果 // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// `define UD #1 module seq_det_moore_tb;
reg sim_clk; reg sim_rst_n; reg sim_i_data; //输入序列,缓冲,赋值 wire sim_o_out;
// Note: CLK must be defined as a reg when using this method parameter CLK_PERIOD = 10; //仿真周期10ns=100M parameter RST_CYCLE = 5; //复位周期数 parameter RST_TIME = RST_CYCLE * CLK_PERIOD; //复位时间:5个时钟周期 parameter IN_SEQ = 21'b1_1011_1010_1101_0010_1101; //时钟 10ns always #(CLK_PERIOD/2) sim_clk = ~sim_clk; /* //时钟:写法2 always begin i_clk = 1'b0; #(CLK_PERIOD/2) clk = 1'b1; #(CLK_PERIOD/2); end */ reg [4:0]cnt; //过程赋值,一次性复位信号??????????? initial begin sim_clk = 1'b0; sim_rst_n = 1'b0; //复位再拉低,有效,保持足够长时间(5个clk) #RST_TIME sim_rst_n = 1'b1; //复位 sim_i_data = 1'b0; //测试变量,赋初值 end //过程赋值,循环,每次只改变数据的位数cnt always @(posedge sim_clk) begin if(!sim_rst_n) begin cnt <= 1'b0; // sim_i_data <= 1'b0; //复位态,数据首位,保持0 end else begin cnt <= cnt +1'b1; sim_i_data <= IN_SEQ[cnt]; //sim_i_data不输出,sim_i_data与IN_SEQ[cnt]有1个clk的延时,仅用于原始信号变化 end end //模块实例,调用 seq_det_moore u1( .i_clk ( sim_clk ),//input clk, .i_rst_n ( sim_rst_n ),//input rstn, .i_data ( IN_SEQ[cnt] ), //每次送一位数据,第0位数据在复位态就已经发送,第1位数据在解除复位后发送 //.i_data ( sim_i_data ), //解除复位后,才发送数据 .o_out ( sim_o_out ) ); endmodule |
4、Modelsim仿真
【参考】Modelsim的简单使用
来自
5、调试修改代码
【参考】修改代码后如何使用 modelsim 仿真
来自
6、仿真和调试问题以及解决思路
主要问题是输入序列到输出结果之间有延时
下图写错了,应该是检测1101,延时分别为3.5clk 与 1.5clk
原因1、Testbench文件中,赋值为循环,一次性送完数据,时间长
2、改为循环中每次只改变送的数据位,每次送一位数据
还是有延时
Q1、复位态不发送数据,解除复位态后才发送,
输入数据打一拍,FSM第二阶段判断也要clk等,延时有4个clk
改为以下方法,延时消失
Q2: 数据一开始就发送过去,第一个clk上升沿就接到信号,开始判断,但当前状态一直循环为空
就等解除复位态,下一个数据过来,进入下一状态
最终输出没有延时,相当于前4个clk上升沿输入信号,第5个clk上升沿 输出结果