ASM IJMP in C program over 64K

Go To Last Post
28 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi.

This is my first time to integrate C and ASM code in WINAVR and I have a problem. I use an Atxmega256.

my C code is really large (I have some large data array in PROGMEM). And my .S assembly code contain a IJMP (Indirect jump.) the IJMP use Z as reference pointer to jump to a specific location in flash memory. But the IJMP instruction can jump only in memory from the first 64k. (It's normal, it use the Z pointer ZH and ZL to make the jump).

My problem is that since my C code is larger than 64K, the C compiler compile the C code first and the ASM code second. This cause the Asm code to be executed past the first 64K, so the IJMP fail. I searched on internet and this forum how to compile the ASM code first to avoid this issue, but never found an answer. Maybe someone here have a solution to add a parameter to the compiler to compile the ASM code first? or a way to do it in the code?

Thanks!

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

why don't you use EIJMP it's made for that ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Follow my "Options" link below and read "EIND and Devices with more than 128 Ki Bytes of Flash".

After reading it, are there still any questions?

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow. fast answer.

Yes, I could use EIND (and EIJMP) but the code I write could be used be other peoples to do other things that will not overpass the 64K limit. And the code will not work.

But if there is no option for the compiler to compile the ASM file first, maybe I can accomodate with EIJMP, but I will need to know if it's possible to do something like in avr studio to start some code at a specific FLASH address... like the .ORG in ASM avr studio to start a part of code at a specific FLASH position. Any idea?

Thanks for the fast answer!

Mast3rbug

Last Edited: Sun. Nov 18, 2012 - 02:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From what you write you don't correctly determine the function address. If your code exceeds 128k they you will have to use linker stubs as described in the avr-gcc manual.

Setting EIND after main is entered won't work.

IJMP can address 128k of flash, not only 64k. This is because function pointers are word addresses, not byte addresses.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IJMP can use only 64K because it use byte and not word like mentioned in avr studio help.

Are you sure there is no option to compile ASM file before the C file?

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IJMP chews word addresses, not byte addresses.

It does not matter what you compile first. You get relocatable objects that are located by the linker / locator. It's no concern of the compiler to locate objects.

If you need to implement a special memory layout, you may want to think about providing your own linker script that arranges for the special layout.

The default linker script cannot accomodate for all imagineable layouts, in particular if code or data are big and the underlying oddities of the AVR architecture must be handled in a specific way.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But a custom makefile can be fine for me, I just don't know how to change the compile order. I tried to change something and make a custom script, but it compile in the same order. Whatever I do, it still compile the C code first...

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When you build for "large" devices the compiler defines the macro __AVR_HAVE_RAMPZ__ so I would have thought the code could just do:

#ifdef __AVR_HAVE_RAMPZ__
 ;setup EIND
 EIJMP
#else
 IJMP
#endif

which would build and work on both small and large devices.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson:

Ok, but what will tell to the compiler that now the code have reach the limit and must use the EIJMP?

and what if the current code is overlaping between FFFE and the next instruction is at 10000 and up? confusing to me...

Because I will not only need EIJMP, but also using the RAMPZ for ELPM.

For that reason, I still think that finding a way to compile the .S before the .C is the best solution. I'm pretty sure it's possible because it's possible to do that with a pre compiled .bin file as an include.

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Ok, but what will tell to the compiler that now the code have reach the limit and must use the EIJMP?

Eh? On a processor that has EIJMP what's the downside of always using it whether it's beyond the boundary or not? In fact all the changes is whether EIND holds 0 or not.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I already said (and the avr-gcc docs explain), changing EIND at runtime is not compatible with code generated by avr-gcc.

avr-gcc works in a way so that EIND need not and must not be changed.

Besides that, the right condition to probe for EIJMP / EICALL is __AVR_HAVE_EIJMP_EICALL__

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

is __AVR_HAVE_EIJMP_EICALL__

Rather sadly not documented in the user manual:

http://www.nongnu.org/avr-libc/u...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course it is documented in in the secondary lietrature: Follow im "Options" link and read "AVR Built-in Macros".

Only __AVR_ARCH__ is missing; it is documented in 4.8 and will be be documented in 4.7.3.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for hijacking (slightly) this thread:

Johann,

in "Options", you write:
Linker relaxation must be turned on so that the linker will generate the stubs correctly an all situaltion.

Why? I thought the linker bug necessitating relax has been fixed, or is there any known regression, or other similar problem?

Thanks,

Jan

(PS. there is a typo in the above sentence, "situaltion")

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I verified all the option that I have and finally, all the sugestions here will not work. So, I'm back with my initial question;

I want an option in the C compiler to compile the ASM code before the C code. It's impossible that is not doable, It's a flexible compiler. It must be possible.

Moreover, I will have an ASM routine that will have to absolutely get Data Stored from the upper 128K of the Xmega at $1F400 in pure ASM in avr studio, All that I had to do is a new .ORG $1F400 and placing the Data code (.db generated) here.

Is there a kind of .ORG in C to tell the compiler to start to compile there?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, Despite all you tried to tell me to change my code instead of answering to the initial question, I known that what I wanted was possible. And after searching A LOT of time on google, I find the solution, that remain quite simple.

So for people interested, I will explain it.

First, I have discovered the solution here:

http://www.scienceprog.com/contr...

(no name in the page to give credit... But anyway, thanks a lot)

If you read the article, it needed to add some parameters in the makefile, but I discovered that it's not necessary to add the:
CFLAGS += -Wl,-section-start=.MySection5=0x1F00
in the makefile, WINAVR will assume itself that this will be placed after the compiled code. Thats it.

Example: you want to place a large array at the END of your code to avoid the EIJMP and all that kind of stuff to disturb your code. So, at the moment of declaring your PROGMEM array:

Instead of THIS:

const unsigned char PROGMEM big_data_array[] = {

some big data....

}

Use THIS:

const unsigned char big_data_array[] __attribute__ ((section (".MySectionName")))= {

some big data....

}

This will compile the code automatically at the end of the actual code instead of the start, because all custom Data section is linked at the end By default.

MySectionName is a name of your choice, and WINAVR will use it as reference for pointers to this Array.

I hope this will help some peoples in the future.

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I must admit that I don't know how binutils handles orphan sections. Your input section is orphan because it is never treated by the linker script.

If you know binutils enough to know that orphans are always dropped where you intend it, you can use that, of course.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While mast3rbug's description is a bit messy, I believe he is referring to sections explicitly located through the --section-start command-line switch.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
My problem is that since my C code is larger than 64K, the C compiler compile the C code first and the ASM code second.

I think the problem is that the linker is putting PROGMEM data (section .progmem.data ?) before *any* code in flash. Otherwise it would be a simple matter of reordering the link command, or using one of the .init sections, or something like that.

Is there an easy way to move some code (via section name, perhaps?) before the PROGMEM data? So that the final memory layout looks like:
"early code"
"progmem"
"normal code"
"etc (initializers?)"
("easy" probably means "without having to learn about linker scripts.)

(Hmm. Researching...
It looks like there is a section called ".lowtext" in the default linker scripts aimed at doing something like this, but it comes AFTER PROGMEM data. If your assembly code can live with being before the other code, but after the PROGMEM data, you can probably just put it into the .lowtext segment instead of .text
.vectors comes before PROGMEM, but it's probably a bad idea to rely on link order to put "other code" after the actual vectors...)

(All things considered, producing a modified linker script doesn't look like it would be too difficult. Though I wouldn't want to write one from scratch!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
t looks like there is a section called ".lowtext" in the default linker scripts aimed at doing something like this, but it comes AFTER PROGMEM data. If your assembly code can live with being before the other code, but after the PROGMEM data, you can probably just put it into the .lowtext segment instead of .text
.vectors comes before PROGMEM, but it's probably a bad idea to rely on link order to put "other code" after the actual vectors...

Not sure... Because this was the actual problem. The ASM code was AFTER the PROGMEM data... so, whats the difference? the C code and PROGMEM data is always compiled first.

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

the C code and PROGMEM data is always compiled first.

The order of compilation has no bearing on placement in memory. The linker script is what dictates the layout when it links the object files that may have been compiled 3 seconds, 3 days or 3 weeks before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
The linker script is what dictates the layout when it links the object files [...]
Within a particular section, it seems that the order of modules is the order in which they were presented to the linker.
avr-gcc a.o b.o c.o
vs.
avr-gcc a.o c.o b.o

I suspect, however, that this is an implementation detail that is not guaranteed.

That being said, one could exploit this artifact by carefully constructing the list of object names in the makefile.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I suspect, however, that this is an implementation detail that is not guaranteed.

Maybe added a SORT(*) to the linker script and do it by name?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok here an update to this post.

Since the method I used to "TWEAK" the PROGRAM MEMORY address of specific code works only in some point and seems to be buggy, I have continued my research and find the right way to do it;

If you remember, what I wanted was be able to compile PROGMEM ARRAY where I want in the AVR memory. The solution I found was to add some directives to CFLAGS like this:

CFLAGS += -Wl,-section-start=.MySection5=0x1F00

This is kind of broken in this version of WINAVR with multiple source file project. Instead of that, the parameters need to be added to the linker. And avr studio can manage that in the project options directly. At the end, it will add something like this to the linker directive in the makefile:

LDFLAGS += -Wl,-section-start=.mytext=0x20000

Ok here what you have to do:

first in your code, instead of the PROGMEM, declare a section exactely like the previous method:

const uint8_t sinewave[] __attribute__ ((section (".mytext")))= {7,6,3,4,5,6,7,8,9,0x0a};

This array is called "sinewave" and have an attribute of ".mytext" (it can be whatever you like)

Now to tell the linker where to place it in memory, go to Project, Configuration options, and in memory settings, add the following:

Memory: Flash
Name: .mytext (the name you gave previously)
Address(Hex): 0x10000 (The address you want)

Now, this line will be automatically added to your makefile:

LDFLAGS += -Wl,-section-start=.mytext=0x20000
(I don't know why it multiply by 2, but I can assure you that the code is working)

Thats it.

I think that you can add as many as you want without problem. The linker will do the job for all. Of course, you have to manage the RAMPZ by yourself. I don't know if this can work for code or only data, since this is a linker command... It have to be tested... But since what I wanted is having all my code in the lower 128K, Music Data in First upper 64K and graphics Data in the second upper 64K for a total of 256k on an Atxmega256, this is the best think to do.

I hope this will help some in the same situation than me, because I searched for this for 1 month.

By the way, for some that can be curious to what kind of project is it and why I need such specific FLASH addressing, here is a youtube link:

http://www.youtube.com/watch?v=hL3_t7yUrfc

Thanks
Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The solution I found was to add some directives to CFLAGS like this

As you have found --section-start is a link time command. I have no idea why it would have ever be suggested to add it to CFLAGS unless it were the case that the linker were to be invoked with both $(CFLAGS) and $(LDFLAGS). LDFLAGS is clearly the right place to add linker directives.
Quote:

I don't know why it multiply by 2

Because you are thinking in term of Atmel word addressing (they use words for everything relating to flash as the minimum granularity is 16 bits (shortest opcode) there). Meanwhile GCC is a generic toolchain that applies to multiple architectures so it uses byte addressing for ever type of memory addressing. *2 (for AVR8) converts from words to bytes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know that. But since I already declare it in Byte, normally it must divide by 2 for Words, not multiply.

I also think this option works for the C compiler and the Linker. Because it work also as compiler option, but have some broken features.

Mast3rbug

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you confusing "compiler" and "compiler driver" perhaps? avr-gcc is a compiler driver and can be made to direct command to the linker with -Wl, it is true.