2017-11-27 2 views
1

편집 이것은 "c"가 아닌 MSP430 마이크로 컨트롤러 용 Texas 컴파일러의 동작입니다. 아래의 코드는 텍사스 LTS 컴파일러 16.9.x를 사용하는 MSP430FR5969 16 비트 (20 비트 주소 지정) 마이크로 컨트롤러 용 코드 작성기 스튜디오 7로 컴파일됩니다. 결과는 STS 17.9.x와 동일합니다. 코드 예제를 편집하여 이름 지정이 아닌 명시 적으로 만들었습니다. 마이크로 컨트롤러 컴파일러는 명시 적으로 요청하지 않는 한 구조체 멤버를 워드 경계에 정렬하지 않습니다.바이트 구조체 멤버에 대한 C 워드 포인터

는이 같은 구조를 가지고 :

struct foo_{ 
    uint8_t low; 
    uint8_t high; 
} foo; 

을 그리고이 같은 영리하기 위해 노력하고있어 :

void bar(){ 
    uint16_t * volatile myvar; 
    myvar = (uint16_t *) &foo.low; 
    foobar(*bar); 
} 

void foobar(uint16_t value){ 
    printf("Value %d\n", value); 
} 

당신은 포인터가 * myVar에가 foo를 가리 키도록 기대. 낮은 주소이지만 완전히 다른 것입니다. 우리가 이식성을 창 밖으로 던지면 C에서 완벽하게 유효하지 않아야합니까?

편집 코드 예제를 편집했습니다. 예기치 않은 동작은 디버거에 표시된 내용 (홀수 메모리 위치의 워드 데이터를 표시 할 수 있음)과 실제 프로세서 (홀수 주소에서 읽는 워드에 0xFFFF를 반환 함) 사이의 불일치입니다. 때때로 컴파일 된 코드가 실패하고 FRAM 메모리의 구조체의 실제 위치 때문에 발생하지 않는 경우가 있습니다.

+3

과 2 –

+0

당신이 그것을 * 완전히 * 다른 확실하지 있는지? 예상 값과 실제 값을 16 진수로 보면 * any * relationship이 표시됩니까? 그리고 창 밖으로 이동성을 던져 잘못된 결과를 초래했기 때문에 어쩌면 당신은 결국 영리하지 못했습니다. –

+0

'* bar'는 포인터가 아닙니다. MCVE ([MCVE])로 질문하는 것이 좋습니다. –

답변

1

EDIT 이 동작은 바이트 주소가 홀수 메모리 위치에 저장되어 발생합니다. MSP430 마이크로 컨트롤러는 홀수 번지의 워드 값을 반환 할 수 없으며 대상 변수에 0xFFFF를 할당합니다. 구조체 멤버가 짝수 주소에 있기 때문에 예제/테스트 사례 중 일부가 작동합니다.

uint8_t myconfig_rw.current_l은 워드로 정렬되지 않은 메모리 주소 0x04563에 있습니다 (현재 컴파일 된 코드에 있음). 현재 = (USHORT *) & myconfig_rw.current_l; 라인이 생성 I 워드 변수 이것을 복종 때

MOVX.A #0x04563,0x0000a(SP) 

,이 라인 0x000e 대신 프로세서에서 워드를 판독 할 수없는 것처럼 어드레스 0x04563에 무엇이든 0xFFFF가 수신

MOVA 0x000a(SP),R15 
MOV.W @R15,0x000e(SP) 

생성 홀수 번지.

이제 코드 작성기 디버거은 홀수 주소에서 단어 값을 표시하는 데 문제가 없으며 포인터 주소에 예상 값 (0x140)을 행복하게 표시합니다.

문제를 해결하려면 2 바이트 값을 수동으로 배열하여 짝수 주소에서 시작하거나 단어와 바이트 사이를 수동으로 변환해야합니다. 레지스트리를 외부에서 버스트 읽기/쓰기 주소 지정이 가능하기 때문에 유니온을 사용하는 것은 해결책이 아닙니다. 패딩이 필요한지 아닌지에 따라 레지스터 주소가 1 바이트 씩 이동합니다.

일부 해결 방법은 단어 정렬 주소에서 시작하고 "다음 사람"이 혼란에 빠지기를 바라는 코드를 문서화하도록 레지스트리 구조를 정의하는 것입니다. 단어를 2 바이트로 또는 그 반대로 변환하는 것이 훨씬 안전한 접근 방법입니다.

OLD 이 MSP430FR5969 16 비트 마이크로 컨트롤러 텍사스 TLV 17.x 컴파일러 생성 코드로 코드 컴포저 스튜디오 (Code Composer Studio)에 특정 행동 것으로 보인다. 질문에있는 코드 스 니펫이 매우 좋지 않은 경우 "유효한"것으로 나타납니다. 실제 응용 프로그램에서 사용하면 결과를 예측할 수 없습니다. 이 조각을 고려 : 사실

struct myconfig_rw_{ 
    uint8_t current_l; 
    uint8_t current_h; 
} myconfig_rw; 

void PWM_Decode_Init(){ 

USHORT * volatile current; 
current = (USHORT *) &myconfig_rw.current_l; 
PWM_status.current_constant=Calculate_Current_Constant(*current, false); 
} 

* 현재의 의지가 myconfig_rw.current_l 및 .current_h에서 올바른 16 비트 값을 포함합니다. 그러나 Calculate_Current_Constant 함수에 전달 된 값이 올바르지 않으면 함수는 값 0xFFFF를받습니다.

