전자회로 실무 / 디지털 회로와 검증

화면이 이상할 때는 프레임 버퍼(framebuffer)에 쓴 값과 실제 표시를 따로 확인합니다

자작 보드에서 글자나 그림이 엉뚱하게 나오면 화면 출력 회로부터 바꿔 보고 싶어집니다. 하지만 잘못된 값을 저장한 것인지, 저장한 값을 화면으로 바꾸는 과정이 틀린 것인지 먼저 나눠 봐야 합니다. 알고 있는 바이트 하나를 써 보고, 같은 주소에서 읽은 값과 화면의 점 배열을 각각 비교하면 문제를 조사할 경로를 좁힐 수 있습니다.

화면에 보일 데이터를 저장하는 곳을 확인합니다

프레임 버퍼(framebuffer)는 화면을 이루는 각 점의 데이터를 저장하는 메모리 영역입니다. 화면의 한 점을 픽셀(pixel)이라고 합니다. 표시 회로가 이 데이터를 읽어 화면을 만들기 때문에, 저장값이 맞는지와 실제 표시가 맞는지를 따로 확인할 수 있습니다.

비트(bit)는 0 또는 1을 나타내며, 바이트(byte) 하나는 8비트입니다. 흑백 화면에서 한 픽셀에 1비트를 쓰는 1 bpp(bits per pixel) 형식이라면 바이트 하나에 픽셀 여덟 개를 담을 수 있습니다. 다만 그 여덟 개가 가로로 이어지는지, 세로로 묶이는지, 어느 비트가 먼저 표시되는지는 회로와 표시 형식에 따라 다릅니다.

메모리 맵(memory map)은 주소마다 어떤 메모리나 장치가 배치되는지 정한 정보입니다. 화면에 보이는 주소 숫자를 다른 보드에 그대로 쓰지 않습니다. 같은 주소가 다른 시스템에서는 일반 메모리, 주변장치 제어 레지스터 또는 접근할 수 없는 영역일 수 있습니다. 레지스터(register)는 장치의 동작 상태를 읽거나 설정하는 저장 공간으로, 값을 쓰는 행위 자체가 장치를 동작시킬 수도 있습니다.

쓰기 전에 시험할 영역과 표시 형식을 정합니다

이 방법은 화면 데이터가 놓인 읽기·쓰기 가능한 영역을 정확히 알고 있을 때 사용합니다. 운영체제나 표시 드라이버를 쓰는 환경이라면 그 환경이 제공하는 접근 방법으로 시험용 버퍼를 확보합니다. 임의의 물리 주소에 값을 쓰는 명령부터 실행하지 않습니다.

먼저 정할 항목확인하는 이유
시험할 주소와 접근 크기바이트 쓰기를 지원하는지, 실제 접근이 더 큰 단위로 이루어지는지 확인합니다.
픽셀당 비트 수와 비트 순서같은 0xAB도 흑백 8픽셀, 색상 성분 또는 다른 데이터로 해석될 수 있습니다.
한 줄의 저장 길이화면 너비 외에 줄 끝 여유 공간이 있을 수 있습니다. 다음 줄 주소를 너비만으로 계산하지 않습니다.
표시 중인 버퍼데이터를 쓴 버퍼와 화면이 읽는 버퍼가 같은지 확인합니다.
자동으로 화면을 그리는 코드커서, 콘솔, 화면 갱신 코드가 시험값을 바로 덮어쓰지 않게 합니다.

스트라이드(stride)는 한 줄의 시작 위치에서 다음 줄 시작 위치까지의 메모리 간격입니다. 두 개의 버퍼를 번갈아 쓰는 이중 버퍼링(double buffering)에서는 화면에 나오지 않는 버퍼를 수정했을 수도 있습니다. 이런 구조라면 표시 대상을 전환하는 절차까지 포함해야 합니다.

바이트 하나를 쓰고, 읽은 값과 표시를 각각 비교합니다

다음은 가로로 나란한 픽셀 여덟 개가 한 바이트에 들어 있고, 높은 자리 비트부터 왼쪽에 표시되며 1이 밝은 점이라는 설명용 조건입니다. 실제 보드가 이 조건과 같은지 먼저 확인합니다.

16진수(hexadecimal)는 한 자리를 0부터 15까지 나타내는 표기이며, 10부터 15에는 A부터 F를 씁니다. 0x는 뒤의 숫자가 16진수임을 나타냅니다. 0xAB는 2진수로 10101011입니다. 이 예시에서는 화면에 밝음·어두움·밝음·어두움·밝음·어두움·밝음·밝음 순서가 나타나야 합니다. 끝의 연속된 두 점은 좌우 비트 순서를 구분하는 데 도움이 됩니다.

