비트 조작 모범 사례
초보 C 프로그래머인 저는 기기에서 제어 비트를 설정할 때 가장 읽기 쉽고 이해하기 쉬운 해결책이 무엇인지 궁금합니다.어떤 기준이 있습니까?흉내 낼 수 있는 예시적인 코드가 있습니까?구글은 신뢰할만한 답변을 주지 않았습니다.
예를 들어, 제어 블록 맵은 다음과 같습니다.
제가 볼 수 있는 첫 번째 방법은 필요한 비트를 간단히 설정하는 것입니다.그것은 댓글로 많은 설명을 필요로 하고 그렇게 전문적이지는 않은 것 같습니다.
DMA_base_ptr[DMA_CONTROL_OFFS] = 0b10001100;
두 번째 방법은 비트 필드를 만드는 것입니다.처음 언급한 옵션과 달리 이렇게 사용되는 것을 경험한 적이 없기 때문에 이것을 고수해야 하는지 잘 모르겠습니다.
struct DMA_control_block_struct
{
unsigned int BYTE:1;
unsigned int HW:1;
// etc
} DMA_control_block_struct;
옵션 중 하나가 다른 것보다 낫습니까?제가 볼 수 없는 선택지가 있나요?
어떤 조언이든 매우 감사히 받겠습니다.
비트 필드의 문제점은 C 표준이 정의되는 순서가 구현되는 순서와 동일하다는 것을 지시하지 않는다는 것입니다.따라서 자신이 생각하는 비트를 설정하지 않을 수도 있습니다.
C 표준의 섹션 6.7.2.1p11은 다음과 같이 명시합니다.
구현은 비트 필드를 보유할 수 있을 만큼 충분히 큰 주소 지정 가능한 저장 장치를 할당할 수 있습니다.충분한 공간이 남아 있는 경우, 구조에서 다른 비트 필드를 즉시 따르는 비트 필드는 동일한 유닛의 인접한 비트에 패킹되어야 합니다.공간이 부족한 경우 적합하지 않은 비트 필드를 다음 유닛에 넣을지, 인접 유닛과 겹치는지가 구현 정의됩니다. 단위 내 비트 필드의 할당 순서(고차에서 저차로 또는 저차에서 고차로)가 구현 정의됩니다.주소 지정 가능한 저장 장치의 정렬이 지정되지 않았습니다.
, , 의 .struct iphdr
은 Linux. Linux: /usr/include/netine/ip.h 파일에서는 IP 헤더를 나타냅니다.
struct iphdr
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ihl:4;
unsigned int version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
unsigned int version:4;
unsigned int ihl:4;
#else
# error "Please fix <bits/endian.h>"
#endif
u_int8_t tos;
...
구현에 따라 비트 필드가 다른 순서로 배치되는 것을 확인할 수 있습니다.또한 이 동작은 시스템에 따라 다르므로 이 특정 검사 항목을 사용해서는 안 됩니다.이 파일은 시스템의 일부이므로 허용 가능합니다.다른 시스템에서는 이를 다양한 방식으로 구현할 수 있습니다.
그러니까 비트 필드를 사용하지 마세요.
가장 좋은 방법은 필요한 비트를 설정하는 것입니다.그러나 각 비트에 대해 명명된 상수를 정의하고 설정하려는 상수를 비트 단위로 OR하는 것이 좋습니다.예를 들어,
const uint8_t BIT_BYTE = 0x1;
const uint8_t BIT_HW = 0x2;
const uint8_t BIT_WORD = 0x4;
const uint8_t BIT_GO = 0x8;
const uint8_t BIT_I_EN = 0x10;
const uint8_t BIT_REEN = 0x20;
const uint8_t BIT_WEEN = 0x40;
const uint8_t BIT_LEEN = 0x80;
DMA_base_ptr[DMA_CONTROL_OFFS] = BIT_LEEN | BIT_GO | BIT_WORD;
내용을 , Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ ΔΔ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ, Δ0b
문,은여할수다을다 를 이동할 수 .1
비트 수에 따라 위치를 지정합니다. 즉:
#define DMA_BYTE (1U << 0)
#define DMA_HW (1U << 1)
#define DMA_WORD (1U << 2)
#define DMA_GO (1U << 3)
// …
마지막 숫자가 설명서의 "비트 번호" 열과 어떻게 일치하는지 기록합니다.
비트를 설정하고 지우는 용도는 변경되지 않습니다.
#define DMA_CONTROL_REG DMA_base_ptr[DMA_CONTROL_OFFS]
DMA_CONTROL_REG |= DMA_HW | DMA_WORD; // set HW and WORD
DMA_CONTROL_REG &= ~(DMA_BYTE | DMA_GO); // clear BYTE and GO
기존의 C 방식은 많은 비트를 정의하는 것입니다.
#define WORD 0x04
#define GO 0x08
#define I_EN 0x10
#define LEEN 0x80
그러면 당신의 초기화는
DMA_base_ptr[DMA_CONTROL_OFFS] = WORD | GO | LEEN;
다음을 사용하여 개별 비트를 설정할 수 있습니다.|
:
DMA_base_ptr[DMA_CONTROL_OFFS] |= I_EN;
을 를 하여 을 하여 를 &
그리고.~
:
DMA_base_ptr[DMA_CONTROL_OFFS] &= ~GO;
다음을 사용하여 개별 비트를 테스트할 수 있습니다.&
:
if(DMA_base_ptr[DMA_CONTROL_OFFS] & WORD) ...
하지만 비트 필드는 절대 사용하지 않습니다.사용법은 있지만 외부 사양에서 비트가 특정 위치에 있다고 정의할 때는 그렇지 않습니다. 여기서는 이러한 경우라고 가정합니다.
C FAQ 목록의 질문 20.7과 2.26도 참조하십시오.
비트 필드에 대한 표준이 없습니다.이 경우 매핑 및 비트 연산은 컴파일러에 따라 달라집니다.은진값값s 등의 0b0000
또한 표준화되어 있지 않습니다.일반적인 방법은 각 비트에 대해 16진수 값을 정의하는 것입니다.예를 들어,
#define BYTE (0x01)
#define HW (0x02)
/*etc*/
비트를 설정하려면 다음을 사용할 수 있습니다.
DMA_base_ptr[DMA_CONTROL_OFFS] |= HW;
또는 다음을 사용하여 비트를 지울 수 있습니다.
DMA_base_ptr[DMA_CONTROL_OFFS] &= ~HW;
현대의 C 컴파일러는 오버헤드 없이 사소한 인라인 기능을 잘 처리합니다.사용자가 비트나 정수를 조작할 필요가 없고 구현 세부 정보를 남용하지 않도록 모든 추상화 기능을 만들겠습니다.
구현 세부 정보에는 물론 상수를 사용할 수 있고 함수를 사용할 수는 없지만 API는 함수여야 합니다.또한 고대 컴파일러를 사용하는 경우 함수 대신 매크로를 사용할 수 있습니다.
예를 들어,
#include <stdbool.h>
#include <stdint.h>
typedef union DmaBase {
volatile uint8_t u8[32];
} DmaBase;
static inline DmaBase *const dma1__base(void) { return (void*)0x12340000; }
// instead of DMA_CONTROL_OFFS
static inline volatile uint8_t *dma_CONTROL(DmaBase *base) { return &(base->u8[12]); }
// instead of constants etc
static inline uint8_t dma__BYTE(void) { return 0x01; }
inline bool dma_BYTE(DmaBase *base) { return *dma_CONTROL(base) & dma__BYTE(); }
inline void dma_set_BYTE(DmaBase *base, bool val) {
if (val) *dma_CONTROL(base) |= dma__BYTE();
else *dma_CONTROL(base) &= ~dma__BYTE();
}
inline bool dma1_BYTE(void) { return dma_BYTE(dma1__base()); }
inline void dma1_set_BYTE(bool val) { dma_set_BYTE(dma1__base(), val); }
이러한 코드는 컴퓨터에서 생성해야 합니다.사용합니다.gsl
fame의) 정보를 하는 일부 합니다. (0mq fame의) 이것은 XML입니다.
두려움에 떠는 사람들이 뭐라고 했든 간에 당신은 비트 필드를 이용할 수 있습니다.코드에서 작업하려는 컴파일러 및 시스템 ABI가 비트 필드의 "구현 정의된" 측면을 어떻게 정의하는지만 알면 됩니다.행상인들이 "실행이 정의되었다"와 같은 단어를 굵은 글씨로 써서 겁먹지 마세요.
그러나 지금까지 다른 사람들이 놓친 것은 메모리 매핑 하드웨어 장치가 C와 같은 상위 언어를 다룰 때 직관에 어긋날 수 있는 다양한 측면과 그러한 언어가 제공하는 최적화 기능입니다.예를 들어 하드웨어 레지스터의 모든 읽기 또는 쓰기는 쓰기에서 비트가 변경되지 않더라도 때때로 부작용이 발생할 수 있습니다.한편, 옵티마이저는 생성된 코드가 언제 실제로 레지스터의 주소에 읽히거나 쓰고 있는지를 구분하기 어렵게 할 수 있으며, 레지스터를 설명하는 C 객체가 주의 깊게 다음과 같이 자격이 부여되는 경우에도volatile
, I/O가 발생할 때 이를 제어하기 위해서는 많은 주의가 필요합니다.
메모리 매핑 하드웨어 장치를 제대로 조작하려면 컴파일러와 시스템이 정의한 특정 기술을 사용해야 할 수도 있습니다.이것은 많은 임베디드 시스템의 경우입니다.컴파일러와 시스템 벤더가 실제로 비트 필드를 사용하는 경우도 있고, 리눅스가 사용하는 경우도 있습니다.먼저 당신의 컴파일러 매뉴얼을 읽어보는 것을 제안합니다.
인용하는 비트 설명 표는 Intel Avalon DMA 컨트롤러 코어의 제어 레지스터에 대한 것으로 보입니다."읽기/쓰기/지우기" 열은 특정 비트가 읽히거나 쓸 때 어떻게 동작하는지에 대한 힌트를 제공합니다.해당 장치의 상태 레지스터에는 0을 쓰면 비트 값이 지워지지만 기록된 값과 동일한 값을 다시 읽을 수 없는 비트 예제가 있습니다. 즉, 레지스터를 쓰는 것은 DONE 비트 값에 따라 장치에 부작용이 있을 수 있습니다.흥미롭게도 이 프로그램은 SOFTWARE RESET 비트를 "RW"로 문서화한 다음, 절차를 설명하면 리셋을 트리거하기 위해 1을 두 번 쓴 것으로 설명하고, DMA 전송이 활성화되어 있을 때 DMA 소프트웨어 리셋을 실행하면 (다음 시스템이 리셋될 때까지) 영구적인 버스 잠금이 발생할 수 있다고 경고합니다. 따라서 SOFTWARE RESET 비트는 최후의 수단으로 쓰지 않는 것이 좋습니다.C에서 리셋을 관리하는 것은 레지스터를 어떻게 설명하든 신중한 코딩이 필요할 것입니다.
표준의 경우, ISO/IEC는 "내장 프로세서를 지원하기 위한 확장"이라는 부제와 함께 "ISO/IEC TR 18037"로 알려진 "기술 보고서"를 작성했습니다.하드웨어 주소 지정 및 장치 I/O를 관리하기 위해 C를 사용하는 것과 관련된 여러 가지 문제에 대해 설명하며, 특히 질문에서 언급한 비트맵 레지스터의 종류에 대해서는 이들이 호출하는 포함 파일을 통해 사용 가능한 많은 매크로와 기술을 문서화합니다.<iohw.h>
가 이러한 파일을 매크로를 할 수 . 컴파일러에서 이러한 헤더 파일을 제공하는 경우 이러한 매크로를 사용할 수 있습니다.
TR 18037의 초안 사본을 사용할 수 있으며, 가장 최근의 것은 TR 18037(2007)이지만 다소 건조한 판독을 제공합니다.그러나 여기에는 다음의 예시적인 구현이 포함되어 있습니다.<iohw.h>
.
어쩌면 현실 세계의 좋은 예일 수도 있습니다.<iohw.h>
구현은 QNX에 있습니다.개요를 QNX는다를예다게을한예qg게(te다을i,d(hdeya ).enum
s 정수 값의 경우, 매크로 없음): QNX
값을 저장할 변수를 선언할 때는 비트를 알려진 기본값으로 초기화해야 합니다.C
때은 단지 그의 종류에 , 그리고 이 를 할 은 합니다 에 에 을 하는 이고 의 는 의 합니다 에 의 는 의 이고 하는 이 를 할 을 .변수를 초기화하지 않으면 해당 블록에 있는 메모리의 값/상태가 선언하기 전의 값/상태에 따라 변수의 값이 영향을 받기 때문에 정의되지 않은/예상치 못한 동작이 발생할 수 있습니다.변수를 기본값으로 초기화하면 이 메모리 블록의 기존 상태를 지우고 알 수 있는 상태가 됩니다.
가독성에 관해서는 비트 필드를 사용하여 비트 값을 저장해야 합니다.비트 필드를 사용하면 비트 값을 구조물에 저장할 수 있습니다.이렇게 하면 점 표기를 사용할 수 있기 때문에 정리하기가 쉬워집니다.또한 비트 필드 선언에 댓글을 달아 다양한 필드가 모범 사례로 사용되는 것을 설명해야 합니다.이것이 당신의 질문에 대한 답이 되길 바랍니다.행운을 빌어요C
프로그래밍!
언급URL : https://stackoverflow.com/questions/53118858/bit-manipulations-good-practices
'programing' 카테고리의 다른 글
문자열에 붙여넣기 (0) | 2023.09.15 |
---|---|
size_t 유형의 변수에 대한 교차 플랫폼 형식 문자열? (0) | 2023.09.15 |
Android Data Binding이 인수를 onClick 메서드에 전달합니다. (0) | 2023.09.15 |
큰 요청에 대해 응답 헤더를 업스트림에서 읽는 동안 Nginx 업스트림이 너무 일찍 닫혔습니다. (0) | 2023.09.15 |
MySQL: sql_mode 영구 설정 (0) | 2023.09.15 |