programing

C++ 클래스 메모리 구조에 스페이서를 작성하려면 어떻게 해야 하나요?

nicescript 2022. 7. 6. 22:08
반응형

C++ 클래스 메모리 구조에 스페이서를 작성하려면 어떻게 해야 하나요?

문제

로우레벨 베어메탈 임베디드 콘텍스트에서는 C++ 구조 내에 이름을 붙이지 않고 메모리에 공백 공간을 만들어 사용자가 이러한 메모리 위치에 액세스하지 못하도록 합니다.

지금은 못생긴 걸 넣어서 달성했습니다.uint32_t :96;비트필드(bitfield)는 3개의 단어를 편리하게 대신하지만 GCC(bitfield가 너무 커서 uint32_t에 맞지 않음)에서 경고를 발생시킵니다.

정상적으로 동작하지만, 수백 개의 경고가 있는 라이브러리를 배포하는 경우는 그다지 깨끗하지 않습니다.

어떻게 하면 좋을까요?

애초에 왜 문제가 있는 거죠?

제가 작업하고 있는 프로젝트는 마이크로컨트롤러 라인 전체의 주변기기의 메모리 구조(STM32)를 정의하는 것입니다.이렇게 하면 대상 마이크로컨트롤러에 따라 모든 레지스터를 정의하는 여러 구조의 결합을 포함하는 클래스가 생성됩니다.

매우 심플한 페리페럴의 예로서 General Purpose Input/Output(GPIO; 범용 입력/출력)을 들 수 있습니다.

union
{

    struct
    {
        GPIO_MAP0_MODER;
        GPIO_MAP0_OTYPER;
        GPIO_MAP0_OSPEEDR;
        GPIO_MAP0_PUPDR;
        GPIO_MAP0_IDR;
        GPIO_MAP0_ODR;
        GPIO_MAP0_BSRR;
        GPIO_MAP0_LCKR;
        GPIO_MAP0_AFR;
        GPIO_MAP0_BRR;
        GPIO_MAP0_ASCR;
    };
    struct
    {
        GPIO_MAP1_CRL;
        GPIO_MAP1_CRH;
        GPIO_MAP1_IDR;
        GPIO_MAP1_ODR;
        GPIO_MAP1_BSRR;
        GPIO_MAP1_BRR;
        GPIO_MAP1_LCKR;
        uint32_t :32;
        GPIO_MAP1_AFRL;
        GPIO_MAP1_AFRH;
        uint32_t :64;
    };
    struct
    {
        uint32_t :192;
        GPIO_MAP2_BSRRL;
        GPIO_MAP2_BSRRH;
        uint32_t :160;
    };
};

모두 어디에GPIO_MAPx_YYY매크로로, 다음과 같이 정의됩니다.uint32_t :32또는 레지스터 타입(전용 구조).

여기 보시면uint32_t :192;잘 작동하지만 경고가 발생합니다.

지금까지 생각한 것:

몇 개로 바꿨을지도 몰라uint32_t :32;(여기는 6개입니다만, 몇 가지 극단적인 케이스가 있습니다.uint32_t :1344;(42) (특히)따라서 구조 생성 스크립트가 작성되어 있더라도 8k개의 다른 행 위에 100개 정도의 행을 추가하지 않는 것이 좋습니다.

정확한 경고 메시지는 다음과 같습니다.width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type(그냥 그늘이 너무 좋아요)

이 문제를 단순히 경고를 제거함으로써 해결할 것이 아니라,

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop

해결책이 될 수 있다...내가 발견하면TheRightFlag하지만, 이 스레드에서 지적된 바와 같이gcc/cp/class.c이 슬픈 코드 부분과 함께:

warning_at (DECL_SOURCE_LOCATION (field), 0,
        "width of %qD exceeds its type", field);

그 말은 우리가 이 일을 할 수 있는 일은 없다는 거지-Wxxx이 경고를 제거하는 플래그...

C++ 같은 방법은 어떻습니까?

namespace GPIO {

static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);

}

int main() {
    GPIO::MAP0_MODER = 42;
}

자동완성을 얻을 수 있는 이유는GPIO더미 패딩은 필요 없습니다.게다가 각 레지스터의 주소를 확인할 수 있기 때문에 컴파일러의 패딩 동작에 전혀 의존할 필요가 없습니다.

인접한 여러 익명 비트필드를 사용합니다.그래서 다음 대신:

    uint32_t :160;

예를 들어 다음과 같습니다.

    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;

익명으로 하고 싶은 레지스터마다 1개씩.

공간을 많이 채우면 더 명확해지고 매크로를 사용하여 32비트 공간을 반복하는 오류가 줄어들 수 있습니다.예를 들어 다음과 같습니다.

