Bit-Banding 등장 배경
임베디드 어플리케이션에서는 비트연산을 통해서 특정한 메모리의 비트를 조작하는 일이 많다.
일반적으로 다음과 같이 쓸 수 있다.
uint8_t temp = 0;
temp |= 0x4; // 3번째 비트 설정
temp &= ~0x4; // 3번째 비트 초기화‘단일 태스크’를 사용해서 동작한다면 위 코드도 문제가 없다. 하지만 ‘다중 태스크’를 사용하고 여기서 같은 주소를 참조하는 코드가 존재한다면 이는 ‘원자적(Atomic)’하게 동작하지 않게 되므로 코드가 제대로 동작하지 않게 될 것이다.
위 코드를 어셈블리로 변경한 코드를 살펴보자.
# temp |= 0x4;
a: 79fb ldrb r3, [r7, #7] ; 메모리에서 값 로드
c: f043 0304 orr.w r3, r3, #4 ; 3번째 비트 설정
10: 71fb strb r3, [r7, #7] ; 수정된 값 저장
# temp &= ~0x4;
12: 79fb ldrb r3, [r7, #7] ; 메모리에서 값 로드
14: f023 0304 bic.w r3, r3, #4 ; 3번째 비트 초기화
18: 71fb strb r3, [r7, #7] ; 수정된 값 저장비트 연산 과정에서 ‘메모리로 값을 로드’하고 연산후 이를 저장한다. 일반적인 과정으로 보이지만, 다중 태스크에서 로드한 다음에 ‘컨텍스트 스위칭’이 일어났다면? 이는 원하지 않은 방향으로 코드가 동작할 것이다. 즉, ‘아토믹’하지 않게된다.
또한 위 방식은 ‘CPU 사이클’을 낭비시키기도 한다.
이를 해결하기 위해서 필요한 것이 ‘비트 밴딩’이다.
비트 밴딩(Bit-Banding)
‘비트 밴딩’은 특정한 ‘워드’ 내부의 하나의 비트를 가리키는 공간을 따로 할당하여 조작하는 방식을 말한다.
하나의 ‘워드’가 4바이트를 가지므로 ‘비트 밴딩’을 사용한다면 워드의 각 비트를 가리키는 32개의 추가적인 ‘워드’(공간)가 필요한 셈이다.

비트 밴드를 활용한 예제 코드를 살펴보자.
*(volatile uint32_t*)0x22000008 = 1; // 비트 2를 1로 설정위와 같이 메모리에 바로 접근하여 비트를 설정하는 것을 볼 수 있다. 어셈블리로 번역하면 아래와 같다.
LDR R0, =0x22000008 ; 비트 밴딩 별칭 주소를 레지스터 R0에 로드
MOV R1, #1 ; R1에 1을 로드 (설정할 값)
STR R1, [R0] ; R1의 값을 R0가 가리키는 메모리 주소에 저장여전히 ‘다중 태스크’로 동작할 경우 조작한 비트가 덮어씌어질 수 있지만, ‘워드’ 전체가 덮어질 위험은 사라진다. 또한 워드에서 비트를 찾아가는 연산이 사라졌기에 보다 빠르게 연산을 수행하게 된다.
비트 밴딩 영역 주소
이제 조작하고자하는 ‘워드’를 통해서 ‘비트 밴딩’ 주소를 찾는 공식을 알아보자.
공식은 다음과 같다.
bit_band_address = alias_region_base + (region_base_offset x 32) + (bit_number x 4)설명만 보면 이해가 힘드므로 아래 예시를 같이 보자.
그림 5의 메모리 주소를 고려하여 비트 2에 접근하려고 하면:
- alias_region_base = 0x22000000
- region_base_offset = 0x20000000 - 0x20000000 = 0
- bit_band_address = 0x22000000 + 0x0 x 32 + (0x2 x 0x4) = 0x22000008
어렵게 생각할 것 없다. ‘**region_base_offset’**은 일반적인 메모리에서의 Word간 차이를 의미한다.
왜 32를 곱할까?
하나의 주소는 하나의 바이트 만큼의 크기를 가진다. 하나의 바이트에서 ‘비트 밴드’는 추가로 4바이트 만큼의 크기를 가지므로 일반 주소 하나의 바이트는 32개의 ‘비트 밴드’ 공간을 소모하게 된다. 따라서 다음 인덱스를 계산하기 위해 32를 곱한다.
예시의 (0x2 x 0x4)는 세부 비트의 주소를 계산하는 부분이다.
위 설명의 ‘비트 밴드’공간을 시각화하면 아래와 같다.
