Forum > Linux PDAs
Atmel AT91SAM9G20 ARM CPU stucks after writing into some CPU registers (SOLVED)
mig-31:
I have development board with Atmel AT91SAM9G20 ARM CPU with Linux OS. This CPU has Parallel Input/Output controller with 3 lines PIOA, PIOB and PIOC. Each lines have 32 I/Os.
PIOA maps into address $FFFFF400, PIOB - $FFFFF600 and PIOC - $FFFFF800 and amout of memory for each PIOA, PIOB and PIOC registers is 512 bytes. There is a code rewrited from C example into FreePascal.
--- Code: --- program led_io;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes,SysUtils,BaseUnix,crt,errors,strutils
{ you can add units after this };
const
//map consts
PAGE_SIZE=4096;
MASK=PAGE_SIZE-1;
//BASE REGISTER ADDRESS OF PIOB
//calculating page number wtih page size 4096 = $1000 PIOB_BASE= $FFFFF600 div $1000
PIOA_BASE=$FFFFF400;
////BASE REGISTER ADDRESS OF PIOB
//SHIFT CONTROL REGISTERS ADDRESS
PIOA_PER=PIOA_BASE+$0000;
PIOA_OER=PIOA_BASE+$0010;
PIOA_IFDR=PIOA_BASE+$0024;
PIOA_SODR=PIOA_BASE+$0030;
PIOA_CODR=PIOA_BASE+$0034;
PIOA_IDR=PIOA_BASE+$0044;
PIOA_PSR=PIOA_BASE+$0008;
//BASE REGISTER ADDRESS OF PIOB
//calculating page number wtih page size 4096 = $1000 PIOB_BASE= $FFFFF600 div $1000
PIOB_BASE=$FFFFF600;
////BASE REGISTER ADDRESS OF PIOB
//SHIFT CONTROL REGISTERS ADDRESS
PIOB_PER=PIOB_BASE+$0000;
PIOB_OER=PIOB_BASE+$0010;
PIOB_IFDR=PIOB_BASE+$0024;
PIOB_SODR=PIOB_BASE+$0030;
PIOB_CODR=PIOB_BASE+$0034;
PIOB_IDR=PIOB_BASE+$0044;
PIOB_PSR=PIOB_BASE+$0008;
//calculating address due of page size 4096 = $1000 PIOB_BASE= $FFFFF600 div $1000
PIOC_BASE=$FFFFF800;
//SHIFT CONTROL REGISTERS ADDRESS
PIOC_PER=PIOC_BASE+$0000;
PIOC_OER=PIOC_BASE+$0010;
PIOC_IFDR=PIOC_BASE+$0024;
PIOC_SODR=PIOC_BASE+$0030;
PIOC_CODR=PIOC_BASE+$0034;
PIOC_IDR=PIOC_BASE+$0044;
PIOC_PSR=PIOC_BASE+$0008;
//
PIOA_A00=$FFFFFFFF;
//set 1 on PB27 on PIOB controller registers
PIOB_B27=cardinal(1 shl 15);
//set 1 on PC00 on PIOC controller registers
PIOC_C00=cardinal(1 shl 31);
//set 1 on PC00 on PIOC controller registers
PIOC_C01=cardinal(1 shl 32);
var
fd:cint;
map_base:Pointer;
i,blink_count,blink_delay:integer;
begin
clrscr;
//read data to setup blinking
write('Please enter LED blink count: ');
readln(blink_count);
write('Please enter LED blink delay ms: ');
readln(blink_delay);
//get file descriptor
fd:=fpOpen('/dev/mem',O_RdWr or O_SYNC);
if fd=-1 then begin
writeln('Cant get file description');
Halt(1);
end;
writeln('/dev/mem has opened');
//map 1 page memory 4kB
map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE div 4096);
if longint(map_base^)=-1 then begin
writeln('Memory map error. Error: ',strerror(fpGeterrno));
Halt(2);
end;
writeln('Memory mapped on address $',hexstr(map_base));
// setting registers to blink on PB29 row A PIN 31
//enable registr
Longword((map_base+(PIOB_PER AND MASK))^):=PIOB_B27;
//disable input filtr
Longword((map_base+(PIOB_IFDR AND MASK))^):=PIOB_B27;
//disable interrupt
Longword((map_base+(PIOB_IDR AND MASK))^):=PIOB_B27;
//enable output
Longword((map_base+(PIOB_OER AND MASK))^):=PIOB_B27;
// setting registers to blink on PC00 and PC01 row A PIN 31
//enable registr
Longword((map_base+(PIOC_PER AND MASK))^):=(PIOC_C00 OR PIOC_C01);
//disable input filtr
Longword((map_base+(PIOC_IFDR AND MASK))^):=(PIOC_C00 OR PIOC_C01);
//disable interrupt
Longword((map_base+(PIOC_IDR AND MASK))^):=(PIOC_C00 OR PIOC_C01);
//enable output
Longword((map_base+(PIOC_OER AND MASK))^):=(PIOC_C00 OR PIOC_C01);
//blinking
i:=0;
write('Blinking: ');
repeat
write('.');
//PB27 row A pin31
//set output data register = blink
Longword((map_base+(PIOA_SODR AND MASK))^):=PIOA_A00;
sleep(blink_delay);
//clear output data register = no blink
Longword((map_base+(PIOA_CODR AND MASK))^):=PIOA_A00;
sleep(blink_delay);
//PB27 row A pin31
//set output data register = blink
Longword((map_base+(PIOC_CODR AND MASK))^):=PIOC_C00;
Longword((map_base+(PIOB_SODR AND MASK))^):=PIOB_B27;
sleep(blink_delay);
//clear output data register = no blink
Longword((map_base+(PIOB_CODR AND MASK))^):=PIOB_B27;
Longword((map_base+(PIOC_SODR AND MASK))^):=PIOC_C01;
sleep(blink_delay);
//PC00 and PC01
//set output data register = blink
Longword((map_base+(PIOC_CODR AND MASK))^):=PIOC_C01;
Longword((map_base+(PIOC_SODR AND MASK))^):=PIOC_C00;
sleep(blink_delay);
inc(i);
until (i>blink_count) or (keypressed);
writeln(' done');
Longword((map_base+(PIOC_CODR AND MASK))^):=(PIOC_C00 OR PIOC_C01);
Longword((map_base+(PIOB_CODR AND MASK))^):=PIOB_B27;
//close file descriptor
fpClose(fd);
//unmap memory
if fpMUnMap(map_base, PAGE_size)<>0 then Halt(fpgeterrno);
end.
--- End code ---
In Linux memory maps with page size 4096 bytes ($1000), which means that I maps addresses form FFFFF000 to FFFFF1000.
But this code works only for PIOB, when the code, which controlls PIOC are commented ({},//).
If I writes value into PIOC registers, CPU is stucked. The same situation is for PIOA This means that I can write into memory address only for PIOB: from $FFFFF600 to $FFFFF799.
In case of C example all work:
--- Code: ---#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
#define PIOB_BASE 0xfffff600UL
#define PIOC_BASE 0xfffff800UL
#define PIO_C00 ((unsigned long) 1 << 0)
#define PIO_C01 ((unsigned long) 1 << 1)
#define PIO_B27 ((unsigned long) 1 << 27)
#define PIOB_PER PIOB_BASE + 0x0000
#define PIOB_OER PIOB_BASE + 0x0010
#define PIOB_IFDR PIOB_BASE + 0x0024
#define PIOB_SODR PIOB_BASE + 0x0030
#define PIOB_CODR PIOB_BASE + 0x0034
#define PIOB_IDR PIOB_BASE + 0x0044
#define PIOC_PER PIOC_BASE + 0x0000
#define PIOC_OER PIOC_BASE + 0x0010
#define PIOC_IFDR PIOC_BASE + 0x0024
#define PIOC_SODR PIOC_BASE + 0x0030
#define PIOC_CODR PIOC_BASE + 0x0034
#define PIOC_IDR PIOC_BASE + 0x0044
int main(int argc, char **argv) {
int fd, i, repetitions;
void *map_base;
if (argc > 1)
{
repetitions =(atoi(argv[1]));
}
else
{
printf("\nMissing argument\n");
printf("Usage: led REPETITIONS\n");
printf("REPETITIONS: int, that specifies number of repetitions of blinking led cycles\n\n\n");
exit(1);
}
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
{
printf("Couldn't open /dev/mem.\n");
exit(1);
}
printf("/dev/mem opened.\n");
fflush(stdout);
/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PIOB_BASE & ~MAP_MASK);
if(map_base == (void *) -1)
{
printf("Couldn't get Map-Address.\n");
exit(1);
}
printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
// Configure the registers
*((unsigned long *) (map_base + (PIOB_PER & MAP_MASK))) = PIO_B27; //enable io
*((unsigned long *) (map_base + (PIOB_IFDR & MAP_MASK))) = PIO_B27; //disable input filter
*((unsigned long *) (map_base + (PIOB_IDR & MAP_MASK))) = PIO_B27; // disable interrupt
*((unsigned long *) (map_base + (PIOB_OER & MAP_MASK))) = PIO_B27; // Enable output
*((unsigned long *) (map_base + (PIOC_PER & MAP_MASK))) = PIO_C00 | PIO_C01;
*((unsigned long *) (map_base + (PIOC_IFDR & MAP_MASK))) = PIO_C00 | PIO_C01;
*((unsigned long *) (map_base + (PIOC_IDR & MAP_MASK))) = PIO_C00 | PIO_C01;
*((unsigned long *) (map_base + (PIOC_OER & MAP_MASK))) = PIO_C00 | PIO_C01;
i = 0;
while(i < repetitions)
{
*((unsigned long *) (map_base + (PIOC_CODR & MAP_MASK))) = PIO_C00;
*((unsigned long *) (map_base + (PIOB_SODR & MAP_MASK))) = PIO_B27;
sleep(1);
*((unsigned long *) (map_base + (PIOB_CODR & MAP_MASK))) = PIO_B27;
*((unsigned long *) (map_base + (PIOC_SODR & MAP_MASK))) = PIO_C01;
sleep(1);
*((unsigned long *) (map_base + (PIOC_CODR & MAP_MASK))) = PIO_C01;
*((unsigned long *) (map_base + (PIOC_SODR & MAP_MASK))) = PIO_C00;
sleep(1);
i++;
}
*((unsigned long *) (map_base + (PIOB_CODR & MAP_MASK))) = PIO_B27;
*((unsigned long *) (map_base + (PIOC_CODR & MAP_MASK))) = PIO_C00 | PIO_C01;
if(munmap(map_base, MAP_SIZE) == -1) exit(1);
close(fd);
return 0;
}
--- End code ---
I don't know why code in FreePascal doesn't work.
Thank you for any idea.
Laksen:
map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE div 4096);
should be
map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE and (not longword(MASK));
if longint(map_base^)=-1 then begin
should be
if ptrint(map_base) = -1 then begin
I think that should at least make it work
mig-31:
Thank you for the answer. But I'm afraid that the result will be the same, because PIOB_BASE div 4096 = PIOB_BASE and (not longword(MASK).
I will try it and write the result here.
Laksen:
It's not. (PIOB_BASE div 4096)*4096 = PIOB_BASE and (not longword(MASK) would however be correct.
mig-31:
I changed
--- Code: --- map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE div 4096);
to
map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE and (not longword(MASK));
--- End code ---
It doesn't work. I get error invalid argument, which means that value of PIOB_BASE and (not longword(MASK) is not correct.
In case of page memory for 32-bit CPU first five letters in hex value is page number, other three letters is SHIFT.
I think, that PIOB_BASE div 4096 is correct and there is a page number $FFFFF. PIOB_PER AND MASK is the shift to register address PIOB_PER = $600.
Writing to register of PIOB work correctly. But when I want to write to any address of PIOC the CPU stucks. Line of code bellow doesn't work.
--- Code: --- Longword((map_base+(PIOC_PER AND MASK))^):=(PIOC_C00 OR PIOC_C01);
--- End code ---
Thank for any idea.
Navigation
[0] Message Index
[#] Next page