Thursday, July 29, 2010

speeding up the clock

The prior example showed that when booting from flash you are running using the slow clock. This clock reading the docs is something like 32768 hz. pretty slow.

Looking at section 25.7 as a reference for how to switch to the off chip/main oscillator and to use the pll to run faster than that oscillator.

48mhz is a useful frequency in case you decide to do usb stuff in the future, so lets go for that speed. The oscillator on the SAM7-H256 is 18.432, a prescribed Atmel sam7 frequency for these parts. Using this calculator from atmel

http://www.atmel.com/dyn/resources/prod_documents/AT91SAM_pll.htm

Knowing the oscillator is 18.432 mhz, the pll minimum is 1mhz and the desired is 48mhz I got a divide by 5 and multiply by 13. The code below is the same as my prior examples except it adds a function to switch to the main clock. The details of the registers is an exercise for the reader, note though that the multiply value is the number in the register plus 1 so if we want to multiply by 13 we should put a 12 in the register.



void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void ASMDELAY ( unsigned int );

#define WDT_BASE 0xFFFFFD40
#define WDT_CR (WDT_BASE+0x04)

#define PMC_BASE 0xFFFFFC00
#define CKGR_MOR (PMC_BASE+0x0020)
#define CKGR_PLLR (PMC_BASE+0x002C)
#define PMC_MCKR (PMC_BASE+0x0030)
#define PMC_SR (PMC_BASE+0x0068)

#define PIOA_BASE 0xFFFFF400

#define PIO_PER 0x0000
#define PIO_OER 0x0010
#define PIO_SODR 0x0030
#define PIO_CODR 0x0034
#define PIO_PUDR 0x0060

#define PIOA_PER (PIOA_BASE+PIO_PER)
#define PIOA_OER (PIOA_BASE+PIO_OER)
#define PIOA_SODR (PIOA_BASE+PIO_SODR)
#define PIOA_CODR (PIOA_BASE+PIO_CODR)
#define PIOA_PUDR (PIOA_BASE+PIO_PUDR)


#define LED_BIT (1<<8)

void switch_to_mainclock ( void )
{
PUT32(CKGR_MOR,0x701);
while(1) if(GET32(PMC_SR)&1) break; //wait for MOSCS
//div by 5, mul by 13 should give 48mhz
PUT32(CKGR_PLLR,0x000C3F05);
//PUT32(CKGR_PLLR,0x00013F01); //mul by 2? 30864 mhz?
//PUT32(CKGR_PLLR,0x00023F01); //mul by 3?

while(1) if(GET32(PMC_SR)&(1<<2)) break; //waitfor LOCK
while(1) if(GET32(PMC_SR)&(1<<3)) break; //waitfor MCKRDY

PUT32(PMC_MCKR,0x01); //MAIN clock no prescale

while(1) if(GET32(PMC_SR)&(1<<3)) break; //waitfor MCKRDY
}

int notmain ( void )
{
PUT32(WDT_CR,1<<15); //disable wdt
switch_to_mainclock();
PUT32(PIOA_PER,LED_BIT); //enable/connect PIO control to led output
PUT32(PIOA_PUDR,LED_BIT); //disable pull up resistor
PUT32(PIOA_OER,LED_BIT); //enable/set as an output pin
PUT32(PIOA_CODR,LED_BIT); //turn on led. other side of led is 3.3v so to turn it on drive/sink 0 volts.

while(1)
{
PUT32(PIOA_CODR,LED_BIT);
ASMDELAY(0x300000);
PUT32(PIOA_SODR,LED_BIT);
ASMDELAY(0x300000);
}
return(0);
}



we can see that we have to make the counter value bigger between led on and off in order to make it blink at a human readable/tolerable rate. so we have indeed booted from flash and increased the arm's clock speed.

Ideally you do not want to loop instructions like this to time things, you want to use a timer, which is a future example.

use the makefile and vectors.s and memmap from prior posts in order to build this test program. the prior (sam-ba) post describes how to erase and load the program into flash.