#define REPEAT_2(a) a a
#define REPEAT_4(a) REPEAT_2(a) REPEAT_2(a)
#define REPEAT_8(a) REPEAT_4(a) REPEAT_4(a)
#define REPEAT_16(a) REPEAT_8(a) REPEAT_8(a)
#define REPEAT_32(a) REPEAT_16(a) REPEAT_16(a)

다음으로 1344(42*32비트) 공간을 추가할 수 있습니다.

struct
{
    ...
    REPEAT_32(uint32_t :32;) 
    REPEAT_8(uint32_t :32;) 
    REPEAT_2(uint32_t :32;)
    ...
};

임베디드 시스템 영역에서는 구조를 사용하거나 레지스터 주소에 대한 포인터를 정의하여 하드웨어를 모델링할 수 있습니다.

구조별 모델링은 권장되지 않습니다.왜냐하면 컴파일러는 정렬을 위해 멤버 간에 패딩을 추가할 수 있기 때문입니다(많은 임베디드 시스템용 컴파일러는 구조 패킹을 위한 플러그마를 가지고 있지만).

예:

uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);

배열 표기법을 사용할 수도 있습니다.

uint16_t status = UART1[UART_STATUS_OFFSET];  

IMHO라는 구조를 사용해야 하는 경우 주소를 건너뛰는 가장 좋은 방법은 멤버를 정의하고 멤버에 액세스하지 않는 것입니다.

struct UART1
{
  uint16_t status;
  uint16_t reserved1; // Transmit register
  uint16_t receive_register;
};

델의 프로젝트 중 하나에는 상수와 다른 벤더의 구조가 모두 포함되어 있습니다(벤더 1은 상수를 사용하고 벤더 2는 구조를 사용).

이런 일에 수업을 이용하고 싶지 않다는 게 맞아

단, 굳이 n바이트 너비의 미사용 멤버를 추가하는 최선의 방법은 단순히 그렇게 하는 것입니다.

char unused[n];

클래스 멤버에 임의 패딩이 추가되는 것을 방지하기 위해 구현별 플러그마를 추가하는 경우 이 기능이 작동할 수 있습니다.


GNU C/C++(gcc, clang 및 동일한 확장을 지원하는 기타)의 경우 Atribute를 배치할 수 있는 유효한 장소 중 하나는 다음과 같습니다.

#include <stddef.h>
#include <stdint.h>
#include <assert.h>  // for C11 static_assert, so this is valid C as well as C++

struct __attribute__((packed)) GPIO {
    volatile uint32_t a;
    char unused[3];
    volatile uint32_t b;
};

static_assert(offsetof(struct GPIO, b) == 7, "wrong GPIO struct layout");

(Godbolt 컴파일러 탐색기의 예:offsetof(GPIO, b)= 7달러).

@Cliffords 및 @Adam Kotwasinski의 답변을 자세히 설명하려면:

#define REP10(a)        a a a a a a a a a a
#define REP1034(a)      REP10(REP10(REP10(a))) REP10(a a a) a a a a

struct foo {
        int before;
        REP1034(unsigned int :32;)
        int after;
};
int main(void){
        struct foo bar;
        return 0;
}

Clifford의 답변을 확장하려면 익명 비트필드를 항상 매크로 아웃할 수 있습니다.

그래서 대신

uint32_t :160;

사용하다

#define EMPTY_32_1 \
 uint32_t :32
#define EMPTY_32_2 \
 uint32_t :32;     \ // I guess this also can be replaced with uint64_t :64
 uint32_t :32
#define EMPTY_32_3 \
 uint32_t :32;     \
 uint32_t :32;     \
 uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N

그 다음에 이렇게 쓰면

struct A {
  EMPTY_UINT32(3);
  /* which resolves to EMPTY_32_3, which then resolves to real declarations */
}

도 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★EMPTY_32_X structure에 을 포함할 수 있습니다.bariants는 many bytes :( 「 」 , 「 」 ) 。

큰 스페이서를 32비트 그룹으로 정의합니다.

#define M_32(x)   M_2(M_16(x))
#define M_16(x)   M_2(M_8(x))
#define M_8(x)    M_2(M_4(x))
#define M_4(x)    M_2(M_2(x))
#define M_2(x)    x x

#define SPACER int : 32;

struct {
    M_32(SPACER) M_8(SPACER) M_4(SPACER)
};

저는 더 많은 구조를 도입하는 것이 유익할 것이라고 생각합니다.그것은 다시 스페이서 문제를 해결할 수 있을지도 모릅니다.

변종 이름 지정

플랫 네임스페이스는 좋지만, 문제는 결국 다양한 필드 집합이 생겨 모든 관련 필드를 함께 전달할 수 있는 간단한 방법이 없다는 것입니다.또한 익명 결합에서 익명 구조를 사용하면 구조 자체에 참조를 전달하거나 템플릿 매개변수로 사용할 수 없습니다.

