EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
( X8 r7 w% c9 m
w6 q" w8 e. H1 M2 {3 O( A) }概述 % g4 y, s- R0 [0 I; f+ s
本篇主要介绍在上一篇在CPU的基础上添加外部中断和定时器中断等必要功能部件,以实现俄罗斯方块游戏所必须的硬件基础。 然后介绍一下软件设计流程。并附上最终演示效果。 - R6 f, A; Z3 a% m
(一)硬件设计 目前已经有了cpu,、 ps2,、vga三者结合的soc模块。然而俄罗斯方块游戏还需要一个定时器中断,用来控制向下匀速运动。一个外部中断检测外部的按键信息,并实施旋转变换的操作。此外,还添加了一个额外的定时器,用于产生随机的0-5的数字来生成随机图形。 7 ~3 h! N+ N- z$ v
中断机制: IE寄存器 IE[0] = 定时器中断开关 IE[2]= 外部中断开关
0 S& z5 y' i# N) S7 ueret指令和epc寄存器
% @& Q2 x- y. r# T 在mips32指令集中有一条eret指令,用于返回中断前地址。epc寄存器用于存储中断来的时候下一条指令的地址。在中断开启的情况下,比如IE[2]=1;如果外部中断来(1),首先epc=next_pc,然后令pc等于事先规定好的中断服务程序的入口地址(本例中外部中断入口地址定义的是32’h14)。在进入外部中断的中断服务程序后,马上把IE寄存器整体左移4位。这样就可以避免进入中断后再重复进入中断的情况。在中断程序最后,以eret指令结尾。执行的操作是pc=epc;IE=IE>>4。返回到跳中断前的下一条指令。完成整个操作。 ( _$ `! l' R, S8 J* r) |# e9 S
$ W) v' E- b. S5 S# {
![]()
6 l. U/ M Q( j6 c
' _8 M+ N# F2 G/ g下面是在外部,定时器和ps2中断的接入方法。这里注意cpu端的d_f_m出来的数据选择器要稍微改动一下,使之满足在timer1_write的时候把time1的数据接入的d_f_m。 / k: {1 `) f; d
0 b; J' F& s: U& K5 z- j# C$ s# \& E0 r3 c1 V: L5 q
然后我们来看关键几处的verilog代码 1)IE操作,如果intr1(定时器中断)=1或者外部中断(IE[2])开启、外部中断信号(ready)为1,则中断发生。IE<<4,关闭中断开关。如果eret指令,则IE>>4。 reg [31:0]IE_register; always @(posedge clk) begin if(IE_write) IE_register<=d_t_mem; else if((intr1&IE_register[0])||(ready&IE_register[2])) IE_register<=IE_register<<4; else if(i_eret) IE_register<=IE_register>>4; ! c* k/ u( v1 }9 ], q9 b' \* Z% [1 c
% }: z' o$ J# Q$ s! ?
2)PC操作,如果外部中断开关开启且外部中断来,epc=next_pc, pc=32’h14(外部中断服务程序入口地址)。如果定时器中断来,则epc=next_epc,pc=32’h10(定时器中断服务程序地址)。
# s+ Q0 w8 s; M9 h( R k4 Y
8 r! V J z3 K) c reg [31:0] pc; reg [31:0]epc; always @ (posedge clk or negedge clrn) begin if (!clrn) pc <= 0; else if(IE_register[2]&ready)begin pc<=32'h14;epc<=next_pc; end else if(intr1) begin pc<=32'h10; epc<=next_pc;end else if(i_eret) begin pc<=epc;end else pc <= next_pc; end + A& f8 R+ }( e z4 @
3)此外,为了程序运行时可以存放一些中间变量的结果,这里还定义了一个数据存储器(data_memory)。起初没有定义data_memory时,发现仅依靠r0~r31 32个寄存器放数据存储空间捉襟见肘。 / j; H) [9 I! f
0 ~+ H+ G1 C6 ~+ c/ f K# b
6 X# A$ \/ w3 \- v9 P6 ?/ E* D+ }( F; ^
@3 ^- r5 h% v2 K+ {5 G2 H9 l9 U& }% i: _1 d
至此,一些必要的硬件设施就安排完善了。 ' E' L% |0 r7 P+ e- L F
, m7 @: |* h3 O7 G/ K6 i
下面是具体的硬件地址定义信息 #0x0000_0000 :0x0000_00(外部中断)0(timer1)0(time0) IE #0x0000_0001 :0x0000_000(time1 run)0(time0run) #0x0000_0002 计数器0: 只读0-5, 用来生成形状数字,生成伪随机数。 #0x0000_0003 (timer1)定时器1: 可读写 7 g8 V! u1 T& {5 c5 k% S4 [7 v
#外部存储空间(datamemory): #0xe000_0000----e000_001e ! M2 j% P6 P2 l9 L6 `" d) `
8 [/ ] ~* G, Q6 X4 d* [6 v
(二)软件设计: s+ T* C1 E+ P. ^) A# X+ k2 H
3 C$ l) I# `; B6 B/ r' W
4 |2 t% k6 s( ]
, G; l" v5 Q6 \: z; v$ g' [# {1 c) c
由于俄罗斯方块的汇编语言程序有2000多行,不方便一一讲解。我这里只贴了大概的流程图。 总体思路是这样的,首先生成模块图形,然后定时器中断溢出一次则模块下降一格。定时器初值决定了下降速度。中断服务程序开头别忘了重新赋初值。 图形下降时如果碰到按键按左右按钮,则执行左右移位,这涉及到对应数据的左右搬移。 如果按向上键,则需要执行旋转操作。只要对数据应用旋转矩阵,就可以了。 比较难的一点是如何使模块停在下面的模块上,我的做法是首先把当先模块里的每个小方框位置记下,然后删除每个小方框中的数据。接着计算查看每个小方框下面那格是否有数据,如果有,则说明有东西,需要停下来,反之则可以继续下降,直到碰到底边为止。
0 i1 |) ^# Q( ` R' f# S右边界面还做了一点积分的小功能。 & v% T% F9 R+ V& x
(三)效果 1 o& V! m K# | V4 t# q
& f/ h) q9 O) L# i8 Z
9 D( l( N& h/ \. ~- M. I) h |