Line 1... |
Line 1... |
; irq_test.a51 -- First interrupt service test.
|
; irq_test.a51 -- Basic interrupt service test.
|
;
|
;
|
; This progam is only meant to work in the simulation test bench, because it
|
; This program is only meant to work in the simulation test bench, because it
|
; requires the external interrupt inputs to be wired to the P1 output port.
|
; requires the external interrupt inputs to be wired to the P1 output port.
|
; They are in the simulation test bench entity but not in the synthesizable
|
; They are in the simulation test bench entity but not in the synthesizable
|
; demo top entity.
|
; demo top entity.
|
;
|
;
|
; Its purpose is to demonstrate the working of the interrupt service logic. No
|
; Its purpose is to demonstrate the working of the interrupt service logic. No
|
; actual tests are performed (other than the co-simulation tests), only checks.
|
; actual tests are performed (other than the co-simulation tests), only checks.
|
;
|
;
|
|
; This program makes the following assumptions about the MCU configuration:
|
|
;
|
|
; 1.- Port line P1.0 is wired to external input EXTINT0.0
|
|
; 2.- The timer prescaler is set to 20us@50MHz.
|
|
;
|
|
; NOTE: I am aware that this code is perfectly hideous and nearly useless as a
|
|
; test bench; it will have to do for the time being. I can seldom find quality
|
|
; time for this project...
|
;-------------------------------------------------------------------------------
|
;-------------------------------------------------------------------------------
|
|
|
; Include the definitions for the light52 derivative
|
; Include the definitions for the light52 derivative
|
$nomod51
|
$nomod51
|
$include (light52.mcu)
|
$include (light52.mcu)
|
|
|
ext_irq_ctr set 060h ; Incremented by external irq routine
|
ext_irq_ctr set 060h ; Incremented by external irq routine
|
|
timer_irq_ctr set 062h ; Incremented by timer irq routine
|
|
uart_irq_ctr set 063h ; Incremented by uart irq routine
|
|
irq_test_code set 064h ; Selects the behavior of the irq routines
|
|
|
|
|
;-- Macros -------------------------------------------------------------
|
;-- Macros -------------------------------------------------------------
|
|
|
; putc: send character in A to console (UART)
|
; putc: send character in A to console (UART)
|
putc macro character
|
putc macro character
|
local putc_loop
|
local putc_loop
|
mov SBUF,character
|
mov SBUF,character
|
putc_loop:
|
putc_loop:
|
|
; This program will only ever run in the simulated environment, where
|
|
; UART transmission is instantaneous. No need to loop here.
|
;mov a,SCON
|
;mov a,SCON
|
;anl a,#10h
|
;anl a,#10h
|
;jz putc_loop
|
;jz putc_loop
|
endm
|
endm
|
|
|
Line 52... |
Line 65... |
ljmp irq_wrong
|
ljmp irq_wrong
|
|
|
|
|
;-- Main test program --------------------------------------------------
|
;-- Main test program --------------------------------------------------
|
org 30h
|
org 30h
|
|
|
|
; Place a few utility routines here at the start so they are reachable
|
|
; by CJNE.
|
|
|
|
; Did not get expected IRQ: print failure message and block.
|
|
fail_expected:
|
|
mov DPTR,#text3
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
|
; Got unexpected IRQ: print failure message and block.
|
|
fail_unexpected:
|
|
mov DPTR,#text1
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
|
|
start:
|
start:
|
|
|
; Disable all interrupts.
|
mov IE,#00 ; Disable all interrupts...
|
mov IE,#00
|
mov IP,#01 ; ...and set EXTINT as high-priority.
|
|
mov irq_test_code,#00h ; Tell irq routines to only inc the counters
|
|
|
|
|
;---- External interrupt test --------------------------------------
|
;---- External interrupt test --------------------------------------
|
|
|
|
; Basic interrupt test.
|
|
|
; We'll be asserting the external interrupt request line 0, making
|
; We'll be asserting the external interrupt request line 0, making
|
; sure the interrupt enable flags work properly. No other interrupt
|
; sure the interrupt enable flags work properly. No other interrupt
|
; will be asserted simultaneously or while in the interrupt service
|
; will be asserted simultaneously or while in the interrupt service
|
; routine.
|
; routine.
|
|
|
Line 82... |
Line 117... |
nop ; Wait a little...
|
nop ; Wait a little...
|
nop
|
nop
|
nop
|
nop
|
mov a,ext_irq_ctr ; ...and make sure the interrupt was NOT
|
mov a,ext_irq_ctr ; ...and make sure the interrupt was NOT
|
cjne a,#00,fail_unexpected ; serviced.
|
cjne a,#00,fail_unexpected ; serviced.
|
setb EXTINT0.0 ; Clear timer IRQ flag
|
setb EXTINT0.0 ; Clear external IRQ flag
|
|
|
; Trigger external IRQ with external and global IRQ enabled
|
; Trigger external IRQ with external and global IRQ enabled
|
mov P1,#00h ; Clear the external interrupt line...
|
mov P1,#00h ; Clear the external interrupt line...
|
mov IE,#81h ; ...before enabling interrupts globally.
|
mov IE,#81h ; ...before enabling interrupts globally.
|
mov ext_irq_ctr,#00 ; Reset the interrupt counter...
|
mov ext_irq_ctr,#00 ; Reset the interrupt counter...
|
Line 94... |
Line 129... |
nop ; Give it some time to be acknowledged...
|
nop ; Give it some time to be acknowledged...
|
nop
|
nop
|
nop
|
nop
|
mov a,ext_irq_ctr ; ...and make sure it has been serviced.
|
mov a,ext_irq_ctr ; ...and make sure it has been serviced.
|
cjne a,#01,fail_expected
|
cjne a,#01,fail_expected
|
setb EXTINT0.0 ; Clear timer IRQ flag
|
setb EXTINT0.0 ; Clear external IRQ flag
|
|
|
|
; Somewhat less basic interrupt test: priorities.
|
|
|
|
; Here we are going to use the test code byte (irq_test_code) to tell
|
|
; the interrupt routines what we want them to do. Since we only use two
|
|
; interrupt routines to test everything, each routine has to perform
|
|
; a few different roles.
|
|
; Basically we want to make sure that the irq priority rules hold:
|
|
;
|
|
; A.-Nothing can interrupt a high priority irq routine.
|
|
; B.- Only a high-priority irq can interrupt a low-priority irq.
|
|
; C.- Simultaneous irqs get ordered by their vector number.
|
|
;
|
|
; Rule C will NOT be tested in this program; and rules B and A get only
|
|
; the most basic of basic tests.
|
|
|
|
; Run test 1: Trigger another external interrupt while serving an
|
|
; external interrupt. Since both are high-priority, the timer interrupt
|
|
; should be ignored.
|
|
mov irq_test_code,#01h
|
|
mov P1,#00h ; Clear the external interrupt line...
|
|
mov IE,#83h ; ...before enabling interrupts globally.
|
|
mov ext_irq_ctr,#00 ; Reset the interrupt counter...
|
|
mov P1,#01h ; ...and assert the external interrupt.
|
|
nop ; Give it some time to be acknowledged...
|
|
nop
|
|
mov a,ext_irq_ctr ; ...and make sure it has been serviced.
|
|
cjne a,#01,fail_expected
|
|
setb EXTINT0.0 ; Clear external IRQ flag
|
|
|
|
; Run test 2: Trigger Timer interrupt while serving an external
|
|
; interrupt. Since the Timer irq is low-priority, it should be ignored.
|
|
mov irq_test_code,#02h
|
|
mov P1,#00h ; Clear the external interrupt line...
|
|
mov IE,#83h ; ...before enabling interrupts globally.
|
|
mov ext_irq_ctr,#00 ; Reset the interrupt counter...
|
|
mov P1,#01h ; ...and assert the external interrupt.
|
|
nop ; Give it some time to be acknowledged...
|
|
nop
|
|
mov a,ext_irq_ctr ; ...and make sure it has been serviced.
|
|
cjne a,#01,fail_expected
|
|
setb EXTINT0.0 ; Clear external IRQ flag
|
|
|
|
; Run test 3: Trigger interrupts within the timer interrupt service
|
|
; routine.
|
|
mov irq_test_code,#03h
|
|
mov timer_irq_ctr,#00h
|
|
mov P1,#00h ; Clear the external interrupt line...
|
|
mov IE,#83h ; ...before enabling interrupts globally.
|
|
mov ext_irq_ctr,#00 ; Reset the interrupt counter...
|
|
mov TSTAT,#01 ; Stop timer and clear timer interrupt...
|
|
mov TH,#00h ; ...set counter = 000h...
|
|
mov TL,#00h ;
|
|
mov TCH,#00h ; ...and set Compare register = 0001h...
|
|
mov TCL,#01h ;
|
|
mov TSTAT,#030h ; ...then start counting.
|
|
|
|
mov r1,#95 ; Wait for the timer IRQ to trigger...
|
|
loop_001:
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
djnz r1,loop_001 ; ...then make sure the timer irq has
|
|
mov a,timer_irq_ctr ; been triggered.
|
|
cjne a,#01h,fail_expected_irq_bridge
|
|
|
; End of irq test, print message and continue
|
; End of irq test, print message and continue
|
mov DPTR,#text2
|
mov DPTR,#text2
|
call puts
|
call puts
|
|
|
Line 107... |
Line 212... |
|
|
; All we will do here is make sure the counter changes at the right
|
; All we will do here is make sure the counter changes at the right
|
; time, i.e. 20us after being started. We will NOT test the full
|
; time, i.e. 20us after being started. We will NOT test the full
|
; functionality of the timer (not in this version of the test).
|
; functionality of the timer (not in this version of the test).
|
|
|
|
; Note that the irq tests above already assume the timer works... this
|
|
; test is somewhat redundant.
|
|
|
mov IE,#000h ; Disable all interrupts...
|
mov IE,#000h ; Disable all interrupts...
|
; ...and put timer in
|
; ...and put timer in
|
mov TSTAT,#00 ; Stop timer...
|
mov TSTAT,#00 ; Stop timer...
|
mov TH,#00 ; ...set counter = 0...
|
mov TH,#00 ; ...set counter = 0...
|
mov TL,#00 ;
|
mov TL,#00 ;
|
Line 145... |
Line 253... |
call puts
|
call puts
|
|
|
;-- End of test program, enter single-instruction endless loop
|
;-- End of test program, enter single-instruction endless loop
|
quit: ajmp $
|
quit: ajmp $
|
|
|
|
fail_expected_irq_bridge:
|
|
jmp fail_expected_irq
|
|
|
fail_timer_error:
|
fail_timer_error:
|
mov DPTR,#text4
|
mov DPTR,#text4
|
call puts
|
call puts
|
mov IE,#00h
|
mov IE,#00h
|
ajmp $
|
ajmp $
|
|
|
|
|
; Did not get expected IRQ: print failure message and block.
|
|
fail_expected:
|
|
mov DPTR,#text3
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
|
; Got unexpected IRQ: print failure message and block.
|
|
fail_unexpected:
|
|
mov DPTR,#text1
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
|
; End of the test code. Now let's define a few utility routines.
|
; End of the test code. Now let's define a few utility routines.
|
|
|
;-- puts: output to UART a zero-terminated string at DPTR ----------------------
|
;-- puts: output to UART a zero-terminated string at DPTR ----------------------
|
puts:
|
puts:
|
mov r0,#00h
|
mov r0,#00h
|
Line 183... |
Line 278... |
putc a
|
putc a
|
sjmp puts_loop
|
sjmp puts_loop
|
puts_done:
|
puts_done:
|
ret
|
ret
|
|
|
;-- irq_timer: interrupt routine for timer -------------------------------------
|
;-- irq_ext: interrupt routine for external irq lines --------------------------
|
; Note we don't bother to preserve any registers.
|
; Note we don't bother to preserve any registers.
|
irq_ext:
|
irq_ext:
|
mov P1,#00h ; Remove the external interrupt request
|
mov P1,#00h ; Remove the external interrupt request
|
mov EXTINT0,#0ffh ; Clear all external IRQ flags
|
mov EXTINT0,#0ffh ; Clear all external IRQ flags
|
inc ext_irq_ctr ; Increment irq counter
|
inc ext_irq_ctr ; Increment irq counter
|
|
; Ok, now check the test code byte to see what we have to do here.
|
|
mov a,irq_test_code
|
|
cjne a,#00h,irq_ext_0
|
|
|
|
; Test 0: Just increment irq counter (already done).
|
mov DPTR,#text0 ; Print IRQ message...
|
mov DPTR,#text0 ; Print IRQ message...
|
call puts
|
call puts
|
reti ; ...and quit
|
reti ; ...and quit
|
|
|
|
irq_ext_0:
|
|
cjne a,#02h,irq_ext_1
|
|
; Test 2: Trigger timer interrupt while in the service routine.
|
|
; Verify that low-priority interrupts get ignored while in the service
|
|
; routine of a high-priority interrupt.
|
|
mov timer_irq_ctr,#00h
|
|
mov TSTAT,#01 ; Stop timer and clear timer interrupt...
|
|
mov TH,#00h ; ...set counter = 000h...
|
|
mov TL,#00h ;
|
|
mov TCH,#00h ; ...and set Compare register = 0001h.
|
|
mov TCL,#01h ;
|
|
mov IE,#82h ; Enable timer interrupt...
|
|
mov TSTAT,#030h ; ...and start counting.
|
|
|
|
mov r0,#95 ; Wait for the timer interrupt to trigger...
|
|
irq_ext_test0_loop0:
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
djnz r0,irq_ext_test0_loop0 ; ...the make sure the timer irq has
|
|
mov a,timer_irq_ctr ; NOT triggered.
|
|
cjne a,#00h,fail_unexpected_irq
|
|
reti
|
|
|
|
irq_ext_1:
|
|
cjne a,#01h,irq_ext_2
|
|
|
|
; Test 1: Trigger external interrupt while in the service routine.
|
|
; Verify that high-priority interrupts get ignored while in the service
|
|
; routine of another high-priority interrupt.
|
|
mov ext_irq_ctr,#00h
|
|
mov irq_test_code,#00h
|
|
mov IE,#81h ; ...enable the UART irq...
|
|
mov P1,#01h ; ...and trigger it
|
|
|
|
mov r0,#10 ; Give time for the irq to trigger...
|
|
irq_ext_test2_loop0:
|
|
nop
|
|
djnz r0,irq_ext_test2_loop0 ; ...the make sure the Timer irq has
|
|
mov a,ext_irq_ctr ; NOT triggered.
|
|
cjne a,#00h,fail_unexpected_irq
|
|
reti
|
|
|
|
irq_ext_2:
|
|
; Code byte irq_test_code not used; ignored.
|
|
reti
|
|
|
|
|
|
;-- irq_timer: interrupt routine for timer -------------------------------------
|
|
; Note we don't bother to preserve any registers.
|
irq_timer:
|
irq_timer:
|
|
; Check the test code to see what we have to do here.
|
|
mov a,irq_test_code
|
|
cjne a,#03,irq_timer_0
|
|
|
|
; Test code 3: interrupts within timer irq service routine.
|
|
|
|
; Trigger external interrupt within this irq service routine and make
|
|
; sure it gets serviced.
|
|
mov ext_irq_ctr,#00h
|
|
mov irq_test_code,#00h
|
|
mov IE,#81h ; ...enable the UART irq...
|
|
mov P1,#01h ; ...and trigger it
|
|
|
|
mov r0,#10 ; Give time for the irq to trigger...
|
|
irq_timer_test3_loop0:
|
|
nop
|
|
djnz r0,irq_timer_test3_loop0 ; ...the make sure the Timer irq has
|
|
mov a,ext_irq_ctr ; NOT triggered.
|
|
cjne a,#01h,fail_expected_irq
|
|
|
|
; Ok, now re-trigger the timer interrupt within the timer service
|
|
; interrupt and make sure the new interrupt is not serviced.
|
|
mov irq_test_code,#00h
|
|
mov timer_irq_ctr,#00h
|
|
mov TSTAT,#01 ; Stop timer and clear timer interrupt...
|
|
mov TH,#00h ; ...set counter = 000h...
|
|
mov TL,#00h ;
|
|
mov TCH,#00h ; ...and set Compare register = 0001h...
|
|
mov TCL,#01h ;
|
|
mov TSTAT,#030h ; ...then start counting.
|
|
|
|
mov r1,#95 ; Wait for the timer IRQ to trigger...
|
|
irq_timer_test3_loop1:
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
nop
|
|
djnz r1,irq_timer_test3_loop1 ; ...then make sure the timer irq has
|
|
mov a,timer_irq_ctr ; been ignored.
|
|
cjne a,#00h,fail_unexpected_irq
|
|
|
|
inc timer_irq_ctr ; Increment timer interrupt counter...
|
|
mov TSTAT,#01 ; Stop timer and clear timer interrupt.
|
|
|
|
reti
|
|
|
|
|
|
irq_timer_0:
|
|
; Test code 0: increment irq counter.
|
|
; Just increment the timer irq counter and quit.
|
|
inc timer_irq_ctr ; Increment timer interrupt counter...
|
|
mov TSTAT,#01 ; Stop timer and clear timer interrupt.
|
|
reti ; ...and quit.
|
|
|
irq_wrong:
|
irq_wrong:
|
ajmp irq_wrong
|
ajmp irq_wrong
|
|
|
|
; Utility functions -- error messages to console, etc.
|
|
|
|
; Got unexpected IRQ: print failure message and block.
|
|
fail_unexpected_irq:
|
|
mov DPTR,#text1
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
|
; Did not get expected IRQ: print failure message and block.
|
|
fail_expected_irq:
|
|
mov DPTR,#text3
|
|
call puts
|
|
mov IE,#00h
|
|
ajmp $
|
|
|
; End of the utility routines. Define constant data and we're done.
|
; End of the utility routines. Define constant data and we're done.
|
|
|
text0: db '',13,10,00h,00h
|
text0: db '',13,10,00h,00h
|
text1: db 'Unexpected IRQ',13,10,00h,00h
|
text1: db 'Unexpected IRQ',13,10,00h,00h
|
text2: db 'IRQ test finished, no errors',13,10,0
|
text2: db 'IRQ test finished, no errors',13,10,0
|