문제

나는 PCI 버스를 통해 VME-Bridge 칩 (Tundra Universe II)까지 32 개의 비트 읽기를 수행하려고 노력하고 있으며, 그런 다음 VME 버스로 가서 대상에 의해 픽업됩니다.

대상 VME 응용 프로그램은 D32 (32bits의 데이터 폭을 읽음) 만 허용하고 다른 것을 무시합니다.

VME 창 위에 매핑 된 비트 필드 구조 (NMAP'd)를 사용하면 비트 필드> 24 비트를 읽을 수 있지만 덜 실패합니다. 즉 :-

struct works {
    unsigned int a:24;
};

struct fails {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct main {
    works work;
    fails fail;
}
volatile *reg = function_that_creates_and_maps_the_vme_windows_returns_address()

이것은 구조물이 작동합니다 32 비트로 읽히지 만 읽기 실패합니다 구조 예를 들어 reg-> 실패 X 비트 읽기까지 고려되고 있습니다. (X는 16 또는 8 일 수 있습니까?)

그래서 질문은 다음과 같습니다.
a) 이것은 어디에서 축소됩니까? 컴파일러? OS? 아니면 툰드라 칩?
b) 수행 된 읽기 작업의 실제 크기는 얼마입니까?

나는 칩을 제외한 모든 것을 배제하고 싶다. 그것에 대한 문서는 웹에 있지만 PCI 버스를 통해 요청 된 데이터 폭이 32bits라는 것을 증명할 수 있다면 Tundra 칩에서 문제를 비난 할 수 있습니다!

편집하다:-
구체적인 예, 코드는 다음과 같습니다.

struct SVersion
{
    unsigned title         : 8;
    unsigned pecversion    : 8;
    unsigned majorversion  : 8;
    unsigned minorversion  : 8;
} Version;

이제 나는 이것을 이것으로 바꿨습니다 :-

union UPECVersion
{
    struct SVersion
    {
        unsigned title         : 8;
        unsigned pecversion    : 8;
        unsigned majorversion  : 8;
        unsigned minorversion  : 8;
    } Version;
    unsigned int dummy;
};

그리고 기본 주요 구조물 :-

typedef struct SEPUMap
{
    ...
    ...
    UPECVersion PECVersion;

};

그래서 여전히 모든 기준 코드를 변경해야합니다.

// perform dummy 32bit read
pEpuMap->PECVersion.dummy;

// get the bits out
x = pEpuMap->PECVersion.Version.minorversion;

그리고 두 번째 읽기가 원래 코드처럼 실제로 다시 읽지 않을지 어떻게 알 수 있습니까? (노조를 통해 이미 읽은 비트를 사용하는 대신!)

도움이 되었습니까?

해결책

예를 들어 Linux 커널에는 메모리 매핑 된 IO 읽기 및 쓰기를 명시 적으로 처리하는 인라인 기능이 있습니다. 새로운 커널에서는 인라인 어셈블리로 요약되는 큰 매크로 래퍼입니다. movl 지침이지만 오래된 커널은 다음과 같이 정의되었습니다.

#define readl(addr) (*(volatile unsigned int *) (addr))
#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))

다른 팁

컴파일러는 구조물의 크기를 메모리 정렬 설정의 배수로 조정합니다. 거의 모든 현대 컴파일러 가이 작업을 수행합니다. 일부 프로세서, 변수 및 지침에서 가지다 일부 메모리 정렬 값의 배수 인 메모리 주소 (종종 32 비트 또는 64 비트이지만 정렬은 프로세서 아키텍처에 따라 다름)를 시작합니다. 대부분의 현대 프로세서는 그렇지 않습니다 필요하다 더 이상 메모리 정렬 - 그러나 거의 모든 사람들이 상당한 성능의 이점. 따라서 컴파일러는 성능 향상을 위해 데이터를 정렬합니다.

그러나 많은 경우 (예 : 당신과 같은) 이것은 당신이 원하는 행동이 아닙니다. 구조의 크기는 여러 가지 이유로 매우 중요 할 수 있습니다. 이 경우 문제에 대한 다양한 방법이 있습니다.

한 가지 옵션은 컴파일러가 다른 정렬 설정을 사용하도록 강제하는 것입니다. 이를 수행하는 옵션은 컴파일러마다 다르므로 문서를 확인해야합니다. 그것은 일반적으로 어떤 종류의 #pragma입니다. 일부 컴파일러 (예 : Microsoft 컴파일러)에서는 매우 작은 코드 섹션에 대해서만 메모리 정렬을 변경할 수 있습니다. 예를 들어 (VC ++) :