Using sam-ba on linux

Looks like ATMEL has a linux version of sam-ba.

http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3883

And it turns out this board I have is actually an olimex sam7-h256 instead of a h64, so that is good.

to use sam-ba install the tst jumper, plug the board into a usb cable to apply power, wait for a second. unplug from the usb cable, remove the tst jumper, and plug back in. If everything worked right dmesg should show you that a device was connected and there is a serial port on ttyUSBx.

Download sam-ba from the above link.

unzip somewhere and run sam-ba. the gui will ask for a serial port, enter the ttyUSBx number from dmesg. I my case I select the sam7s-256 evaluation kit board type even though it is not an atmel eval kit, it makes sam-ba look at the chip and recognize what it is.

Taking the software and instructions from the prior post, even though I said the rom/ram based solution would be the last thing I would do that is the next thing I am going to show.

Change memmap to:



/* memmap */
MEMORY
{
rom(RX) : ORIGIN = 0x00000000, LENGTH = 0x10000
ram(WAIL) : ORIGIN = 0x00200000, LENGTH = 0x1000
}

SECTIONS
{
.text : { *(.text*) } > rom
}



The RX means read and execute, the WAIL is everything else which we want in ram. We are not using variables so we dont have to get into initializing .data or .bss discussions.

I had mentioned in the prior post about the chip booting up slow then writing clock registers to make it go faster. Whatever bootloader was in this chip when I got it did the fast clock thing, so the prior example needed a long count in order to make the blink not too fast and not too slow. But this example runs from flash so we get the chip post-reset without the clock adjusted so it is in the slow mode. So the counter in the prior post was really too long for running from flash as far as a demonstration goes. So here is the new blinker1.c program:




void PUT32 ( unsigned int, unsigned int );
void ASMDELAY ( unsigned int );

#define PIOA_BASE 0xFFFFF400

#define PIO_PER 0x0000
#define PIO_OER 0x0010
#define PIO_SODR 0x0030
#define PIO_CODR 0x0034
#define PIO_PUDR 0x0060

#define PIOA_PER (PIOA_BASE+PIO_PER)
#define PIOA_OER (PIOA_BASE+PIO_OER)
#define PIOA_SODR (PIOA_BASE+PIO_SODR)
#define PIOA_CODR (PIOA_BASE+PIO_CODR)
#define PIOA_PUDR (PIOA_BASE+PIO_PUDR)

#define LED_BIT (1<<8)

int notmain ( void )
{
//volatile unsigned int ra;
PUT32(PIOA_PER,LED_BIT); //enable/connect PIO control to led output
PUT32(PIOA_PUDR,LED_BIT); //disable pull up resistor
PUT32(PIOA_OER,LED_BIT); //enable/set as an output pin
PUT32(PIOA_CODR,LED_BIT); //turn on led. other side of led is 3.3v so to turn it on drive/sink 0 volts.

while(1)
{
PUT32(PIOA_CODR,LED_BIT);
//for(ra=0;ra<0x10000;ra++) continue;
ASMDELAY(0x4000); //this way the compiler wont optimize out the for loop
PUT32(PIOA_SODR,LED_BIT);
ASMDELAY(0x4000);
}
return(0);
}



So count to 0x4000 is actually a pretty slow blink 3 seconds plus or minus between on and off state changes.

So using the Makefile and vectors.s from the prior post build blinker1.bin.

In samba. IN the top area that says memory display, click in the box with 0x200000 and change that to 0x100000 and press the Refresh button. This is the flash data.

In the middle section press the flash tab. next to the execute button select Enable Flash access and press Execute. Then select Erase All Flash and press Execute, it may warn you that
at least one section may be locked do you want to unlock. press the button saying that you do want to unlock those sections.

In the Memory Display window press refres, now the flash memory should be all 0xFFs, which is erased.

use the open folder icon next to the Send File Name box to find blinker1.bin wherever you built
it.

Then press the Send File button. Once complete it will ask you if you want to lock the flash areas affected. You can say yes or no, shouldnt matter.