결론적으로 포인터를 사용하여 자신의 이익을 위해 너무 영리하게하려고해서는 안됩니다. 예상되는 결과를 제공 할 것입니다

급식 작은 예 :

#include <msp430.h> 
#include <stdint.h> 
#include <stdio.h> 

struct foo_{ 
    uint8_t low; 
    uint8_t high; 
}foo; 

/** 
* main.c 
*/ 
void foobar(uint16_t test); 

int main(void) 
{ 
    uint16_t * volatile bar; 
    WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer 
    foo.low=0x10; 
    foo.high=0xBB; 
    bar = (uint16_t *) &foo.low; 
    printf("address %p, value %x\n", bar, *bar); 
    foobar(bar); 
    __no_operation(); 
    return 0; 
} 

void foobar(uint16_t test){ 
    printf("value %x\n", test); 
} 
+0

무슨 일이 있었는지 설명해 주셔서 감사합니다. 나는 몇 년 전과 비슷한 것을 봤다고 믿는다. 정확히 같은 문제는 아니지만 홀수/짝수 주소와 관련이 있습니다. 나는 컴파일러가 잘못된 결과를주는 코드를 생성했다고 생각하지 않지만 성능에 영향을 미쳤다. 나중에 설명을 찾았고 포인터가 짝수 경계에 있는지 확인해야했습니다. – dernst

+0

@dernst 텍사스 컴파일러에서는 #pragma DATA_ALIGN을 사용하여 짝수 주소에서 구조체를 시작할 수 있으므로 멤버를 정렬하여 지정된 하위 바이트 멤버가 짝수 주소에 있는지 확인할 수 있습니다. 이 경우에는 외부 단어 액세스가 바이트 연산으로 분리되어 있는지 확인하기 위해 재량이 더 나은 부분이며 검토 된 코드로 결정되었습니다. – Barleyman

+0

@dernst 결국 바이트 및 워드 멤버로 구현하기로 결정했습니다. 16 비트 부호있는 값을 바이트로 분할하여 처리하는 것은 너무 어려워졌습니다. 이름 기반 인 내부 연산과는 달리 오프셋을 통해 주소가 지정되었으므로 상위 바이트/하위 바이트에 외부 적으로 액세스 할 수 있습니다. 당신은 당신의 단어 변수가 단어 정렬되어 있는지 확인하기 위해 레지스트리를 돌봐야 만합니다. 그러나 그것은 그것들입니다. – Barleyman

1

나는 Groo 가장 가능성이 권리라고 생각합니다. 아래 코드는 예상대로 작동합니다. 주소가 어떻게 인쇄되는지는 의문의 여지가 없습니다. bar 대신 main에 printf 문을두면 컴파일되고 실행되면서 완벽하게 실행되지만 오도 된 결과가 발생합니다. 구조체를 패딩 할 수 있기 때문에이 정의되지 않은 동작은, 어쩌면는 sizeof (foo는)을 확인한다 구조체에 대한 이해에서

#include <stdint.h> 
#include <stdio.h> 

struct _foo{ 
    uint8_t low; 
    uint8_t high; 
} foo; 

void bar(){ 
    uint16_t *bar; 
    bar = (uint16_t *) &foo.low; 
    printf("From within foo() : foo.low addr: %x, foo.high addr: %x, bar addr: %x\n", (uint32_t) &foo.low, (uint32_t)&foo.high, (uint32_t)bar); 

} 

main(){ 
    bar(); 
    printf("From within main(): foo.low addr: %x, foo.high addr: %x, bar addr: %x\n", (uint32_t) &foo.low, (uint32_t)&foo.high, (uint32_t)bar); 
} 
+0

네, 휘발성 포인터 정의가 "volatile uint16_t * foo"가 아닌 "uint16_t * volatile foo"라는 것을 기억하는 한, 제 간단한 예제 코드는 실제로 예상대로 작동합니다. 컴파일 코드에 대한 내 대답을 참조하십시오. 그러나 실제 응용 프로그램의 코드 스 니펫은 작동하지 않으므로 * 사소한 프로그램에서는 이러한 종류의 해킹으로 인해 원하는 결과를 얻을 수 없습니다. 이렇게 캐스팅하면 * (uint16_t *) & foo.low; 결과적으로 0xFFFF로 끝날 것입니다 .. – Barleyman

+0

그래서 어떻게 생각하십니까? 휘발성 키워드를 사용하여 컴파일러에 영향을 미치지 만 코드는 최적화가 해제 된 상태로 작동합니까? – dernst

+0

휘발성 단지 디버거에서 WYSIWYG했다. 나는 그것에 대해 텍사스로 사건을 제기했다. 나는 그것이 "특징"이라고 확신한다. 주소를 변수로 저장하면 함수에 전달하고 포인터를 다시 포인터로 변환하면 expeceted로 작동합니다. 하지만 꼭 필요한 것은 아닙니다. 그리고 당신이 물어보기 전에, 아니요, 2 바이트 값을 16 비트 포인터로 변환 할 실제 이유가 없습니다. 함수는 어떤 경우에도 값을 가져 오기만하기 때문입니다. 저수준 함수가 그 구성 구조를 직접 조작해야하는 곳이 있습니다. 아주 좋지는 않습니다. – Barleyman