문제
나는 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 문서에서 더 많이.