#pragma pack(push)      // save the current alignment
#pragma pack(1)         // set the alignment to one byte
// Define variables that are alignment sensitive
#pragma pack(pop)       // restore the alignment

또 다른 옵션은 변수를 다른 방식으로 정의하는 것입니다. 고유 유형은 정렬에 따라 크기가 크지 않으므로 24 비트 비트 필드 대신 변수를 바이트 배열로 정의하는 또 다른 접근법이 있습니다.

마지막으로, 컴파일러가 원하는 크기에 상관없이 문자열을 만들고 읽고 쓰는 데 필요한 크기를 수동으로 기록 할 수 있습니다. 구조를 함께 연결하지 않는 한, 이것은 잘 작동합니다. 그러나 컴파일러가 후드 아래에 패딩 스트러크를 제공한다는 것을 기억하십시오. 공장 그리고 a 실패합니다 구조, 구조물 사이에 패딩 비트가있어 문제를 일으킬 수 있습니다.

대부분의 컴파일러에서는 8 비트보다 작은 데이터 유형을 만드는 것은 거의 불가능합니다. 대부분의 아키텍처는 그렇게 생각하지 않습니다. 8 비트 미만의 데이터 타입을 사용하는 대부분의 하드웨어 장치가 여전히 8 비트 배수로 나오는 방식으로 패킷을 배열하여 비트 조작을 수행하여 추출하거나 추출하거나 추출하거나 추출 할 수 있기 때문에 큰 문제는 아닙니다. 데이터 스트림의 값이 떠나거나 들어올 때 값을 인코딩합니다.

위에 나열된 모든 이유로, 이와 같은 하드웨어 장치와 함께 작동하는 많은 코드가 원시 바이트 어레이와 함께 작동하고 배열 내 데이터를 인코딩합니다. 현대 언어 구성의 많은 편의성을 잃어 버렸음에도 불구하고 결국 더 쉬워집니다.

나는 가치에 대해 궁금합니다 sizeof(struct fails). 1인가요? 이 경우, 당신이 a에 대한 포인터를 불러 일으켜 읽기를 수행하는 경우 struct fails, VME 버스에서 D8 읽기를 발행하는 것이 맞습니다.

필드를 추가하려고 시도 할 수 있습니다 unsigned int unused:29; 너의 ~에게 struct fails.

a의 크기 struct 비트 필드를 포함하여 필드 크기의 합과 같지 않습니다. 컴파일러는 C 및 C ++ 언어 사양에 따라 필드간에 패딩을 삽입 할 수 있습니다. struct. 패딩은 종종 정렬 목적으로 삽입됩니다.

임베디드 시스템 프로그래밍의 일반적인 방법은 데이터를 서명되지 않은 정수 그런 다음 비트 마스킹을 사용하여 흥미로운 비트를 검색하십시오. 이것은 내가 언급 한 위 규칙과 구조물에 필드를 "포장"에 대한 표준 컴파일러 매개 변수가 없다는 사실 때문입니다.

객체를 만드는 것이 좋습니다. class 또는 struct) 하드웨어와의 인터페이스 용. 물체가 데이터를 읽게 한 다음 비트를 다음과 같이 추출하십시오. bool 회원. 이로 인해 구현은 하드웨어에 가깝습니다. 나머지 소프트웨어는 신경 쓰지 않아야합니다 어떻게 비트가 구현됩니다.

비트 필드 위치를 정의 할 때 / 명명 된 상수를 정의 할 때이 형식을 제안합니다.

#define VALUE (1 << 비트 위치)
// OR
const unsigned int VALUE = 1 << 비트 위치;

이 형식은 더 읽기 쉬우 며 컴파일러가 산술을 수행하도록합니다. 계산은 컴파일 중에 발생하며 런타임 중에 영향을 미치지 않습니다.

IAN- 읽고있는 것의 크기에 대해 확신하고 싶다면 이와 같은 스트러크를 사용하지 않는 것이 좋습니다. 최적화 등을 기반으로해야 할 내용을 결정하려면 Int 's 또는 일반적으로 크기를 보장하는 데 필요한 작업을 명시 적으로 읽고 읽고 쓰여진 다음 노조/구조물로 변환하는 것과 같은 다른 작업을 수행하는 것이 좋습니다. 그 한계.

어떤 크기를 읽을 수 있는지 결정하는 것은 컴파일러입니다. 32 비트 읽기를 강제하기 위해 union:

union dev_word {
    struct dev_reg {
        unsigned int a:1;
        unsigned int b:1;
        unsigned int c:1;
    } fail;
    uint32_t dummy;
};

