Cortex M3 ADR instruction

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Cortex M3 ADR instruction

3,306 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IanB on Fri Jul 17 06:55:33 MST 2015
     6aa:f000 fe32 bl1312 <wcmd>
     6ae:f2af 0004 subwr0, pc, #4
     6b2:2100      movsr1, #0
     6b4:f000 fe9c bl13f0 <wmsg>
     6b8:f20f 2025 addwr0, pc, #549; 0x225
     6bc:2180      movsr1, #128; 0x80
     6be:f000 fe97 bl13f0 <wmsg>
     6c2:2400      movsr4, #0
     6c4:f45f 65fd movs.wr5, #2024; 0x7e8


This bit of code is assembled from:

                BL wcmd
                ADR R0,msg0
                MOVS R1,#0                                  @ top line left display
                BL wmsg
                ADR R0,msg22
                MOVS R1,#0x80
                BL wmsg

but the label msg0 is at address 0x1BD0 and msg22 is at 0x1DF9

shouldn't it have flagged an "offset out of range" error?
0 Kudos
Reply
12 Replies

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by starblue on Sat Aug 08 02:30:06 MST 2015

Quote: IanB
It still rather puzzles me why ARM code is switched off on the Cortex devices

My guess is that not doing things uses less silicon.


Quote:

Surely, there must be some thumb-to-arm lookup table to make it all work, so why not allow it to be programmed in ARM code?

Probably not, it would be a lot of overhead compared to decoding instructions directly.


Quote:
I'd much rather write ARM code than the restricted Thumb code, (although much less restricted on the M3) - and I still prefer either to C.


Cortex-M is designed to be programmed in C, not Assembler. So if you do it anyway, you better accept it the way it is.


Quote:
I'd still like an explanation from the powers-that-be (Are you reading this, NXP?) as to why the ADR instruction assembles the wrong offset.


The powers-that-be in this case is the GNU binutils project, which contains the GNU assembler.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcxpresso-support on Mon Aug 03 01:13:00 MST 2015

Quote:
It still rather puzzles me why ARM code is switched off on the Cortex devices. Surely, there must be some thumb-to-arm lookup table to make it all work, so why not allow it to be programmed in ARM code?


All of the Cortex-M family of devices are native Thumb devices. They execute Thumb directly - there is no ARM mode.


Quote:

I'd still like an explanation from the powers-that-be (Are you reading this, NXP?) as to why the ADR instruction assembles the wrong offset.


If you think there is a problem, please post an example that exhibits the problem. We cannot do anything with snippets - we need  the whole project, and a detailed explanation of what you think is wrong.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cfb on Sat Aug 01 18:26:22 MST 2015

Quote: IanB
It still rather puzzles me why ARM code is switched off on the Cortex devices. Surely, there must be some thumb-to-arm lookup table to make it all work, so why not allow it to be programmed in ARM code?


I wouldn't have thought so. I was under the impression that the Cortex instruction set was only similar to ARM at the assembly language level. When rewriting the Oberon compiler code-generator for Cortex-M3 we found that the bit-patterns of the actual code generated for similar instructions are very different to ARM code. It is not obvious how you could map one onto the other. An interesting read which may give you more insight is:

ARM Cortex-M3 Processor Software Development for ARM7TDMI Processor Programmers:

http://www.arm.com/files/pdf/Cortex-M3_programming_for_ARM7_developers.pdf






