Forum > Linux PDAs

Atmel AT91SAM9G20 ARM CPU stucks after writing into some CPU registers (SOLVED)

(1/2) > >>

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

Go to full version