In the memory display window if you press the Refresh button the data in flash should now match the data in the blinker1.list file.

Close/exit sam-ba

Unplug the board from the usb cable and then plug it back in. the green led should blink every few seconds which means our program is now in flash and running.

Wednesday, July 28, 2010

sam7-h64 led blinker

Simple blinking led example for an olimex SAM7-H64.

You will need some compiler tools. You can look at my other blogs and build your own or
just get codesourcery lite.

http://www.codesourcery.com/sgpp/lite/arm/download.html

the GNU/Linux one is fine, dont use the C library or any system calls so we just need the cross compiler itself (and binutils).

Get schematic from sparkfun or olimex

From schematic see that the part is an AT91SAM7S64, and the led is on PA8

go to atmel's website and get the datasheet/document for the AT91SAM7S series (the big book that is not the arm TRM (although if you dont have an ARM TRM or the ARM ARM you will eventually want that too)). From atmels site this is currently doc6175.pdf

chapter 8 memories, looking at the memory map sram starts at 0x00200000 and ends at 0x00300000

chapter 27 parallel I/O (PIO).

figure 27-3 I/O control line logic. work backward from the pad. We want to just write to registers to blink the led on and off instead of using a peripheral like a uart or something
so we will need to use the PIO_PSR(PER/PDR) registers to switch the mux to register control.

So search in the doc for PIO_PSR. 27.4.2 says that a 0 indicates the pin is tied to the peripheral and 1 is tied to the PIO controller, so we will want a one for that I/O pin.

chapter 27.6 table 27-2 register mapping.

0x0000 PIO_PER write only
0x0004 PIO_PDR write only
0x0008 PIO_PSR read only

So what we really want is the PIO_PER or PIO_PDR to enable or disable, PSR is the status register indicating the current state.

Searching for those registers we find 27.6.1 PIO_PER, enables PIO control of the I/O pin disabling peripheral control. That is what we want.

This chip is the kind that has the write a one to a bit in one register to enable something and write the same 1 bit to a different register to disable something. The other kind of chip is
read a register, modify the one bit of interest zero or one, then write the register back. Pros and cons either way, the chips with both methods are ideal because you can make it a win win.

So for this board with PA8 being the I/O pin in question we are going to want to write to PIO_PER with bit 8 set to give PIO control over that pin.

back to 27.4 figure 27-3. To enable output on that pin we need to look for
PIO_OER/OSR/ODR.

0x0010 PIO_OER output enable

same deal we are going to want PA8 to be an output so we need to write a 1 to bit 8 of PIO_OER.

back to figure 27-3 in 27.4 got the feel for this doc now...
PIO_SODR/PIO_CODR is no doubt how you set and clear the bit

0x0030 PIO_SODR
0x0034 PIO_CODR

So SODR sets the output (to a 1 or VCC) and CODR clears the output or puts zero volts
out on the pin.

That may be all we need for what registers to use, but what is the base address
for PIOA and does it need to be enabled?

Figure 8-1 memory map shows PIOA starting at 0xFFFFF400 I would prefer to see this in
a table as well as a picture but dont see it anywhere else.

While looking for the PIOA address saw the PIO_PUDR register, we dont need a pull up
and pull ups are enabled post reset so we can write a 1 here to disable the pull up. Shouldnt hurt if we leave it on, just draw some more current.

So on to the program.

This example is going to run from ram, if/when we figure out how to run from rom the first rom program may just copy this program to ram and run from ram. Then eventually maybe execute instructions from rom and use data/variables in ram.

Even though we are not initially booting from the rom, I still use a rom style interrupt vector table approach to the beginning of the binary/program. The first exception vector in an ARM(7) is a reset, so it will just branch to our startup anyway. This model means that I only have to do it one way and not think about it.

So the startup code, the bytes at the beginning of our binary in memory will be from vectors.s


.globl _start
_start:
b reset
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang
b hang

reset:
mov sp,#0x00300000
bl notmain
hang: b hang

