Atmel AT91SAM9G20 ARM CPU stucks after writing into some CPU registers (SOLVED) lazarus / 37-Linux PDAs ---------------------------------------------------------------------- mig-31 · 2013-08-30 09:40 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: [Select]   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.                          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: [Select]#include #include #include #include #include #include #include #include #include #include #include   #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; } I don't know why code in FreePascal doesn't work. Thank you for any idea. ---------------------------------------------------------------------- Laksen · 2013-08-30 16:15 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 · 2013-08-31 10:34 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 · 2013-08-31 10:41 It's not. (PIOB_BASE div 4096)*4096 =  PIOB_BASE and (not longword(MASK) would however be correct. ---------------------------------------------------------------------- mig-31 · 2013-09-02 14:11 I  changed Code: [Select]    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)); 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: [Select]  Longword((map_base+(PIOC_PER AND MASK))^):=(PIOC_C00 OR PIOC_C01); Thank for any idea. ---------------------------------------------------------------------- Laksen · 2013-09-02 15:05 map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PIOB_BASE  & ~MAP_MASK); is the same as this: (there was a parenthesis missing) map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE and (not longword(MASK))); "PIOB_BASE  & ~MAP_MASK" is $FFFFF000 "PIOB_BASE and (not longword(MASK))" is $FFFFF000 "PIOB_BASE div 4096" is $000FFFFF The last parameter for fpmmap is an offset, not a page number. This means it's added in address calculations ---------------------------------------------------------------------- mig-31 · 2013-09-02 18:30 Now I have a jam in my mind with address calculation.  After one day discovering, I read that offset in fpmmap fuction is the page offset, not offset in bytes in C example. This means that PIOB_BASE div 4096 is the page number. And now I don't know how exactly I should get the offset to registers, for example to PIOB_IFDR. In CPU specification offset from PIOB_BASE ($FFFFF600) is $0024. Is a part code bellow correct? Code: [Select]    const       PIOB_BASE=$0600;       PIOB_IFDR=(PIOB_BASE+$0024);     begin       map_base:=fpmmap(Nil,PAGE_SIZE,PROT_READ or PROT_WRITE,MAP_SHARED ,fd, PIOB_BASE div 4096);       Longword((map_base+PIOB_IFDR):=PIOB_B27;     end. ---------------------------------------------------------------------- mig-31 · 2013-09-09 14:25 The problem solved. The problem was, that I try to write bits (32-bit control register), which is already used by another process or OS.  . But there is important moment. FreePascal used new call of mmap function, where the offset is the page number offset (C use old implementation of mmap function with byte offset). Example of using fpmmap do not exactly specify offset unit (byte/page number)!!! There is information, who interested for ARM programming under Linux using FreePascal. Board http://www.taskit.de/en/products/portuxg20/index.htm Code example of LED blinking on PIOA, PIOB and PIOC Code: [Select]program led_2; {$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;   //BASE REGISTER ADDRESS OF PIOB   //calculating page number wtih page size 4096 = $1000 PIO_BASE= $FFFFF400 div $1000   PIO_BASE=$FFFFF;   //SHIFT TO CONTROL REGISTERS ADDRESS PIOA   PIOA_PER=$400;   PIOA_OER=$410;   PIOA_IFDR=$424;   PIOA_SODR=$430;   PIOA_CODR=$434;   PIOA_IDR=$444;   PIOA_PSR=$408;   //SHIFT TO CONTROL REGISTERS ADDRESS PIOB   PIOB_PER=$600;   PIOB_OER=$610;   PIOB_IFDR=$624;   PIOB_SODR=$630;   PIOB_CODR=$634;   PIOB_IDR=$644;   PIOB_PSR=$608;   //SHIFT TO CONTROL REGISTERS ADDRESS PIOA   PIOC_PER=$800;   PIOC_OER=$810;   PIOC_IFDR=$824;   PIOC_SODR=$830;   PIOC_CODR=$834;   PIOC_IDR=$844;   PIOC_PSR=$808;   //set 1 on PA10 on PIOA controller registers   PIOA_A00=1 shl 10;   //set 1 on PB27 on PIOB controller registers   PIOB_B27=1 shl 27;   //set 1 on PC00 on PIOC controller registers   PIOC_C00=1 shl 0;   //set 1 on PC00 on PIOC controller registers   PIOC_C01=1 shl 1; var   fd:cint;   map_base, addr:^Longword;   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, PIO_BASE);   if ptrint(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 PA10 row A PIN PIOA_A00   //enable registr   addr:=Pointer(Longword(map_base)+PIOA_PER);   addr^:= PIOA_A00;   //disable input filtr   addr:=Pointer(Longword(map_base)+PIOA_IFDR);   addr^:=  PIOA_A00;   //disable interrupt   addr:=Pointer(Longword(map_base)+PIOA_IDR);   addr^:=  PIOA_A00;   //enable output   addr:=Pointer(Longword(map_base)+PIOA_OER);   addr^:=  PIOA_A00;   // setting registers to blink on PB29 row A PIN 31   //enable registr   addr:=Pointer(Longword(map_base)+PIOB_PER);   addr^:=PIOB_B27;   //disable input filtr   addr:=Pointer(Longword(map_base)+PIOB_IFDR);   addr^:=PIOB_B27;   //disable interrupt   addr:=Pointer(Longword(map_base)+PIOB_IDR);   addr^:=PIOB_B27;   //enable output   addr:=Pointer(Longword(map_base)+PIOB_OER);   addr^:=PIOB_B27;   // setting registers to blink on PC00 and PC01 row A PIN 31   //enable registr   addr:=Pointer(Longword(map_base)+PIOC_PER);   addr^:=PIOC_C00 OR PIOC_C01;   //disable input filtr   addr:=Pointer(Longword(map_base)+PIOC_IFDR);   addr^:=PIOC_C00 OR PIOC_C01;   //disable interrupt   addr:=Pointer(Longword(map_base)+PIOC_IDR);   addr^:=PIOC_C00 OR PIOC_C01;   //enable output   addr:=Pointer(Longword(map_base)+PIOC_OER);   addr^:=PIOC_C00 OR PIOC_C01;   //blinking   i:=0;   write('Blinking: ');   repeat     write('.');     //PA23 row A pin 27     addr:=Pointer(Longword(map_base)+PIOA_SODR);     addr^:=PIOA_A00;     //PB27 row A pin31     addr:=Pointer(Longword(map_base)+PIOB_SODR);     addr^:=PIOB_B27;     addr:=Pointer(Longword(map_base)+PIOC_SODR);     addr^:=PIOC_C00 OR PIOC_C01;     sleep(blink_delay);     addr:=Pointer(Longword(map_base)+PIOA_CODR);     addr^:=PIOA_A00;     //clear output data register = no blink     addr:=Pointer(Longword(map_base)+PIOB_CODR);     addr^:=PIOB_B27;     addr:=Pointer(Longword(map_base)+PIOC_CODR);     addr^:=PIOC_C00 OR PIOC_C01;     sleep(blink_delay);     inc(i);   until (i>blink_count) or (keypressed);   writeln(' done');   //close file descriptor   fpClose(fd);   //unmap memory   if fpMUnMap(map_base, PAGE_size)<>0 then Halt(fpgeterrno); end.