《Xilinx FPGA设计与实践教程》课件-第16章_第1页
《Xilinx FPGA设计与实践教程》课件-第16章_第2页
《Xilinx FPGA设计与实践教程》课件-第16章_第3页
《Xilinx FPGA设计与实践教程》课件-第16章_第4页
《Xilinx FPGA设计与实践教程》课件-第16章_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十六章PicoBlaze中断16.1PicoBlaze中断处理机制16.2外部中断接口16.3软件开发16.4设计举例本章小结

16.1PicoBlaze中断处理机制

16.1.1软件中断处理过程

与中断相关的指令在13.2.9节中讨论过。enableinterrupt和disableinterrupt指令使能和禁止中断请求,对应地,还有两个返回中断指令:returnienable和returnidisable指令,用来返回到原来的中断点。

典型的带有中断服务程序流程的软件执行过程如图16-1所示。它主要包括如下几个部分:

(1)初始化enableinterrupt指令:用来允许中断服务请求。由于中断请求默认是禁止的,所以在初始化时需要打开。

(2)跳转(jump)到指令存储器最后一条指令(如3FF):跳转到中断服务子程序。

(3)中断服务子程序:执行中断请求服务,中断服务程序必须以returni指令结束。假设正在执行adds0,s3指令时,有外部I/O有效中断发生,则PicoBlaze执行中断的步骤如下:

(1)完成当前执行指令;

(2)保存程序计数器内容,清除中断标志i(置为0),保存零标志和进位标志,程序计数器置为3FF;

(3)在3FF地址执行jumpisr指令;

(4)执行中断服务子程序;

(5)执行returni指令,恢复程序计数器和标志位;

(6)重新开始执行subs5,01指令。图16-1中断流程图16.1.2中断时序描述

中断程序执行详细的时序参数的描述如图16-2所示。按照如图16-2所示时序标注时间点描述基本时序如下:

(1)在t1时刻,外部中断接口置位中断信号,PicoBlaze此时继续正常操作,执行完当前adds0,s3指令。

(2)在t2时刻,PicoBlaze发现有中断信号来了,所以放弃执行下一条指令(subs5,01),而去执行call3FF指令。

(3)在t3时刻,PicoBlaze置位中断响应信号,表示要对中断请求进行处理,同时保存subs5,01指令的地址以及清零标志和进位标志,清除中断。

(4)在t4时刻,PicoBlaze装载并执行地址3FF的跳转指令jumpisr,外部中断接口电路响应中断响应信号并撤销中断信号。

(5)在t5时刻,PicoBlaze开始执行中断服务程序。图16-2PicoBlaze中断时序描述图 16.2外部中断接口

16.2.1单个中断请求

如果在PicoBlaze系统外设中仅有一个I/O接口能够产生中断请求,中断接口电路仅需要一个标志寄存器,如图16-3所示。当中断请求后,外部I/O接口电路置位中断请求信号一个时钟周期,置位中断标志寄存器激活PicoBlaze的中断输入。如果PicoBlaze中断允许,则它便通过置位中断响应信号一个时钟周期来响应中断并清零中断标志位。图16-3单个请求中断接口图16.2.2多个中断请求

在PicoBlaze系统中处理两个或者多个中断请求比较复杂。PicoBlaze必须决定处理哪个中断请求,然后在接收中断请求后清零对应的中断标志寄存器,这就需要协调硬件接口和中断服务子程序。