.globl PUT32
PUT32:
str r1,[r0]
mov pc,lr

.globl ASMDELAY
ASMDELAY:
subs r0,r0,#1
bne ASMDELAY
mov pc,lr



From the sam7s document we saw that there is an sram, that doesnt move around, that goes from 0x00200000 to 0x00300000. So just set the stack pointer to the top of that memory so it can work its way down. Calling C functions so we might need a stack.

I call notmain() just to mentally know this is not a operating system based program that normally calls main(). Some compilers add extra junk when you have a function named main() that they wont do if you call it something else.

I have had too many compilers, gcc included, that no matter how hard you try, optimize out or modify a memory/register load or store, for that reason and others, I burn a function call for accessing memory/registers. In this case PUT32, for writing 32 bit registers is implemented in assembler.

To save you possible frustration, an assembler based count to N routine ASMDELAY() was also added. Normally a C counter loop can be used but as compilers get better they try harder to optimize that code out to nothing. Since this simple example is going to turn the led on, count to N then turn it off count to N, turn it on count to N. We need the count to N to actually burn some time and not get optimized out. So ASMDELAY is here for that reason.



void PUT32 ( unsigned int, unsigned int );
void ASMDELAY ( unsigned int );

#define PIOA_BASE 0xFFFFF400

#define PIO_PER 0x0000
#define PIO_OER 0x0010
#define PIO_SODR 0x0030
#define PIO_CODR 0x0034
#define PIO_PUDR 0x0060

#define PIOA_PER (PIOA_BASE+PIO_PER)
#define PIOA_OER (PIOA_BASE+PIO_OER)
#define PIOA_SODR (PIOA_BASE+PIO_SODR)
#define PIOA_CODR (PIOA_BASE+PIO_CODR)
#define PIOA_PUDR (PIOA_BASE+PIO_PUDR)

#define LED_BIT (1<<8)

int notmain ( void )
{
//volatile unsigned int ra;
PUT32(PIOA_PER,LED_BIT); //enable/connect PIO control to led output
PUT32(PIOA_PUDR,LED_BIT); //disable pull up resistor
PUT32(PIOA_OER,LED_BIT); //enable/set as an output pin
PUT32(PIOA_CODR,LED_BIT); //turn on led. other side of led is 3.3v so to turn it on drive/sink 0 volts.

while(1)
{
PUT32(PIOA_CODR,LED_BIT);
//for(ra=0;ra<0x10000;ra++) continue;
ASMDELAY(0x400000); //this way the compiler wont optimize out the for loop
PUT32(PIOA_SODR,LED_BIT);
ASMDELAY(0x400000);
}
return(0);
}


There is nothing to this now, we looked up the registers for enabling PIO control over the pin, making it an output, and turning it on and off. So this program simply does that and then goes into an infinite loop that turns on the led, counts to N, turns off the led, counts to N.

The next step is building it. Here is the makefile.




ARMGNU = arm-none-linux-gnueabi

AOPS = --warn --fatal-warnings
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding

all : blinker1.bin

blinker1.bin : blinker1.elf
$(ARMGNU)-objcopy blinker1.elf -O binary blinker1.bin

blinker1.elf : blinker1.o vectors.o memmap
$(ARMGNU)-ld memmap vectors.o blinker1.o -o blinker1.elf
$(ARMGNU)-objdump -D blinker1.elf > blinker1.list

blinker1.o : blinker1.c
$(ARMGNU)-gcc $(COPS) -c blinker1.c -o blinker1.o

vectors.o : vectors.s
$(ARMGNU)-as $(AOPS) vectors.s -o vectors.o

clean :
rm -f *.o
rm -f *.elf
rm -f *.list



You can see that I do not use standard libs, so we dont care that this is a linux based gcc compiler for arm. Dont want to use the compilers startup code, we have provided our own with vectors.s. if you roll your own gcc or use emdebian or winarm or whatever, just change the ARMGNU to match your prefix. The linker script memmap may not work for gcc 3.x or I guess that would be the binutils of the era not necessarily gcc.

