Неподравненият достъп причинява грешка на ARM Cortex-M4

Имам обект, който има адрес, който не е подравнен в 4 байта. Това причинява грешка HardFault в процесора, когато има STR инструкция, запазваща 2 регистъра.

Това е генерираният код:

   00000000 <_ZN8BaseAreaC1EPcmm>:
   0:   b510            push    {r4, lr}
   2:   4604            mov     r4, r0
   4:   6042            str     r2, [r0, #4]
   6:   e9c4 3102       strd    r3, r1, [r4, #8]
   a:   2001            movs    r0, #1
   c:   7420            strb    r0, [r4, #16]
   e:   b921            cbnz    r1, 1a <_ZN8BaseAreaC1EPcmm+0x1a>

Това са регистрите, когато на ред "4: 6042..."

R0   08738B82  R8          0  
R1   08738BAE  R9          0  
R2          0  R10  082723E0  
R3       2FCC  R11         0  
R4   08738B82  R12         0  
R5   20007630  R13  2000CB38  

Както се вижда целевият регистър за STR-инструкции не е подравнен на 4-байта. Инструкцията STR r2, [r0, #4] се изпълнява добре. Но това HardFaults на следващия STRD r3, r1, [r4, #8]. Ако ръчно променя регистър R4 на 08738B80, това не е твърда грешка.

Това е C++ кодът, който генерира горния asm:

BaseArea::BaseArea(char * const pAddress, unsigned long startOffset, unsigned long endOffset) : 
m_pAddress(pAddress), m_start(startOffset), m_end(endOffset), m_eAreaType(BASE_AREA) {

И m_start е първата променлива в класа и има същия адрес като this (08738B82), m_end следва след 0x08738B86.

Как да подравня обекта на 4 байта? Някой има ли друго решение за това?


person rdrmntn    schedule 16.08.2013    source източник
comment
Всъщност програмирате ли на асемблер или този код е генериран от напр. C компилатор?   -  person Some programmer dude    schedule 16.08.2013
comment
@JoachimPileborg: Като се има предвид, че има ясно повредени C++ имена, подозирам, че това е C++ код.   -  person Mats Petersson    schedule 16.08.2013
comment
Това е асемблер код, генериран от C++.   -  person rdrmntn    schedule 16.08.2013
comment
@jortan: Какъв е class или struct, който използвате тук (стойността в r0 вероятно е this на обекта, с който имаме работа. С други думи, какъв изходен код е използван за генериране на това?   -  person Mats Petersson    schedule 16.08.2013
comment
Може също да помогне, ако ни кажете какъв компилатор използвате (напр. gcc, armcc и т.н.)   -  person Mats Petersson    schedule 16.08.2013
comment
@MatsPetersson актуализира публикацията с C++ код.   -  person rdrmntn    schedule 16.08.2013
comment
Можете ли да публикувате структурата на BaseArea, както и къде се извиква конструкторът?   -  person Mats Petersson    schedule 16.08.2013
comment
Как създавате обекта? Може би е част от пакетирана структура или нещо подобно? В противен случай мисля, че това се регистрира като грешка във веригата инструменти, тъй като езикът гарантира, че обектите ще бъдат разпределени на адреси, които отговарят на техните изисквания за подравняване (с изключение на прекалено подравнени обекти, но това не е случаят тук). Във всеки случай можете да принудите конкретно подравняване, като използвате alignas (C++11) или специфичен за компилатора еквивалент, когато създавате обекта.   -  person Samuel Peter    schedule 13.02.2018


Отговори (2)


На ARM-базирани системи често не можете да адресирате 32-битова дума, която не е подравнена към 4-байтова граница (както грешката ви казва). На x86 можете да получите достъп до неподравнени данни, но има огромен удар върху производителността. Когато ARM част поддържа неподравнени достъпи (напр. нормално зареждане на една дума), има наказание за производителност и трябва да има конфигурируемо прихващане на изключения.

Пример за грешка на границата на ARM (тук), TLDR: съхраняване на указател към unsigned char и след това се опитва да го преобразува в double * (двоен указател).

За да разрешите проблема си, ще трябва да поискате блок памет, който е подравнен по 4 байта, и да копирате неподравнените байтове + да го запълните с байтове боклук, за да сте сигурни, че е подравнен по 4 байта (следователно извършете ръчно подравняване на структурата на данните). След това можете да интерпретирате този обект като 4-байтов подравнен от новия му адрес.

От TurboJ в коментари, изричната грешка:

Cortex-M3 и M4 позволяват неподравнен достъп по подразбиране. Но те не позволяват достъп без връзка с инструкцията STRD, оттук и грешката.

Може също да намерите за полезно да разгледате това за налагане на структура на данните подравняване на ARM.

person Community    schedule 16.08.2013
comment
на x86 хитът на производителността е само когато достъпът ви пресича граница на ред 64B (или - не дай си боже - граница на страница). Просто достъпът до няколко неподравнени байта вътре в ред няма значение, вие просто получавате целия ред в кеша. - person Leeor; 16.08.2013
comment
@Leeor, поставих първоначалния си отговор като уики на общността, тъй като ARM не е област, с която съм твърде запознат (позволявам на други хора да допринасят за един колективен, силен отговор). Има доста подробности, които съм сигурен, че пропускам или не посочвам, не се колебайте да редактирате съответно оригиналната публикация. - person Jacob Pollack; 16.08.2013
comment
Съжалявам, вероятно съм още по-малко запознат с микроархитектурата на ARM, говорех за справката за x86 - person Leeor; 16.08.2013
comment
Благодаря! Проблемът беше, че неподравненият адрес произхожда от редица изчисления, които включват sizeof() и адресът беше подравнен към 2-байта. Ще модифицирам тази част от кода, за да подравня на 4-байта. - person rdrmntn; 16.08.2013
comment
Все още ми е интересно защо първият STR с един регистър работи, но не и STR с 2 регистъра. Някакви идеи за това? Има ли опции за компилатор за тези проблеми с подравняването? - person rdrmntn; 16.08.2013
comment
@Leeor: OP заяви, че това е код, генериран от C++ компилатор, така че не съм сигурен как последната връзка в отговора ви е уместна. - person idoby; 16.08.2013
comment
Cortex-M3 и M4 позволяват неподравнен достъп по подразбиране. Но те не позволяват достъп без връзка с инструкцията STRD, оттук и грешката. - person Turbo J; 16.08.2013
comment
ARM не са микрокодирани, x86 са били и/или са. arm и x86 ще имат едни и същи/подобни наказания за неподравнен достъп, и в двата случая зависи от ширината на шината дали трябва да направи един или два пълни цикъла. Кешът не е проблем с удара в производителността, кеширан или не, ръка или x86, ударът в производителността е налице, ако са необходими две прехвърляния, а не едно. Кешът може просто да умножи наказанието по някаква сума. - person old_timer; 16.08.2013
comment
@dwelch - наказанието не е просто извършване на друг достъп, в някои случаи разделянето прави някои вътрешни проверки по-сложни (кохерентност, препращане, заключване и т.н.), така че неправилен процесор (да речем, i7) може санкционирайте тези натоварвания да се изпълняват по ред. Това е значителен проблем, тъй като сериализира иначе паралелни операции. Arm-M3/4 няма да има този специфичен проблем (срещу правилно подравнени достъпи) - person Leeor; 21.08.2013
comment
има наказания, някои по-лоши от други в зависимост от архитектурата/платформата и ситуацията по време на инструкцията/прехвърлянето. - person old_timer; 21.08.2013

Следното е вярно най-малко за ARM архитектура (проверено на cortex M0):

Когато използваме инструкции за зареждане и съхраняване, паметта, до която имаме достъп, трябва да се дели на броя байтове, които се опитваме да осъществим от/към паметта, или ще получим изключение за твърда грешка.

eg:

LDR r0, = 0x1001
LDR r1, [r0]

Вторият ред в горния код ще даде твърда грешка, тъй като се опитват да прочетат 4 байта, но адресът на паметта не се дели на 4

Ако променим втория ред в горния код на следния

LDRB r1, [r0];//Заредете 1 байт от адреса

Горният ред няма да доведе до твърда грешка, тъй като се опитваме да осъществим достъп до 1 байт (1 байт може да бъде достъпен от всяко място в паметта)

Обърнете внимание и на следния пример;

LDR r0,= 0x1002
LDRH r1,[r0];   //Load half word from 0x1002

Горният ред няма да доведе до твърда грешка, тъй като достъпът до паметта е 2 байта и адресът се дели на 2.

person Gaurav Rathi    schedule 26.08.2014
comment
Изглежда, че за Cortex-M3 най-малко неподравнен адрес с LDR и STR инструкции поддържа неподравнен достъп и само генерира грешки при подравняване само когато битът UNALIGN_TRP е '1' в регистъра за контрол на конфигурацията. - person Uchia Itachi; 25.07.2015