带有两个中断请求的中断接口如图16-4所示。两个独立的中断请求intrequest0和intrequest1连接两个中断标志触发器。中断标志触发器的输出通过一个或门产生最终的中断请求信号。除此之外,这两个信号同时连接到一个2选1选择器的输入端。当PicoBlaze发现中断请求时,它不知道是哪个外设发出请求或者是否是两个外设同时发出请求,中断服务子程序必须首先输入两个请求信号,检查其中断优先级值,然后执行对应的服务程序。图16-4多个中断请求接口图除此之外,PicoBlaze还需要清零对应的中断标志触发器。interrupt_ack信号不能直接用来清零中断标志触发器,因为它不知道哪一个外设请求刚才被PicoBlaze接受了。需要采用特殊的输出逻辑译码电路产生一个清零脉冲信号clr。每个中断标志触发器的清零信号对应唯一的端口id号。在中断服务子程序中,在决定接受哪一个中断请求后,增加一条output指令。该指令实际上并不输出任何数据,而是产生一个周期的脉冲信号,用来清零对应的中断标志触发器。 16.3软件开发

16.3.1中断处理主程序

基于微处理器的应用通常都采用下面软件结构:

call

初始化程序

forever:

call

子程序任务1;

call

子程序任务2;

...

call

子程序任务n;

jumpforever;一些任务有可能包括I/O操作。在执行过程中,微处理器轮流检查I/O状态并对应进行处理。程序结构执行按照轮流循环处理的方式来操作,每个任务都在等待轮流到自己才能被执行。如果微处理器循环周期足够短,以至于每个I/O请求都可以被检查并及时执行,则这种轮换处理的方案可以正常运行。在有些应用中,存在一到两个需要立即执行的请求,需要中断机制来保证该请求得到及时处理。

由于中断任何时候都可能发生,因而原始的轮流机制需要考虑中断频率并且每次中断请求需要一定的执行时间。如果有多个中断请求,则服务子程序就比较复杂。16.3.2中断服务程序

中断服务程序和子程序相类似,它挂起正常程序执行,先执行一个独立的任务,然后再重新返回到原来的操作。然而,与子程序调用不同点在于中断可以在任何时间发生,为了后来能够返回到原来程序执行的地方,中断服务程序必须保存PicoBlaze处理器的当前状态。也就是说,中断服务程序必须在返回正常程序流程之前保存在中断服务子程序中所用到的临时计算值或寄存器值。这个过程就称为内容交换。由于PicoBlaze是一个8位微处理器,硬件支持的前后交换和时间片轮转功能非常有限,所以通常需要在程序中完成这些功能并且保证中断结构比较简单。为了防止前后交换,可以将一些特定的寄存器在中断程序中作为专用寄存器。 16.4设计举例

16.4.1中断接口

如图16-5所示,框图包括了硬件计数器和中断接口以及新添加的输出缓冲器。计数器为模500计数器,每500个时钟周期产生一个单时钟脉冲。由于计数器时钟为50MHz,所以该时钟脉冲的周期为0.1ms。由于仅有一个中断请求,因而在中断接口应设计标志触发器。计数器产生的时钟脉冲触发标志寄存器,从而激活中断信号。16.4.2中断服务子程序开发

在中断服务子程序中,使用两个寄存器count_msb和count_lsb合并成一个16位的寄存器,来追踪PicoBlaze产生的脉冲信号,当每次计数器中断信号到来时,该寄存器加1,总共可以计数的最大时间为0.6s(216 × 1.01ms)。中断相关代码如下:

nameregse,count_msb;

nameregsf,count_lsb;

int_service_routine:

addcount_lsb,01;

addcycount_msb,00

returni

enable

;======================================

;interruptvector

;======================================

address3FF

jumpint_service_routine16.4.3汇编程序开发

根据设计目的,熟悉时序信息之后,可以针对LED显示设计一个新的子程序display_mux_out来替换在第十五章中用到的disp_led子程序。如图16-5所示,两个输出缓冲器用来存储an和sseg信号,子程序的主要任务是存储an模式,包括“1110”、“1101”、“1011”和“0111”,对应的七段数码管模式周期性地输出到相应寄存器,刷新率在几百赫兹到1000Hz,每210个时钟脉冲更新一次寄存器值,大约10ms。我们同样使用led_pos寄存器来跟踪显示位置。使用中断实现程序15-3,代码需要做如下修改:

(1)增加定义新的端口和寄存器;