시험용 주소 A에 0xAB를 쓰고 같은 주소에서 읽은 값, 화면에 나타난 여덟 점을 순서대로 확인합니다. 가로 1 bpp·높은 비트가 왼쪽·1이 밝음이라는 설명용 조건이며 시간은 실제 장비의 속도를 나타내지 않습니다.
정지 그림 확대
시험용 주소 A에 0xAB를 쓰고 같은 주소에서 읽은 값, 화면에 나타난 여덟 점을 순서대로 확인합니다. 가로 1 bpp·높은 비트가 왼쪽·1이 밝음이라는 설명용 조건이며 시간은 실제 장비의 속도를 나타내지 않습니다. · 그림을 클릭하면 확대됩니다.
  1. 시험할 영역을 확보하고 원래 값을 따로 읽어 보관합니다. 다른 코드가 그 영역을 바꾸지 않는 상태를 만듭니다. 글자를 출력하는 콘솔 자체가 같은 프레임 버퍼를 사용한다면 시험 위치를 피하거나 별도 출력 경로를 사용합니다.
  2. 그 주소에 지원되는 크기로 0xAB를 씁니다. 메모리 쓰기 명령의 이름과 숫자 입력 형식은 도구마다 다릅니다. 어떤 도구의 poke 같은 이름이 모든 보드에서 통하는 명령은 아닙니다.
  3. 같은 주소를 같은 접근 크기로 읽고 0xAB가 돌아오는지 확인합니다. 이것이 읽기 검증(readback)입니다. 화면만 보고 메모리에 무엇이 기록됐는지 추정하지 않습니다.
  4. 해당 위치의 픽셀 배열을 예상과 비교합니다. 읽은 값이 맞는데 점이 뒤집혀 보인다면 비트 순서나 밝음·어두움의 정의를 확인합니다. 전혀 다른 위치에 나타나면 주소 계산과 스트라이드, 표시 시작 위치를 살펴봅니다.
  5. 시험이 끝나면 저장해 둔 원래 값을 복원하고 필요한 화면 갱신을 수행합니다. 일시 중단한 그리기 코드도 다시 실행해 정상 표시를 확인합니다.

예를 들어 가로 200픽셀을 1 bpp로 빈틈없이 저장하면 한 줄에는 25바이트가 필요합니다. 그러나 실제 스트라이드가 32바이트라면 다음 줄은 시작 주소에서 32바이트 뒤에 있습니다. 차이인 7바이트를 무시하고 줄마다 25바이트씩 이동하면 점의 위치가 점차 어긋납니다. 이 숫자는 주소 계산을 설명하기 위한 예시이며 특정 보드의 고정 규격이 아닙니다.

두 결과가 다를 때는 조사할 곳도 달라집니다

읽기 검증화면 표시다음에 살펴볼 곳
쓴 값과 다릅니다.표시 결과와 무관합니다.주소, 접근 크기, 쓰기 허용 여부, 다른 코드의 덮어쓰기와 메모리 연결부터 확인합니다.
쓴 값과 같습니다.점의 순서나 밝기가 다릅니다.비트 순서, 픽셀 형식과 밝음·어두움의 정의를 확인합니다.
쓴 값과 같습니다.위치가 다릅니다.스트라이드, 표시 시작 주소, 가로·세로 묶음 방식과 좌표 계산을 확인합니다.
쓴 값과 같습니다.변화가 없습니다.실제 표시 중인 버퍼, 화면 갱신 조건, 출력 회로가 읽는 메모리를 확인합니다.
쓴 값과 같습니다.예상대로 보입니다.이번 주소와 패턴의 경로가 동작한 것입니다. 다른 주소와 다른 패턴은 별도로 확인합니다.

CPU(Central Processing Unit, 중앙처리장치)에 캐시(cache, 자주 쓰는 데이터를 임시 보관하는 고속 저장 공간)가 있으면 CPU가 읽은 값과 표시 회로가 읽는 메모리 내용이 아직 다를 수 있습니다. 실제로 이런 구조를 사용하는 환경에서는 드라이버나 하드웨어가 요구하는 동기화·캐시 처리를 따릅니다. volatile처럼 메모리 접근을 임의로 생략하지 않도록 컴파일러에 알리는 표기만으로 모든 캐시 문제가 해결된다고 보지는 않습니다. 캐시가 없는 단순 보드에 이 원인을 억지로 적용할 필요도 없습니다.

바이트 하나가 맞게 보였다고 메모리 전체와 화면 타이밍까지 모두 정상이라고 결론 내리지는 않습니다. 먼저 이 작은 시험으로 저장과 표시 중 어느 경로부터 조사할지 정한 뒤, 필요한 위치와 패턴을 늘립니다. 실제 주소, 접근 크기, 예상값, 읽은 값과 화면 결과를 함께 남기면 다음 시험과도 비교하기 쉽습니다.