Here is my linker script, memmap



/* memmap */
MEMORY
{
rom(RXWAIL) : ORIGIN = 0x00200000, LENGTH = 0x10000
}

SECTIONS
{
.text : { *(.text*) } > rom
}



vectors.o being the first object in the linker script puts it first in the file which is where we want it.

So that leaves us with either a blinker1.elf file or a blinker1.bin file depending on the tools. I have a funny feeling that whatever comes with this board uses some of that sram so you may want to change memmap to some other address like 0x00202000 or something to move it out of the way. using a jtag wiggler, and kicking the thing twice I can get it to run from 0x200000.

I do not have a sam-ba solution as I dont run windows. the .bin or .elf above should work for you, you might have to adjust the address. Eventually I may make a linux loader, at one point I had a windows loader for this board using libusb, so a linux one should be possible as well.

I am currently using an Amontec Jtag-tiny wiggler, the olimex usb one or parallel port one or any arm based one supported by openocd will work using the following instructions. You will need to get and install openocd which may be a simple apt-get install. Here is the configuration file that I used with the jtag-tiny



telnet_port 4444
gdb_port 0
tcl_port 0

interface ft2232
ft2232_device_desc "Amontec JTAGkey"
ft2232_layout jtagkey
ft2232_vid_pid 0x0403 0xcff8
#ft2232_serial T1RWU3KU

reset_config trst_and_srst srst_pulls_trst

jtag newtap sam7s cpu -irlen 4 -ircapture 0x1 -irmask 0x0f -expected-id 0x3f0f0f0f

set _TARGETNAME sam7s.cpu
target create $_TARGETNAME arm7tdmi -endian little -chain-position $_TARGETNAME -variant arm7tdmi



It is easy to build this file from the config files provided with openocd. We just need enough for openocd to gain access to the arm debug tap controller, from there we use openocd commands.


If you are using something other than the jtag-tiny then find the config file for your wiggler and replace the first section of this config file. Note that I have commented out the serial number, I can/have more than one jtag-tiny in a system at a time and using serial numbers like this you can have more than one openocd instance running, also change the telnet port number if you want to do something like that.

In addition to openocd you may need some ftdi drivers for your wiggler. here again for ubuntu this is an apt-get install thing. libftdi-dev or something like that. maybe has a 2232 in the name.

Power the card and in one terminal window run

> openocd -f sam7s.cfg

Hopefully it says that it has found the sam7s cpu/tap device

In another terminal window run

> telnet localhost 4444

You should get a Open On-Chip Debugger prompt

The first thing is type halt

> halt

Now be really careful, if you are not in the telnet session to openocd but at some other prompt you will halt your computer and that is really annoying.

The second command is to load the .elf file, you can do the .bin and provide an address, but since we have the .elf lets use it.

> load_image /path/to/blinker1.elf

If you ran openocd from the same directory as the elf then you dont need the path. The terminal window should say so many bytes were loaded to 0x200000.

Now start the arm running at that address

> resume 0x200000

I had to do this twice the first time after power on to get this to work, I want to remember the startup code in the chip uses some of that sram.

> halt
> resume 0x200000

And you should have a blinking green led on your board!


Now chips like this one that are advertised at some dozens of mhz do not necessarily power up at that speed. They may start at 8mhz or 12 or whatever the crystal is and there is a procedure for programming some clock registers to speed the thing up. So the rate that the led blinks depends on what code you had running from rom before running this program. Likewise if you add code to adjust that clock the blink rate can change and the led may look like it is just on instead of blinking (blinking faster than your eye can tell). That is because this is a simple program that uses a counter as a delay, it counts X number of arm instructions, and if we adjust the execution speed of the arm that X instructions can run fast or slow. Eventually you would want to take control over what speed you run the arm, what speed you run timer reference clocks and use a timer to count to some X number of reference clocks and the arm speed wont matter, also the arm can go do other stuff and come back to check the timer instead of having to burn cycles on blinking.