(2)用display_mux_out子程序替换原来的disp_led子程序;

(3)增加enableinterrupt指令在init子程序中使能中断处理;

(4)在初始化程序中初始化led_pos、count_msb和count_lsb寄存器;

(5)增加中断服务子程序。

修改之后的汇编代码如程序16-1所示。图16-5带计数器的中断接口图

【程序16-1】带硬件乘法器和UART接口的求平方电路。

;程序操作:

;从拨码开关读取a和b,计算a × a+b × b并在超级终端显示结果和七段数码管值

;=============================================

;数据常数

;=============================================

constanta_lsb,00

constantb_lsb,02

constantaa_lsb,04

constantaa_msb,05

constantbb_lsb,06

constantbb_msb,07

constantaabb_lsb,08

constantaabb_msb,09

constantaabb_cout,0A

constantled0,10

constantled1,11

constantled2,12

constantled3,13

;=============================================

;寄存器定义

;=============================================

;局部寄存器

nameregs0,data ;临时数据寄存器

nameregs1,addr ;临时存储器和I/O端口地址

nameregs2,i ;通用循环计数器变量

;全局变量定义

nameregsc,switch_a_b ;当前拨码开关输入作为RAM存储偏移地址

nameregsb,led_pos ; LED显示位置(0,1,2或3)

nameregse,count_msb ;计数脉冲计数器高8位

nameregsf,count_lsb ;计数脉冲计数器低8位

;===============================================

;端口定义

;===============================================

;------------输入端口定义---------------------

constantrd_flag_port,00

;2位flag标志

(xxxxxxsc):

constantsw_port,01 ; 8位拨码开关

;------------输出端口定义---------------------

constantan_port,00

constantsseg_port,01

;==============================================

;主程序

callinit ;初始化

forever:

;主程序循环

callproc_btn ;检查和处理按钮

callsquare ;计算求平方

callload_led_pttn ;存储LED显示模式值到RAM中

calldisplay_mux_out ;动态扫描显示LED

jumpforever

;================================================

;子程序: init

;功能:执行初始化并清零寄存器和RAM值

;输出寄存器:

; switch_a_b:清零clearedto0

;临时寄存器:data,i

;===============================================

init:

enableinterrupt

;清零寄存器

loadi,40 ;循环索引值初始化为64

loaddata,00

clr_mem_loop:

storedata,(i)

subi,01 ;循环减1

jump

nz,clr_mem_loop ;重复,直到i=0

;清零寄存器

loadswitch_a_b,00

loadled_pos,00

loadcount_msb,00

loadcount_lsb,00

return

;================================================

;子程序:proc_btn

;功能:检查两个按键并处理显示

;输入寄存器:

;switch_a_b:ramoffset(0为a,2为b)

;输出寄存器:

;s3:存储输入端口标志

;switch_a_b:可交替锁存0和2

;临时寄存器:data,addr

;================================================

proc_btn:

inputs3,rd_flag_port ;获取标志

;checkandprocesscbutton

tests3,01 ;检查按键c标志

jumpz,chk_btns ;若未设置,则跳转到chk_btns

callinit ;若设置,则执行初始化程序int

jumpproc_btn_done

chk_btns:

;检查和处理按键s

tests3,02 ;检查按钮s标志位

jumpz,proc_btn_done ;标志未设置

inputdata,sw_port ;获取拨码开关值

loadaddr,a_lsb ;获取数据a地址

addaddr,switch_a_b ;获取地址偏移

storedata,(addr) ;写数据到RAM中

;更新当前显示位置

xorswitch_a_b,02 ; switch_a_b在00和02之间锁存

proc_btn_done:

return

;===============================================

;子程序名:load_led_pttn

;程序功能:读取拨码开关低三位输入并将对应显示值转换成数码管显示形式,装载到RAM中

;拨码开关输入译码值:000:a;001:b;010:a2;011:b2;其他a2+b2

;临时寄存器:data,addr

