sam7stuff

Thursday, July 29, 2010

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

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.

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.

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.

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.

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:

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.

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 thatat 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 builtit.

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.

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 somethingso 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.

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 isread 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 forPIO_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_SODR0x0034 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 addressfor 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 upand 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

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.

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.

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.

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.

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

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.