大家帮我看看我这个状态机为什么时序仿真不对,功能仿真对呢?
http://cache.amobbs.com/bbs_upload782111/files_8/ourdev_186577.jpghttp://cache.amobbs.com/bbs_upload782111/files_8/ourdev_186578.jpg
http://cache.amobbs.com/bbs_upload782111/files_8/ourdev_186579.jpg
乒乓球游戏机
一、 实验目的
1、学习较复杂状态及设计方法;
2、掌握通过采样防止开关抖动的方法
3、初步了解功能分割的方法
二、 试验说明
本试验设计一个简单的乒乓球游戏机。它可以有两个人进行游戏,游戏规则如下:
1.过早击球,对方获胜;
2.每次击球,若球飞出界外,对方获胜。
试验用8 个状态代表乒乓球的运动轨迹,两个输出信号指示获胜方,两个输入信号
代表双方的球拍,输入信号击球低有效。因为一般的开关在大约20ms 内信号不稳定,
存在所谓的“开关抖动”,会产生多个脉冲影响电路正常工作。所以含开关输入的设计
需要做防抖动处理,在本试验可以用20Hz 的时钟采样击球信号实现防抖。
设计时需要考虑乒乓球的运动规律和游戏规则的要求。乒乓球运动可分解为两个因
素:运动方向、位置。状态及中可以用八个发光二极管代表八个位置,即
1<---------􀃆 2<---------􀃆 3<---------􀃆 4<---------􀃆 5 <---------􀃆 6 <---------􀃆 7
<---------􀃆 8
根据游戏规则,在本方位置击球有效,在对方位置击球或球超出1 位置或8 位置,
则对方获胜。游戏结果有两个状态,即左方获胜和右方获胜,进入作方获胜或有方获胜
状态后,可以直接或用复位开关推出两个状态。可以将游戏结果与乒乓球位置统一成一
个状态机,共10 个状态。
游戏机的乒乓球运动速度用单位时间的移动的位置书代表,以每秒一个位置或两个
位置为宜,可以通过系统时钟控制。游戏机的乒乓球运动只能够有两个运动方向,即向
左或向右。
根据以上说明,对状态机进行编码,画出状态转移图,并用verilog 语言进行建模。
注意个状态机输入和输出信号之间的关系。
三、 试验要求
1、分别输入状态机设计文件,编译,调试通过。
2、对程序进行仿真,检查各种情况下结果是否正确。
四、 总结报告要求
1、写出个状态机的状态编码方案,状态机转移图,verilog 文件,仿真结果波形。
2、写出测试记录。
//pingpang 规则:输者发球
module pingpang(clk,rst,left_hit,right_hit,ball_state);
input clk,rst,left_hit,right_hit;
output ball_state;
reg ball_state;
reg current_state;
reg next_state;
reg direction;
parameter left = 1'b1, right = 1'b0;
parameter left_lose= 10'b1000000000,
s1 = 10'b0100000000,
s2 = 10'b0010000000,
s3 = 10'b0001000000,
s4 = 10'b0000100000,
s5 = 10'b0000010000,
s6 = 10'b0000001000,
s7 = 10'b0000000100,
s8 = 10'b0000000010,
right_lose = 10'b0000000001;
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or posedge rst )
if(rst)
current_state <= left_lose;
else
current_state <= next_state;
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state or left_hit or right_hit) //电平触发
begin
case(current_state)
left_lose:
begin
direction= right;
next_state = s1;
end
s1:
begin
if(left_hit)direction = right;
if(direction == right)
next_state = s2;
else
next_state = left_lose;
if(right_hit) next_state = right_lose;
end
s2:
begin
if(left_hit)direction = right;
if(direction == right)
next_state = s3;
else
next_state = s1;
if(right_hit) next_state = right_lose;
end
s3:
begin
if(left_hit)direction = right;
if(direction == right)
next_state = s4;
else
next_state = s2;
if(right_hit) next_state = right_lose;
end
s4:
begin
if(left_hit)direction = right;
if(direction == right)
next_state = s5;
else
next_state = s3;
if(right_hit) next_state = right_lose;
end
s5:
begin
if(right_hit) direction = left;
if(direction == right)
next_state = s6;
else
next_state = s4;
if(left_hit)next_state = left_lose;
end
s6:
begin
if(right_hit) direction = left;
if(direction == right)
next_state = s7;
else
next_state = s5;
if(left_hit)next_state = left_lose;
end
s7:
begin
if(right_hit) direction = left;
if(direction == right)
next_state = s8;
else
next_state = s6;
if(left_hit)next_state = left_lose;
end
s8:
begin
if(right_hit) direction = left;
if(direction == left)
next_state = s7;
else
next_state = right_lose;
if(left_hit)next_state = left_lose;
end
right_lose:
begin
direction= left;
next_state = s8;
end
default:
next_state = left_lose; //阻塞赋值
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @(posedge clk)
case(next_state)
left_lose:
ball_state <= left_lose;//output
s1:
ball_state <= s1;
s2:
ball_state <= s2;
s3:
ball_state <= s3;
s4:
ball_state <= s4;
s5:
ball_state <= s5;
s6:
ball_state <= s6;
s7:
ball_state <= s7;
s8:
ball_state <= s8;
right_lose:
ball_state <= right_lose;
default:
ball_state <= left_lose;
endcase
endmodule
下载到板子上有时后会出现和时序仿真一样的问题,动一动left_hit或者right_hit就好了,奇怪了 继续求助 问题的原因就在于你写的所谓三段式状态机的第二个进程的描述格式有问题,由于第二个进程是电平敏感方式,转移条件判断语句只有if而没有else,属于不完整描述,会生成Latch,编译结果与期望的不一样(你看一下编译结果,肯定有警告信息,里面有"Latch"字样)。再来分析一下你这种描述为什么产生上面的仿真结果。首先从系统上电说起。
你把复位信号给直接接地了,也就是相当于不要复位信号了,这样一来,系统上电后寄存器处于无需状态。好在FPGA里面有PSR电路,系统上电或重配置后寄存器会复位为0。再看一下你的状态机描述,状态寄存器为0的情况属于defa-lut状态,因此你的状态机一上电后跑到default状态,这是不合理的。至少人家还没有开始和你比赛,Left方就失掉了一个球。哈哈!
然后呢?状态机自然就转移到s8了,初一看,没什么错误.你看,语法规则都是这样,语句整齐,没有错误了!实际不然,你看一下开头的地方,always @ (current_state or left_hit or right_hit),你自己也注释了电平敏感,什么是电平敏感,意思就是:你一直不断(这就是always的意思了)地给我监视这三个信号,一旦(这就是@的意思了)其中的任何(这就是or的意思了)一个发生了变化,那么就执行这个进程了。好,由于current_state发生变化,状态机跑到s8了,然后检查right_hit,由于right方没有键按下,不执行。接着向下检查direction,嗯,direction == left,好next_state = s7,但是没完,后面还有一句。由于Left方也没有键按下,这个条件不成立了,不予以执行,看到这里,状态机似乎应该转向s7了。实际上,问题就出在这个"if(left_hit)next_state = eft_lose; "了,我要问你,else呢?接下来该怎么办,next_state 该怎么赋值呢,由于没有了else下文,这里的意思就是暗示:保持现有状态不要改变,next_state 当前的值是right_lose(注意,不是s7),所以next_state= right_lose,下一个状态又回到right_lose了。结果是Right方变成不停地输球了,呵呵!至于有键按下之后为什么变好了就不用我多讲啦! 谢谢 that1101 帮助,终于做出来,但还有不少小问题
1.开始写时没考虑到idle态,所以初始化都从left_lose态开始了,以后一定要写
2.问题的关键的确是if和else配合,少了else,next_state就乱掉了,最后我把所有赋值next_state的语句写到一个if else里面
给direction赋值也容易引起问题,最后把direction拿到case外面去赋值
总结一下,写好状态机需要丰富的经验
====================================================================
//修改后的程序
//pingpang 规则:输者发球
module pingpang2(clk,rst,left_hit,right_hit,ball_state);
input clk,rst,left_hit,right_hit;
output ball_state;
reg ball_state;
reg current_state;
reg next_state;
reg direction;
parameter left = 1'b1, right = 1'b0;
parameter left_lose = 10'b1000000000,
s1 = 10'b0100000000,
s2 = 10'b0010000000,
s3 = 10'b0001000000,
s4 = 10'b0000100000,
s5 = 10'b0000010000,
s6 = 10'b0000001000,
s7 = 10'b0000000100,
s8 = 10'b0000000010,
right_lose = 10'b0000000001;
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or posedge rst )
if(rst)
current_state <= left_lose;
else
current_state <= next_state;
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state or left_hit or right_hit) //电平触发
begin
if(left_hit) direction = right;
if(right_hit) direction = left;
case(current_state)
left_lose:
begin
direction= right;
next_state = s1;
end
s1:
begin
if(right_hit) next_state = right_lose;
else if(direction == right)
next_state = s2;
else
next_state = left_lose;
end
s2:
begin
if(right_hit) next_state = right_lose;
else if(direction == right)
next_state = s3;
else
next_state = s1;
end
s3:
begin
if(right_hit) next_state = right_lose;
else if(direction == right)
next_state = s4;
else
next_state = s2;
end
s4:
begin
if(right_hit) next_state = right_lose;
else if(direction == right)
next_state = s5;
else
next_state = s3;
end
s5:
begin
if(left_hit) next_state = left_lose;
else if(direction == right)
next_state = s6;
else
next_state = s4;
end
s6:
begin
if(left_hit) next_state = left_lose;
else if(direction == right)
next_state = s7;
else
next_state = s5;
end
s7:
begin
if(left_hit) next_state = left_lose;
else if(direction == right)
next_state = s8;
else
next_state = s6;
end
s8:
begin
if(left_hit) next_state = left_lose;
else if(direction == right)
next_state = right_lose;
else
next_state = s7;
end
right_lose:
begin
direction= left;
next_state = s8;
end
default:
next_state = left_lose; //阻塞赋值
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @(posedge clk )
begin
case(next_state)
left_lose:
ball_state <= left_lose;//output
s1:
ball_state <= s1;
s2:
ball_state <= s2;
s3:
ball_state <= s3;
s4:
ball_state <= s4;
s5:
ball_state <= s5;
s6:
ball_state <= s6;
s7:
ball_state <= s7;
s8:
ball_state <= s8;
right_lose:
ball_state <= right_lose;
default:
ball_state <= left_lose;
endcase
end
endmodule
http://cache.amobbs.com/bbs_upload782111/files_8/ourdev_186982.jpg mark 和我遇到的问题一样http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4408483&bbs_page_no=1&bbs_id=1029 mark mark,正在搞一个状态机 学习 mark mark状态机 好贴。学习了。状态机。 问得好,打得好,学习了~~~~~~~~ mark状态机 第二个进程可用 always@(*)
页:
[1]