adding blinker05, interrupt example
This commit is contained in:
@@ -21,8 +21,6 @@ extern void dummy ( unsigned int );
|
|||||||
#define GPSET0 0x2020001C
|
#define GPSET0 0x2020001C
|
||||||
#define GPCLR0 0x20200028
|
#define GPCLR0 0x20200028
|
||||||
|
|
||||||
#define TIMEOUT 4000000
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
int notmain ( void )
|
int notmain ( void )
|
||||||
{
|
{
|
||||||
|
|||||||
70
blinker05/Makefile
Normal file
70
blinker05/Makefile
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
ARMGNU ?= arm-none-eabi
|
||||||
|
|
||||||
|
AOPS = --warn --fatal-warnings
|
||||||
|
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gcc : blinker05.hex blinker05.bin
|
||||||
|
|
||||||
|
all : gcc clang
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -f *.o
|
||||||
|
rm -f *.bin
|
||||||
|
rm -f *.hex
|
||||||
|
rm -f *.elf
|
||||||
|
rm -f *.list
|
||||||
|
rm -f *.img
|
||||||
|
rm -f *.bc
|
||||||
|
rm -f *.clang.opt.s
|
||||||
|
|
||||||
|
vectors.o : vectors.s
|
||||||
|
$(ARMGNU)-as vectors.s -o vectors.o
|
||||||
|
|
||||||
|
blinker05.o : blinker05.c
|
||||||
|
$(ARMGNU)-gcc $(COPS) -c blinker05.c -o blinker05.o
|
||||||
|
|
||||||
|
blinker05.elf : memmap vectors.o blinker05.o
|
||||||
|
$(ARMGNU)-ld vectors.o blinker05.o -T memmap -o blinker05.elf
|
||||||
|
$(ARMGNU)-objdump -D blinker05.elf > blinker05.list
|
||||||
|
|
||||||
|
blinker05.bin : blinker05.elf
|
||||||
|
$(ARMGNU)-objcopy blinker05.elf -O binary blinker05.bin
|
||||||
|
|
||||||
|
blinker05.hex : blinker05.elf
|
||||||
|
$(ARMGNU)-objcopy blinker05.elf -O ihex blinker05.hex
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LOPS = -Wall -m32 -emit-llvm
|
||||||
|
LLCOPS = -march=arm -mcpu=arm1176jzf-s
|
||||||
|
LLCOPS0 = -march=arm
|
||||||
|
LLCOPS1 = -march=arm -mcpu=arm1176jzf-s
|
||||||
|
COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding
|
||||||
|
OOPS = -std-compile-opts
|
||||||
|
|
||||||
|
clang : blinker05.clang.hex blinker05.clang.bin
|
||||||
|
|
||||||
|
|
||||||
|
blinker05.clang.bc : blinker05.c
|
||||||
|
clang $(LOPS) -c blinker05.c -o blinker05.clang.bc
|
||||||
|
|
||||||
|
blinker05.clang.opt.elf : memmap vectors.o blinker05.clang.bc
|
||||||
|
opt $(OOPS) blinker05.clang.bc -o blinker05.clang.opt.bc
|
||||||
|
llc $(LLCOPS) blinker05.clang.opt.bc -o blinker05.clang.opt.s
|
||||||
|
$(ARMGNU)-as blinker05.clang.opt.s -o blinker05.clang.opt.o
|
||||||
|
$(ARMGNU)-ld -o blinker05.clang.opt.elf -T memmap vectors.o blinker05.clang.opt.o
|
||||||
|
$(ARMGNU)-objdump -D blinker05.clang.opt.elf > blinker05.clang.opt.list
|
||||||
|
|
||||||
|
blinker05.clang.hex : blinker05.clang.opt.elf
|
||||||
|
$(ARMGNU)-objcopy blinker05.clang.opt.elf blinker05.clang.hex -O ihex
|
||||||
|
|
||||||
|
blinker05.clang.bin : blinker05.clang.opt.elf
|
||||||
|
$(ARMGNU)-objcopy blinker05.clang.opt.elf blinker05.clang.bin -O binary
|
||||||
|
|
||||||
|
|
||||||
107
blinker05/README
Normal file
107
blinker05/README
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
|
||||||
|
See the top level README for information on where to find the
|
||||||
|
schematic and programmers reference manual for the ARM processor
|
||||||
|
on the raspberry pi. Also find information on how to load and run
|
||||||
|
these programs.
|
||||||
|
|
||||||
|
This is derived from blinker04.
|
||||||
|
|
||||||
|
I dont understand why but way too many blinker examples use interrupts.
|
||||||
|
In short only use interrupts if you have to, usually you dont have to.
|
||||||
|
If you feel you have to this example shows you a little about enabling
|
||||||
|
and handling interrupts on a raspberry pi.
|
||||||
|
|
||||||
|
Interrupts are an advanced programming topic, no matter how simple
|
||||||
|
this example makes it appear, there is a long list of things that can
|
||||||
|
and will go wrong. I definitely didnt get this working on the first
|
||||||
|
or second or third try. It took many. Fortunately on this system
|
||||||
|
we have a few layers we can play with before actually interrupting
|
||||||
|
the processor. This is both a blessing and a curse, some systems
|
||||||
|
have a number of layers, not all easy to find in the docs if in the
|
||||||
|
docs and all layers have to have the right enables, etc to route the
|
||||||
|
interrupt from the peripheral to processor. blinker03 showed a timer
|
||||||
|
with a raw interrupt status register, basically the interrupt signal
|
||||||
|
near its source. The interrupt enable bit is the first layer you
|
||||||
|
have to enable the signal through. This example kills three birds with
|
||||||
|
one stone. Instead of three examples I enable one layer at a time
|
||||||
|
and show a few blinks with each layer before completely connecting
|
||||||
|
the interrupt to the processor.
|
||||||
|
|
||||||
|
The first problem we have with the raspberry pi is where the binary
|
||||||
|
is loaded. I have had the most success by not mucking with settings
|
||||||
|
which means that our program is loaded at address 0x8000. To service
|
||||||
|
an interrupt we need to have our exception table at address 0x0000.
|
||||||
|
If you have been reading your ARM documents as you should by this point
|
||||||
|
you will know that for an interrupt on this processor it executes
|
||||||
|
the instruction at address 0x0001C. Also the interrupt mode has
|
||||||
|
a different r13 or stack pointer than supervisor mode. So if we plan
|
||||||
|
to use the stack at all in the interrupt handler we need to prepare
|
||||||
|
that stack pointer before any interrupts come along. Before we get
|
||||||
|
to that looking at vectors.s. We know that 0x8000 is our entry point
|
||||||
|
where _start will be. We know that is not the exception table but I
|
||||||
|
have made one there anyway, why? Using the ldr pc,label instruction
|
||||||
|
I am letting the assembler do the work of creating the machine code
|
||||||
|
for a relative load. Offset 0x0000 loads into the program counter
|
||||||
|
(same as doing a branch if you are not switching modes) the address at
|
||||||
|
offset 0x0020 which is filled in (by the assembler) the address for
|
||||||
|
the reset label. This repeats one for one for 8 handlers 0x0004 gets
|
||||||
|
address in 0x0024, etc. I happen to know and you can look up the
|
||||||
|
fact that ldr pc,label when used this way uses relative addressing
|
||||||
|
|
||||||
|
8000: e59ff018 ldr pc, [pc, #24] ; 8020 <reset_handler>
|
||||||
|
...
|
||||||
|
00008020 <reset_handler>:
|
||||||
|
8020: 00008040 andeq r8, r0, r0, asr #32
|
||||||
|
|
||||||
|
The machine instruction 0xe59ff018 when placed at address 0x1000 will
|
||||||
|
read from address 0x1000+0x20, basically pc+0x20 (the disassembly is
|
||||||
|
misleading). So if I were to copy the instruction at 0x8000 to 0x0000
|
||||||
|
and the address at 0x8020 to 0x0020, then it still works, a reset exception
|
||||||
|
if we could create one would read 0x8040 in this case and put that in
|
||||||
|
the pc, causing a jump to 0x8040. Same is true for all 8 instructions and
|
||||||
|
all 8 addresses. So the first thing the code does after reset is
|
||||||
|
copy those 16 words from 0x8000 to 0x0000, we didnt have to figure out
|
||||||
|
addresses for functions and didnt have to generate machine code this
|
||||||
|
approach let the assembler do the work.
|
||||||
|
|
||||||
|
The next thing the reset code does is set up the stack pointer, not
|
||||||
|
much different than the other example programs except in this case
|
||||||
|
we need to setup both the supervisor mode, which we normally run
|
||||||
|
these examples in, and interrupt mode. The modes determine which
|
||||||
|
registers are used, the stack pointer being one that has a different
|
||||||
|
register for each mode. The trick is to use the mrs instruction to
|
||||||
|
change some of the bits in the program status register (psr) the bits
|
||||||
|
in particular we care about are the mode bits. Change the mode to
|
||||||
|
interrupt, set the interrupt stack pointer, change the mode back to
|
||||||
|
supervisor and change the stack pointer, then continue by calling the
|
||||||
|
entry C function, notmain().
|
||||||
|
|
||||||
|
Just like blinker04, blinker05 configures the gpio pin for driving the
|
||||||
|
led and sets up the timer. Different from blinker05, interrupts are
|
||||||
|
enabled in the control register (bit 5). Using the assumption that this
|
||||||
|
program was run just after reset, either as kernel.img on the sd card
|
||||||
|
or using one of my bootloaders that doesnt mess with enabling interrupts,
|
||||||
|
etc. The interrupt will leave the timer but get stopped at the next layer.
|
||||||
|
Since it is leaving the timer we will be able to see it in the masked
|
||||||
|
interrupt status register. So the first 5 blinks, once state change
|
||||||
|
per second, are based on polling the MIS register.
|
||||||
|
|
||||||
|
The next layer is between the timer and the ARM. Each chip vendor and
|
||||||
|
model can vary wildly as to how many layers and how to use them. In
|
||||||
|
this case we have a relatively simple interrupt enable . By setting
|
||||||
|
a bit in the irq enable register we are allowing the arm timer interrupt
|
||||||
|
to pass through to the ARM core. Because our early cpsr registers
|
||||||
|
manipulation (to set up the interrupt stack) forced the interrupt
|
||||||
|
enable for the ARM core to a one, ARM core interrupts are disabled.
|
||||||
|
The next 5 blinks at 2 seconds per state change poll the interrupt
|
||||||
|
status register at this layer.
|
||||||
|
|
||||||
|
The last step is to enable the interrupts to the arm core by changing
|
||||||
|
the I bit in the cpsr to a zero. Because we had prepared the exception
|
||||||
|
table when the interrupt comes the code at address 0x1C is executed
|
||||||
|
which eventually hits the c_irq_handler function, remember this is
|
||||||
|
an interrupt handler, you are soemwhat running parallel. Dont take
|
||||||
|
very long and dont mess with resources in use by the foreground
|
||||||
|
application. typically you want to be in and out fast. At some
|
||||||
|
point in the isr you need to clear the source of the isr.
|
||||||
|
|
||||||
102
blinker05/blinker05.c
Normal file
102
blinker05/blinker05.c
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
extern void PUT32 ( unsigned int, unsigned int );
|
||||||
|
extern unsigned int GET32 ( unsigned int );
|
||||||
|
extern void dummy ( unsigned int );
|
||||||
|
extern void enable_irq ( void );
|
||||||
|
extern void enable_fiq ( void );
|
||||||
|
|
||||||
|
|
||||||
|
#define ARM_TIMER_LOD 0x2000B400
|
||||||
|
#define ARM_TIMER_VAL 0x2000B404
|
||||||
|
#define ARM_TIMER_CTL 0x2000B408
|
||||||
|
#define ARM_TIMER_CLI 0x2000B40C
|
||||||
|
#define ARM_TIMER_RIS 0x2000B410
|
||||||
|
#define ARM_TIMER_MIS 0x2000B414
|
||||||
|
#define ARM_TIMER_RLD 0x2000B418
|
||||||
|
#define ARM_TIMER_DIV 0x2000B41C
|
||||||
|
#define ARM_TIMER_CNT 0x2000B420
|
||||||
|
|
||||||
|
#define SYSTIMERCLO 0x20003004
|
||||||
|
#define GPFSEL1 0x20200004
|
||||||
|
#define GPSET0 0x2020001C
|
||||||
|
#define GPCLR0 0x20200028
|
||||||
|
|
||||||
|
#define IRQ_BASIC 0x2000B200
|
||||||
|
#define IRQ_PEND1 0x2000B204
|
||||||
|
#define IRQ_PEND2 0x2000B208
|
||||||
|
#define IRQ_FIQ_CONTROL 0x2000B210
|
||||||
|
#define IRQ_ENABLE_BASIC 0x2000B218
|
||||||
|
#define IRQ_DISABLE_BASIC 0x2000B224
|
||||||
|
|
||||||
|
volatile unsigned int icount;
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
void c_irq_handler ( void )
|
||||||
|
{
|
||||||
|
icount++;
|
||||||
|
if(icount&1)
|
||||||
|
{
|
||||||
|
PUT32(GPCLR0,1<<16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PUT32(GPSET0,1<<16);
|
||||||
|
}
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
int notmain ( void )
|
||||||
|
{
|
||||||
|
unsigned int ra;
|
||||||
|
|
||||||
|
PUT32(IRQ_DISABLE_BASIC,1);
|
||||||
|
|
||||||
|
ra=GET32(GPFSEL1);
|
||||||
|
ra&=~(7<<18);
|
||||||
|
ra|=1<<18;
|
||||||
|
PUT32(GPFSEL1,ra);
|
||||||
|
PUT32(GPSET0,1<<16);
|
||||||
|
|
||||||
|
PUT32(ARM_TIMER_CTL,0x003E0000);
|
||||||
|
PUT32(ARM_TIMER_LOD,1000000-1);
|
||||||
|
PUT32(ARM_TIMER_RLD,1000000-1);
|
||||||
|
PUT32(ARM_TIMER_DIV,0x000000F9);
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
PUT32(ARM_TIMER_CTL,0x003E00A2);
|
||||||
|
|
||||||
|
for(ra=0;ra<5;ra++)
|
||||||
|
{
|
||||||
|
PUT32(GPCLR0,1<<16);
|
||||||
|
while(1) if(GET32(ARM_TIMER_MIS)) break;
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
PUT32(GPSET0,1<<16);
|
||||||
|
while(1) if(GET32(ARM_TIMER_MIS)) break;
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT32(ARM_TIMER_LOD,2000000-1);
|
||||||
|
PUT32(ARM_TIMER_RLD,2000000-1);
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
PUT32(IRQ_ENABLE_BASIC,1);
|
||||||
|
for(ra=0;ra<5;ra++)
|
||||||
|
{
|
||||||
|
PUT32(GPCLR0,1<<16);
|
||||||
|
while(1) if(GET32(IRQ_BASIC)&1) break;
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
PUT32(GPSET0,1<<16);
|
||||||
|
while(1) if(GET32(IRQ_BASIC)&1) break;
|
||||||
|
PUT32(ARM_TIMER_CLI,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT32(ARM_TIMER_LOD,4000000-1);
|
||||||
|
PUT32(ARM_TIMER_RLD,4000000-1);
|
||||||
|
icount=0;
|
||||||
|
enable_irq();
|
||||||
|
while(1) continue;
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
12
blinker05/memmap
Normal file
12
blinker05/memmap
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
ram : ORIGIN = 0x8000, LENGTH = 0x1000
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
.text : { *(.text*) } > ram
|
||||||
|
.bss : { *(.bss*) } > ram
|
||||||
|
}
|
||||||
|
|
||||||
78
blinker05/vectors.s
Normal file
78
blinker05/vectors.s
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
.globl _start
|
||||||
|
_start:
|
||||||
|
ldr pc,reset_handler
|
||||||
|
ldr pc,undefined_handler
|
||||||
|
ldr pc,swi_handler
|
||||||
|
ldr pc,prefetch_handler
|
||||||
|
ldr pc,data_handler
|
||||||
|
ldr pc,unused_handler
|
||||||
|
ldr pc,irq_handler
|
||||||
|
ldr pc,fiq_handler
|
||||||
|
reset_handler: .word reset
|
||||||
|
undefined_handler: .word hang
|
||||||
|
swi_handler: .word hang
|
||||||
|
prefetch_handler: .word hang
|
||||||
|
data_handler: .word hang
|
||||||
|
unused_handler: .word hang
|
||||||
|
irq_handler: .word irq
|
||||||
|
fiq_handler: .word hang
|
||||||
|
|
||||||
|
reset:
|
||||||
|
mov r0,#0x8000
|
||||||
|
mov r1,#0x0000
|
||||||
|
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||||
|
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||||
|
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||||
|
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
|
||||||
|
|
||||||
|
|
||||||
|
;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||||
|
mov r0,#0xD2
|
||||||
|
msr cpsr_c,r0
|
||||||
|
mov sp,#0x8000
|
||||||
|
|
||||||
|
;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||||
|
mov r0,#0xD1
|
||||||
|
msr cpsr_c,r0
|
||||||
|
mov sp,#0x4000
|
||||||
|
|
||||||
|
;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
|
||||||
|
mov r0,#0xD3
|
||||||
|
msr cpsr_c,r0
|
||||||
|
mov sp,#0x8000000
|
||||||
|
|
||||||
|
;@ SVC MODE, IRQ ENABLED, FIQ DIS
|
||||||
|
;@mov r0,#0x53
|
||||||
|
;@msr cpsr_c, r0
|
||||||
|
|
||||||
|
bl notmain
|
||||||
|
hang: b hang
|
||||||
|
|
||||||
|
.globl PUT32
|
||||||
|
PUT32:
|
||||||
|
str r1,[r0]
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.globl GET32
|
||||||
|
GET32:
|
||||||
|
ldr r0,[r0]
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.globl dummy
|
||||||
|
dummy:
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
.globl enable_irq
|
||||||
|
enable_irq:
|
||||||
|
mrs r0,cpsr
|
||||||
|
bic r0,r0,#0x80
|
||||||
|
msr cpsr_c,r0
|
||||||
|
bx lr
|
||||||
|
|
||||||
|
irq:
|
||||||
|
push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
|
||||||
|
bl c_irq_handler
|
||||||
|
pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
|
||||||
|
subs pc,lr,#4
|
||||||
|
|
||||||
Reference in New Issue
Block a user