volatile union dev_word *vme_map_window();

휘발성 자격이있는 포인터를 통해 노동 조합을 읽는 것이 전체 노조를 강요하기에 충분하지 않다면 (나는 그것이 될 것이라고 생각하지만, 그것은 컴파일러에 의존 할 수 있다고 생각할 것입니다), 기능을 사용하여 필요한 간치를 제공 할 수 있습니다. :

volatile union dev_word *real_reg; /* Initialised with vme_map_window() */

union dev_word * const *reg_func(void)
{
    static union dev_word local_copy;
    static union dev_word * const static_ptr = &local_copy;

    local_copy = *real_reg;
    return &static_ptr;
}

#define reg (*reg_func())

... 그런 다음 (기존 코드와의 호환성을 위해) 액세스는 다음과 같이 수행됩니다.

reg->fail.a

GCC 플래그 -FStrict- 전용 비트 필드를 사용하고 비트 필드 변수를 휘발성 U32가 작동하는 것으로 정의했을 때 이전에 기술되었지만 정의 된 총 비트 수는 16보다 크지 않아야합니다.

예를 들어:

typedef     union{
    vu32    Word;
    struct{
        vu32    LATENCY     :3;
        vu32    HLFCYA      :1;
        vu32    PRFTBE      :1;
        vu32    PRFTBS      :1;  
    };
}tFlashACR;
.
tFLASH* const pFLASH    =   (tFLASH*)FLASH_BASE;
#define FLASH_LATENCY       pFLASH->ACR.LATENCY
.
FLASH_LATENCY = Latency;

GCC가 코드를 생성하게합니다

.
ldrb r1, [r3, #0]
.

바이트 읽기입니다. 그러나 typedef를 변경합니다

typedef     union{
    vu32    Word;
    struct{
        vu32    LATENCY     :3;
        vu32    HLFCYA      :1;
        vu32    PRFTBE      :1;
        vu32    PRFTBS      :1;
        vu32                :2;

        vu32    DUMMY1      :8;

        vu32    DUMMY2      :8;
    };
}tFlashACR;

결과 코드를 변경합니다

.
ldr r3, [r2, #0]
.

나는 유일한 해결책은
1) 모든 32 비트 int (서명되지 않은 오래)로 메인 구조물을 편집/작성하십시오.
2) 원래 비트 필드 스트러크를 유지하십시오
3) 필요한 각 액세스,
3.1) 구조물 멤버를 32 비트 단어로 읽고 비트 필드 구조물에 던져야합니다.
3.2) 필요한 비트 필드 요소를 읽으십시오. (그리고 글을 쓰고,이 비트 필드를 설정하고, 단어를 썼습니다!)

(1) "Main/Sepumap"구조물의 각 구성원이있는 고유 유형을 잃기 때문에 동일합니다.

최종 해결책 :-
대신에 :-

printf("FirmwareVersionMinor: 0x%x\n", pEpuMap->PECVersion);

이것 :-

SPECVersion ver = *(SPECVersion*)&pEpuMap->PECVersion;

printf("FirmwareVersionMinor: 0x%x\n", ver.minorversion);

내가 가진 유일한 문제는 글쓰기입니다! (쓰기는 이제 읽기/수정/쓰기입니다!)

// Read - Get current
_HVPSUControl temp = *(_HVPSUControl*)&pEpuMap->HVPSUControl;

// Modify - set to new value
temp.OperationalRequestPort = true;

// Write
volatile unsigned int *addr = reinterpret_cast<volatile unsigned int*>(&pEpuMap->HVPSUControl);

*addr = *reinterpret_cast<volatile unsigned int*>(&temp);

그 코드를 메소드로 정리해야합니다!

#define writel(addr, data) ( *(volatile unsigned long*)(&addr) = (*(volatile unsigned long*)(&data)) )

GCC 컴파일러를 사용하여 ARM에 동일한 문제가 있었는데, 여기서 메모리에 쓰기는 32 비트 단어가 아닌 바이트를 통해서만 있습니다.

해결책은 사용 사용 비트 필드를 정의하는 것입니다 휘발성 UINT32_T (또는 쓰기 위해 필요한 크기) :

union {
    volatile uint32_t XY;
    struct {
        volatile uint32_t XY_A : 4;
        volatile uint32_t XY_B : 12;
    };
};

그러나 컴파일하는 동안 GCC 또는 G ++에 추가해야합니다.

-fstrict-volatile-bitfields

GCC 문서에서 더 많이.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top