讯享网
今天一门课程需要用verilog HDL设计一个硬件的电路,然后自己试着用状态机写(事实上有更偷懒的写法,比如枚举波形)。到最后自己写的情况一地鸡毛,不仅提交的时间迟了很多,而且提交上的代码也是一地鸡毛,仿真波形尽管说基本功能正确,但是还是有一些瑕疵。最终赌气重写了代码,一边写一边反思,最终代码正确实现了我所构想的全部功能。
我觉得之前的设计之所以出问题,很大的原因在于我还是没真正的理解状态机,不能抓住代码到底实现一个实际电路的方法。以下是设计思路和一些反思。之前也看了一篇状态机的文章,感觉那个讲的还很好,当时也挺有收获。放这里一同参考。
万物基于状态机
硅农,公众号:硅农万物基于状态机——状态机大法好
第一节 题目要求
设计一个交通信号灯控制器
要求:
1、东西方向和南北方向各有4盏灯,分别为左拐灯、绿灯、黄灯和红灯;
2、东西方向信号灯的时间为:红灯55s,黄灯5s,绿灯40s,左拐灯15s;南北方向信号灯的时间为:红灯65s,黄灯5s,绿灯30s,左拐灯15s;
3、提交控制器程序和测试程序,观测仿真波形,并对仿真波形做分析,说明设计的正确性。
第二节 代码原理
1、以下简称东西方向的红黄绿左拐灯分别为WER、WEY、WEG、WEL;同样的,南北方向的信号灯依上述顺序简称为SNR,SNY,SNG,SNL;

2、交通信号灯在一方红灯的时候,另一方才能做亮绿灯,红灯,左拐灯的动作。根据日常的交通规范,当一侧红灯亮起的时候,另一侧信号灯依次执行待转区域左转信号发出、黄灯、直行或右转信号发出、黄灯。根据题目所给时间,恰好与这样的交通灯行为吻合。因此,本次设计将以上述规则为主。
3、本次设计将设计两层的状态机,第一层状态机控制两个方向的红灯,当一方红灯的计时结束则改变状态使另一侧红灯亮起。在此基础上,嵌套的子状态机控制一方红灯亮起时另一方信号灯的动作。按照第二条所给的设计规则,来设计状态的跳转。具体的状态转换图如下:

其中,SNR内的状态与WER很相似。
我一开始脑子里直接套三段式,第一段初始化并定义状态,第二段写一个状态下的各个动作,比如在这次的作业里,我第一次写,我第一段用一个边沿信号写时序部分(寄存器),第二段我用state做敏感源来跳转状态,第三段写它的动作。
问题便出在第二段,我在里边写了一个计数。即在某一个状态下要count=count+1。再然后,我写了一个用count来判断是否进行状态跳转。这一块的所有使得在第二段的组合逻辑电路里,实际上构建了一个没什么动作的死循环:状态的跳转依赖计数器的计数到达某一个值,而计数器计数又靠敏感事件列表驱动。伪代码如下:
always@(state)case(state)0: if(count begin count<=count+1; next_state<=1; end else next_state=1; ........
讯享网
这个错误我并未意识到,以至于我花费大量的时间用来改其他的东西,导致波形该咋样依旧咋样。而且改的过程中有很多明明知道没必要改的地方,也改了,心想着碰碰运气万一就好了。这种心态我觉得来源于之前写64位乘法器的时候,瞎改了一个自己认为对的地方,然后盘活了全局。至于那个,还是和这次一样,对于verilog HDL的储备不够。
为了实现我上图所示的状态转换图,综合出一个称心如意的电路,重写的版本我把三段式的第一第二段合并了。也即在同一个敏感事件列表里我实现状态的跳转和计数。这样一来,我盘活了整个代码。代码如下:
讯享网`timescale 1ns/1ns module signalLED(rst,clk,WEcount,SNcount,WER,WEG,WEL,WEY,SNR,SNG,SNL,SNY);//输入信号:时钟信号和复位信号input clk,rst;//输出信号:信号灯上的计时信号output reg [6:0] WEcount,SNcount;//输出信号:南北方向信号灯上的信号output reg SNR,SNG,SNL,SNY;//输出信号:东西方向信号灯上的信号output reg WER,WEG,WEL,WEY;//触发信号,使得外层状态机进行循环reg [6:0] cnt;//外层状态机的状态和下次状态reg outstate; //参数:灯的亮暗parameter ON = 1'b0; //信号灯亮parameter OFF = 1'b1; //信号灯灭 //状态转换,组合逻辑 always@(posedge clk or negedge rst) if(!rst) begin outstate <=1'b0; cnt<=7'd0; SNcount<=0; WEcount<=0; end else case(outstate) 1'b0: begin //东西方向红灯计时55秒钟以后,计时清零,状态跳转到南北方向的红灯 if(cnt==7'd55) begin cnt<=0; outstate<=1'b1; SNcount<=0; WER<=OFF; SNR<=ON; end //否则东西方向红灯计时继续,红灯不变 else begin cnt<=cnt+1; outstate<=1'b0; WEcount<=WEcount+1; WER<=ON; SNR<=OFF; end end 1'b1: begin //南北方向红灯计时65秒钟以后,计时清零,状态跳转到南北方向的红灯 if(cnt==7'd65) begin cnt=0; outstate=1'b0; WEcount=0; WER<=OFF; SNR<=ON; end //否则东西方向红灯计时继续,红灯不变 else begin cnt=cnt+1; outstate=1'b1; SNcount=SNcount+1; WER<=ON; SNR<=OFF; end end default:$display("error1"); endcase always@(outstate or cnt) case(outstate) 1'b0: begin //东西方向红灯计时过程中,南北方向的信号转换 //绿灯先开始计时,并亮灯 if(cnt==7'd0) begin SNcount<=0; SNG<=1; SNL<=0; SNY<=0; end //绿灯亮灯中 else if(cnt<7'd30) begin SNcount<=SNcount+1; SNG<=1; SNL<=0; SNY<=0; end //黄灯开始亮灯并计时 else if(cnt==7'd30) begin SNcount<=0; SNG<=0; SNL<=0; SNY<=1; end //黄灯亮灯 else if(cnt<7'd35) begin SNcount<=SNcount+1; SNG<=0; SNL<=0; SNY<=1; end //左转信号灯开始亮灯,并计时 else if(cnt==7'd35) begin SNcount<=0; SNG<=0; SNL<=1; SNY<=0; end //左转信号灯亮灯中 else if(cnt<7'd50) begin SNcount<=SNcount+1; SNG<=0; SNL<=1; SNY<=0; end //第二次黄灯开始亮灯,并开始计时 else if(cnt==7'd50) begin SNcount<=0; SNG<=0; SNL<=0; SNY<=1; end //黄灯亮灯中 else if(cnt<7'd55) begin SNcount<=SNcount+1; SNG<=0; SNL<=0; SNY<=1; end //亮灯结束 else begin SNG<=0; SNL<=0; SNY<=0; end end 1'b1: begin //南北方向红灯计时过程中,东西方向的信号转换 //绿灯先开始计时,并亮灯 if(cnt==7'd0) begin WEcount<=0; WEG<=1; WEL<=0; WEY<=0; end //绿灯亮灯中 else if(cnt<7'd40) begin WEcount<=WEcount+1; WEG<=1; WEL<=0; WEY<=0; end //黄灯开始亮灯并计时 else if(cnt==7'd40) begin WEcount<=0; WEG<=0; WEL<=0; WEY<=1; end //黄灯亮灯 else if(cnt<7'd45) begin WEcount<=WEcount+1; WEG<=0; WEL<=0; WEY<=1; end //左转信号灯开始亮灯,并计时 else if(cnt==7'd45) begin WEcount<=0; WEG<=0; WEL<=1; WEY<=0; end //左转信号灯亮灯中 else if(cnt<7'd60) begin WEcount<=WEcount+1; WEG<=0; WEL<=1; WEY<=0; end //第二次黄灯开始亮灯,并开始计时 else if(cnt==7'd60) begin WEcount<=0; WEG<=0; WEL<=0; WEY<=1; end //黄灯亮灯中 else if(cnt<7'd65) begin WEcount<=WEcount+1; WEG<=0; WEL<=0; WEY<=1; end //亮灯结束 else begin WEG<=0; WEL<=0; WEY<=0; end end default:$display("error2"); endcaseendmodule module signalLED_tb;// Inputsreg rst;reg clk;// Outputwire SNR,SNG,SNL,SNY;wire WER,WEG,WEL,WEY;// outputwire [6:0] WEcount,SNcount;initialbegin rst= 0; clk = 1;#80; rst= 1;endalways #50 clk = ~clk;//signalLED tarfficLED(.rst(rst),.clk(clk),.WEcount(WEcount),.SNcount(SNcount),.WER(WER),.WEG(WEG),.WEL(WEL),.WEY(WEY),.SNR(SNR),.SNG(SNG),.SNL(SNL),.SNY(SNY));endmodule 总结一下,对于状态机里状态跳转的情况,要注意它跳转的条件是电平触发还是边沿触发,边沿的就合到第一段,电平的就可以放第二段。这样会避免死循环。本质上来说,状态机综合出的电路是两部分的寄存器电路夹了一个组合电路。这是要厘清哪些功能在实际哪个块实现,它会由什么驱动。 此外,电路的跳转需要的一些信号如果无法通过组合逻辑电路实现,也可以不在时序电路里实现,可以单独写module,然后通过使能端来驱动。这样,传统的两段式或者三段式还是可以用的。但是最终,还是得会写一段式。因为一个模块里复杂的话,一段式的构思是比二段式和三段式是要简单的。不过也不是绝对。这是我上述代码的一个改进方向。届时还能实现交通灯的倒数计时。(现在是正数计数)那就是真正实现所有功能的代码。当然,最近估计没时间了。

如上图,信号的跳转正常,符合要求,也符合交规。
计数也正常.
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/124752.html