;s6:从sw输入数据

;其中所调用的hex_to_led、get_lower_nibble、get_lupper_nible可参考求平方和子程序

;=================================================

load_led_pttn:

inputs6,sw_port ;获取拨码开关输入

sl0s6 ; s6内容右移一位,获取地址偏移值

compares6,08 ;判断sw是否大于100

jumpc,sw_ok ;否

loads6,08 ;是,sw出错,按默认执行

sw_ok:

;处理0字节低4位

loadaddr,a_lsb

addaddr,s6 ;获取低地址

fetchdata,(s6) ;获取低字节

callget_lower_nibble ;获取低4位

callhex_to_led ;转换成led显示模式

storedata,led0

;处理0字节高4位

fetchdata,(addr)

callget_upper_nibble

callhex_to_led

storedata,led1

;处理1字节低4位

addaddr,01 ;获取高地址

fetchdata,(addr)

callget_lower_nibble

callhex_to_led

storedata,led2

;处理1字节高4位

fetchdata,(addr)

callget_upper_nibble

callhex_to_led

;检查sw是否等于100来处理led小数点的进位

compares6,08 ;是否显示最终结果

jump

nz,led_done ;否

addaddr,01 ;获取进位地址

fetchs6,(addr) ;寄存器存储进位

tests6,01 ;测试进位寄存器是否为1

jump

z,led_done ;否

anddata,7F ;是,赋值最高位(dp)为0

led_done:

storedata,led3

return

;=====================================

;子程序:square

;功能:计算a × a+b × b

;数据/结果存储RAM起始地址为SQ_BASE_ADDR

;临时寄存器:s3,s4,s5,s6,data

;=======================================

square:

;计算a × a

fetchs3,a_lsb ;取a值

fetchs4,a_lsb ;取b值

callmult_hard ;计算a × a值

stores6,aa_lsb ;存储a × a的低字节

stores5,aa_msb ;存储a × a的高字节

;计算b × b

fetchs3,b_lsb ;取b值

fetchs4,b_lsb ;取b值

callmult_hard ;计算b × b值

stores6,bb_lsb ;存储b × b的低字节

stores5,bb_msb ;存储b × b的高字节

;计算a × a+b × b

fetchdata,aa_lsb ;获取a × a的低字节

adddata,s6 ;求和a × a + b × b的低字节

storedata,aabb_lsb ;存储a × a + b × b的高字节

fetchdata,aa_msb ;获取a × a的高字节

addcydata,s5 ;求和a × a + b × b的高字节

storedata,aabb_msb ;存储a × a + b × b的高字节

loaddata,00 ;清零data,但是保持进位寄存器值

addcydata,00 ;从前一次加法运算获取进位值

storedata,aabb_cout ;存储a × a + b × b的进位值

return

;==============================================

;程序名称:mult_soft

;功能:利用移位和加法运算完成8位的无符号整数乘法

;输入寄存器:s3:被乘数,s4:乘数

;输出寄存器:s5:乘积高字节,s6:乘积低字节

;临时寄存器:i

;=============================================

mult_soft:

loads5,00 ;清零s5

loadi,08 ;初始化循环次数

mult_loop:

sr0,s4 ; s4最低位移位至进位寄存器

jump

nc,shift_prod ;最低位是0

adds5,s3 ;最低位是1

shift_prod:

sras5 ;右移高字节

sras6 ;右移低字节

subi,01 ;循环减1

jump

nz,mult_loop

return

;=============================================

;子程序名:display_mux_out

;程序功能:产生4位七段数码管的使能和显示模式信号

;输入寄存器:

;count_msb,count_lsb:16位计数器

;led_pos:当前LED位置

;输出寄存器:

;led_pos:更新LED位置

;临时寄存器:data,addr

;================================================

display_mux_out:

comparecount_msb,02 ;计数器赋值为00000100_00000000

jumpc,mux_out_done

;如果count大于20,则清零计数器