0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IanB on Sat Aug 01 10:32:40 MST 2015
Thanks. I wasn't quite sure from the manual exactly how it worked.
Having had another look at the ARM Cortex M3 manual, I would think that
LDR R0,=tableaddress
LDR PC,(R0,R1,LSL#2)

would be the easiest way to do it.

It still rather puzzles me why ARM code is switched off on the Cortex devices. Surely, there must be some thumb-to-arm lookup table to make it all work, so why not allow it to be programmed in ARM code? I'd much rather write ARM code than the restricted Thumb code, (although much less restricted on the M3) - and I still prefer either to C. (By the way, Mike, last year you suggested I tried some C programming and I still haven't - my excuse is that all the projects I have done have been very timing-conscious.

I'd still like an explanation from the powers-that-be (Are you reading this, NXP?) as to why the ADR instruction assembles the wrong offset.

0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Mon Jul 20 08:21:29 MST 2015
Hi Ian.

There is a subtle difference between LDR Rx,=xxx abd ADR/ADRL.

LDR reads a value from a pc relative offset. The value is often a big constant, but can be an address of something.
ADR sets a register to an address relative to the current pc. This is useful for tables, messsage strings etc.

Note that TBB uses byte times two offsets so is limited to quite short jumps. TBH uses half word times two offsets.

An example from my own LPCXpresso (i.e. gcc assembler) ...

;---------------------------------------;
2:tbb[pc, r0]; else branch
T0:.byte(T10-T0)/2; listen
.byte(T20-T0)/2; syn received
.byte(T30-T0)/2; established
.byte(T40-T0)/2; last ack
.align 2,0; align even

;---------------------------------------;
; LISTEN;
;---------------------------------------;
T10:tstr1, tcpSyn;
beq9f; not syn, not interrested

       ; ...
b9f; return

;---------------------------------------;
; SYN RCVD;
;---------------------------------------;
T20:tstr1, tcpSyn; another syn, did they miss my ack?
bne11b; apparently -- resend the ack

etc.


Note that R0 (can be any register) must be range checked before using it as a jump index!
Note also, that if there happen to be an odd number of branches, the following opcodes will not be on 16-bit
boundaries -- hence the align!

Hope that helps you a little bit.

Cheers, Mike.

0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IanB on Mon Jul 20 01:50:55 MST 2015
I'm slightly puzzled that ADR complies the wrong value but LDR Rn,=immed gets it right even when the label is in a different section. Compared to ADRL, LDR Rn,= is about as efficient in both time and space!

Quote:
For jump tables, use TBB or TBH.


I'm slowly learning the additional M3 instructions - I've mastered ITT but TBH looked complicated, because the branches are relative, so the branch table has to have entries which calculate the relative branch from the absolute address and the present location. A list of absolute destinations seemed better for future readability!

Joseph Yiu's examples were for a different assembler, but presumably  it has to look something like this:
branchtable: .hword (destination1-branchtable)/2, (destination2-branchtable)/2 

etc.

0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Jul 18 14:45:48 MST 2015
I think that ADR/ADRL are only intended for use in the same assembler section.

Linker relocation after the assembly phase will probably screw you over if cross-section offset!

Mike.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Jul 18 14:39:47 MST 2015
Ian, did you try "ADRL"?

For jump tables, use TBB or TBH.

Mike.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IanB on Sat Jul 18 05:34:02 MST 2015
It appears to assemble to the ADD Rn, Rm, #immed12 instruction, where m is 15 (the program counter), so it can get ±4095 from the current (or next) PC location; rather than use the ADD Rn, Rm, <flexible second operand>  which has the odd rules about constants.

What bothers me is that it quite happily assembled the WRONG #immed12 value without even telling me, and left me to wonder why my program loaded the data from completely the wrong address.

However, the Cortex M0 "ADR" is a complete chocolate teapot - because #immed12 has to have its lowest two bits as zero, it is impossible to use ADR for branch tables because it is impossible to set bit 0 for a proper Thumb branch. I suppose it would be OK for lookup tables, but as they tend to go after the main program in a .rodata section, it tends to be out of range for both M0 and M3.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Fri Jul 17 09:15:39 MST 2015
Hi Ian.

ADR is an assembler construct to get an address value 'near' the current code.
As you see, it does this by adding or subtracting a constant from the current PC (i.e current instruction + 8 -- or is it + 4?).

As you may know the size of constants than may be embedded in an instruction is limited -- and there are crazy rules about
shifting a 8-bit literal up to make *some* bigger values.

I'd guess that the location of your 'msg0' etc. are too far and you are getting the same effect as truncating words to bytes etc.
[I'd have thought that the assembler would throw an error, but ...]

HOWEVER, there is also a long form of 'ADR' called 'ADRL' naturally. This uses two basic instructions to do the add/subtract
from the current (ish) PC. If only one is actually neccessary, the second will be a NOP.

I am not clear about the acceptable ranges for either the standard ADR or the longer (more expensive) ADRL -- maybe it is
buried in the GNU assembler manual, or -- more likely -- in the ARM ARCHITECTURE V 7M manual.

Hope that yields some insight and or actual help -- Cheers, Mike.
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by IanB on Fri Jul 17 07:35:23 MST 2015
R0 contained 0x6AC which is PC-4 like the disassembly listing says.
It should have contained 0x1BD0, though it can't because that's more than ±4095 away.

00001bd0 <msg0>:
    1bd0:20202020 .word0x20202020
...
0 Kudos
Reply

3,060 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by TheFallGuy on Fri Jul 17 07:17:55 MST 2015
Dunno. Can't tell from a short snippet. Would need to see the entire file.

Presumably, though, you are not seeing the behavior you expected, so what does R0 contain when you step through the code?
0 Kudos
Reply