따라서 첫 번째 단계로 다음 항목을 분리하는 것을 고려하겠습니다.

// GpioMap0.h
#pragma once

// #includes

namespace Gpio {
struct Map0 {
    GPIO_MAP0_MODER;
    GPIO_MAP0_OTYPER;
    GPIO_MAP0_OSPEEDR;
    GPIO_MAP0_PUPDR;
    GPIO_MAP0_IDR;
    GPIO_MAP0_ODR;
    GPIO_MAP0_BSRR;
    GPIO_MAP0_LCKR;
    GPIO_MAP0_AFR;
    GPIO_MAP0_BRR;
    GPIO_MAP0_ASCR;
};
} // namespace Gpio

// GpioMap1.h
#pragma once

// #includes

namespace Gpio {
struct Map1 {
    // fields
};
} // namespace Gpio

// ... others headers ...

마지막으로 글로벌헤더:

// Gpio.h
#pragma once

#include "GpioMap0.h"
#include "GpioMap1.h"
// ... other headers ...

namespace Gpio {
union Gpio {
    Map0 map0;
    Map1 map1;
    // ... others ...
};
} // namespace Gpio

이제 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.void special_map0(Gpio:: Map0 volatile& map);또한 사용 가능한 모든 아키텍처의 개요를 한눈에 볼 수 있습니다.

심플 스페이서

정의를 여러 헤더로 분할하면 헤더를 개별적으로 훨씬 쉽게 관리할 수 있습니다.

따라서 귀사의 사항을 저의 첫 접근 은 반복적인 '일부러'를입니다.std::uint32_t:32;에 몇 각작기 때문에 수 .

좀 더 이국적인 해결책을 생각해보실 의향이 있으시다면...

$를 소개합니다.

는 거의 않은 $C++는 C++는 C++를 사용합니다.숫자와는 달리 사용할 수 있는 시작 문자이기도 합니다.

A $ 것은을 찌푸리게 할 것, 소스코드에 나오는 은 눈살을 찌푸리게 할 이다.$$$$★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★이것은, 간단하게 이용할 수 있는 것입니다.

#define GPIO_RESERVED(Index_, N_) std::uint32_t $$$$##Index_[N_];

struct Map3 {
    GPIO_RESERVED(0, 6);
    GPIO_MAP2_BSRRL;
    GPIO_MAP2_BSRRH;
    GPIO_RESERVED(1, 5);
};

한 「하거나, 「lint」를 에 짜넣을 도 있습니다.$$$$C++로 하다

MCU I/O 포트 액세스에 구조체를 사용하지 않는 것에 동의합니다만, 원래의 질문에 대해서는 다음과 같이 대답할 수 있습니다.

struct __attribute__((packed)) test {
       char member1;
       char member2;
       volatile struct __attribute__((packed))
       {
       private:
              volatile char spacer_bytes[7];
       }  spacer;
       char member3;
       char member4;
};

경우, 이 경우 대체가 할 수 .__attribute__((packed))#pragma pack을 사용법

일반적으로 C++ 표준에서는 프라이빗 멤버와 퍼블릭멤버를 조합하면 메모리 레이아웃이 보증되지 않습니다.그러나 구조의 모든 비정적 멤버가 비공개인 경우에도 POD/표준 레이아웃으로 간주되며 이를 포함하는 구조도 마찬가지입니다.

어떤 이유에서인지 익명 구조의 구성원이 비공개일 경우 gcc가 경고를 생성하기 때문에 이름을 지정해야 했습니다.또는 다른 어나니머스 구조체로 래핑하면 경고(버그일 수 있음)도 제거됩니다.

:spacer비공개로 않기 같이 할 수 .

(char*)(void*)&testobj.spacer;

그러나 그러한 표현은 명백한 해킹처럼 보이며, 실수로서는커녕 정말 좋은 이유 없이 사용되지 않기를 바란다.

안티솔루션.

작업 안 함: 개인 필드와 공용 필드를 혼합합니다.

유니키 변수 이름을 생성하는 카운터가 있는 매크로가 유용할 수 있습니까?

#define CONCAT_IMPL( x, y ) x##y
#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
#define RESERVED MACRO_CONCAT(Reserved_var, __COUNTER__) 


struct {
    GPIO_MAP1_CRL;
    GPIO_MAP1_CRH;
    GPIO_MAP1_IDR;
    GPIO_MAP1_ODR;
    GPIO_MAP1_BSRR;
    GPIO_MAP1_BRR;
    GPIO_MAP1_LCKR;
private:
    char RESERVED[4];
public:
    GPIO_MAP1_AFRL;
    GPIO_MAP1_AFRH;
private:
    char RESERVED[8];
};

언급URL : https://stackoverflow.com/questions/53109888/how-do-i-create-a-spacer-in-a-c-class-memory-structure

반응형