loadcount_lsb,00

loadcount_msb,00

;更新七段数码管显示位置

addled_pos,01

compareled_pos,04

jumpnz,gen_an_signal

loadled_pos,00 ; led位置循环显示

gen_an_signal:

;产生4位使能信号

loaddata,0E ; xxxx_1110

compareled_pos,00

jumpz,shift_an_0

compareled_pos,01

jumpz,shift_an_1

compareled_pos,02

jumpz,shift_an_2

sl1data ;移位1110三次

shift_an_2:

sl1data ;移位1110两次

shift_an_1:

sl1data ;移位1110一次

shift_an_0:

outputdata,an_port

;输出七段数码管显示模式

loadaddr,led0

addaddr,led_pos

fetchdata,(addr)

outputdata,sseg_port

mux_out_done:

return

;===============================================

;子程序名:interruptserviceroutine

;功能:16位加法器

;输入寄存器:

;count_msb,count_lsb:timercount

;输出寄存器:

;count_msb,count_lsb:incremented

;===========================================

int_service_routine:

addcount_lsb,01;16位加法器

addcycount_msb,00

returnienable

;===========================================

;中断向量

;===========================================

address3FF

jumpint_service_routine16.4.4HDL代码开发

基于中断的求平方电路I/O接口包括三个部分,输入接口与图15-4相似,输出接口包括一个译码电路和两个针对an和sseg信号的输出寄存器,中断接口包括一个计数器和标志寄存器,如图16-5所示。HDL描述如程序16-2所示。

【程序16-2】基于中断的求平方电路HDL描述。

modulepico_int

(

input

wireclk,reset,

input

wire[7:0]sw,

input

wire[1:0]btn,

output

wire[3:0]an,

output

wire[7:0]sseg

);

//信号声明

//KCPSM3/ROM信号

wire[9:0]address;

wire[17:0]instruction;

wire[7:0]port_id,out_port;

reg[7:0]in_port;

wirewrite_strobe,read_strobe;

wireinterrupt,interrupt_ack;

//I/O端口信号

//输出使能

reg[1:0]en_d;

// 4位七段数码管显示

reg[7:0]sseg_reg;

reg[3:0]an_reg;

//两个按键

regbtnc_flag_reg,btns_flag_reg;

wirebtnc_flag_next,btns_flag_next;

wireset_btnc_flag,set_btns_flag,clr_btn_flag;

//中断相关信号

reg[8:0]timer_reg;

wire[8:0]timer_next;

wireten_us_tick;

regtimer_flag_reg;

wiretimer_flag_next;

//程序主体

//==========================================

//I/O模块

//==========================================

debouncebtnc_unit

(.clk(clk),.reset(reset),.sw(btn[0]),

.db_level(),.db_tick(set_btnc_flag));

debouncebtns_unit

(.clk(clk),.reset(reset),.sw(btn[1]),

.db_level(),.db_tick(set_btns_flag));

//===========================================

//KCPSM3和ROM例化

//===========================================

kcpsm3proc_unit

(.clk(clk),.reset(1'b0),.address(address),

.instruction(instruction),.port_id(port_id),

.write_strobe(write_strobe),.out_port(out_port),

.read_strobe(read_strobe),.in_port(in_port),

.interrupt(interrupt),.interrupt_ack(interrupt_ack));

int_romrom_unit

(.clk(clk),.address(address),

.instruction(instruction));

//=========================================

//输出接口

//=========================================

//输出端口id:

//0x00:an

//0x01:ssg

//=========================================

//寄存器

always@(posedgeclk)

begin

if(en_d[0])

an_reg<=out_port[3:0];

if(en_d[1])

sseg_reg<=out_port;

end

assignan=an_reg;

assignsseg=sseg_reg;

//使能信号译码电路

always

@*

if(write_strobe)

case(port_id[0])

1'b0:en_d=2'b01;

1'b1:en_d=2'b10;

endcas

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论