Fast Accurate Memory Test Code in C

IS026262의 Part5. 중 메모리에 대한 요구사항이 있다. 메모리 테스트에 대한 내용이 필요하여 아래 포스팅을 번역함.

https://barrgroup.com/Embedded-Systems/How-To/Memory-Test-Suite-C


재사용이 가능한 임베디드 소프트웨어가 있다면 메모리 테스트입니다. 이 포스팅에서는 세 가지 효율적이고 적용 가능한 메모리 테스트 기능 셋을 사용하여 가장 일반적인 메모리 문제를 테스트하는 방법을 보여줍니다.

거의 모든 임베디드 개발자는 자신의 경력 중 어느 시점에 도달하였을때 메모리 테스트를 작성하여야 합니다. 종종 프로토타입 하드웨어가 준비되면 보드 설계자는 주소 및 데이터 라인을 올바르게 연결하고 다양한 메모리 칩이 올바르게 작동하는지 확인하기를 원합니다. 그렇지 않은 경우에도 최소한 시스템이 리셋 될 때까지 온보드 RAM을 테스트하는 것이 바람직합니다. 임베디드 소프트웨어 개발자는 무엇이 잘못 될지 파악하고 잠재적인 문제를 발견 할 수있는 일련의 테스트를 설계해야합니다.

언뜻보기에 메모리 테스트를 작성하는 것은 상당히 간단한 시도처럼 보일 수 있습니다. 그러나 문제를 좀 더 면밀히 살펴보면 간단한 테스트로 미묘한 메모리 문제를 감지하는 것이 어려울 수 있음을 알 수 있습니다. 사실, 프로그래머인 naïveté의 결과로 많은 임베디드 시스템은 가장 치명적인 메모리 오류만 감지하는 메모리 테스트를 포함합니다.  믿을수 없겠지만, 이들 중 일부는 메모리 칩이 보드에서 제거되었음을 알지 못할 수도 있습니다!

메모리 테스트의 목적은 메모리 장치의 각 저장 위치가 작동하는지 확인하는 것입니다. 즉, 특정 주소에 값 50을 저장하면 동일한 값에 다른 값이 기록 될 때까지 해당 값을 찾을 수 있습니다. 모든 메모리 테스트의 기본 아이디어는 메모리 장치의 각 주소에 일부 데이터 집합을 쓰고 데이터를 다시 읽음으로써 데이터를 확인하는 것입니다. 다시 읽은 모든 값이 쓰여진 값과 같으면 메모리 장치는 테스트를 통과했다고 말합니다. 보시다시피, 합격 결과가 의미가 있음을 확신 할 수있는 일련의 데이터 값을 신중하게 선택해야합니다.

물론 방금 설명한 것과 같은 메모리 테스트는 파괴적일 수 밖에 없습니다. 메모리를 테스트하는 과정에서 이전 내용을 덮어 써야합니다. 일반적으로 비 휘발성 메모리의 내용을 덮어 쓰는 것은 비현실적이므로이 기사에서 설명하는 테스트는 일반적으로 RAM 테스트에만 사용됩니다. 그러나 플래시와 같은 비 휘발성 메모리 장치의 내용이 제품 개발 단계 에서처럼 중요하지 않은 경우에도 동일한 알고리즘을 사용하여 해당 장치를 테스트 할 수 있습니다.

공통 메모리 문제

가능한 테스트 알고리즘을 구현하기 전에 발생할 수있는 메모리 문제 유형에 대해 잘 알고 있어야합니다. 소프트웨어 엔지니어들 사이에서 가장 흔한 오해 중 하나는 대부분의 메모리 문제가 칩 자체에서 발생한다는 것입니다. 한 때는(몇 십 년 전) 중요한 문제 였지만 이 유형의 문제는 점차 희소합니다. 요즈음, 메모리 장치 제조업체는 칩 배치마다 다양한 후반 작업 테스트를 수행합니다. 특정 배치에 문제가 있으면 나쁜 칩 중 하나가 시스템에 침투 할 가능성이 극도로 희박합니다.

발생할 수있는 메모리 칩 문제 중 하나는 치명적인 오류입니다. 이것은 일반적으로 제조 후 칩에 물리적 또는 전기적 손상의 일종으로 인해 발생합니다. 치명적인 오류는 일반적이지 않으며 일반적으로 칩의 많은 부분에 영향을 미칩니다. 큰 영역이 영향을 받기 때문에 치명적인 오류가 적절한 테스트 알고리즘에 의해 감지된다고 가정하는 것이 합리적입니다.

내 경험상, 실제 메모리 문제의 가장 일반적인 원인은 회로 기판입니다. 일반적인 회로 보드 문제는 프로세서와 메모리 장치 간의 배선, 메모리 칩 누락 및 잘못 삽입 된 메모리 칩 문제입니다. 이것들은 좋은 메모리 테스트 알고리즘이 탐지 할 수 있어야하는 문제점들이다. 그러한 테스트는 특별히 그들을 찾지 않고 치명적인 메모리 실패를 탐지 할 수 있어야합니다. 이제 회로 보드 문제에 대해 자세히 설명합시다.

전기 배선 문제

전기 배선 문제는 보드의 설계 또는 생산상의 오류 또는 제조 후 발생한 손상의 결과로 발생할 수 있습니다. 메모리 장치를 프로세서에 연결하는 각 와이어는 주소 라인, 데이터 라인 또는 제어 라인의 세 가지 유형 중 하나입니다. 주소 및 데이터 라인은 메모리 위치를 선택하고 데이터를 각각 전송하는 데 사용됩니다. 제어 라인은 프로세서가 위치를 읽거나 쓰고 싶어하는지 그리고 데이터가 언제 전송되는지를 메모리 장치에 알려줍니다. 불행하게도 하나 이상의 와이어가 단락(예 : 보드의 다른 와이어에 연결)되거나 단선(다른 것과 연결되지 않음) 등의 방식으로 부적절하게 연결되거나 손상 될 수 있습니다. 이러한 문제는 종종 납땜이 번지거나 또는 패턴이 끊어지는 일부에 의해 발생합니다. 두 경우 모두 그림 1에 나와 있습니다.

shorts opens wiring problems
그림 1. 배선 문제

프로세서에 대한 전기 연결 문제로 인해 메모리 장치가 올바르게 작동하지 않게됩니다. 데이터가 잘못 저장되거나, 잘못된 주소에 저장되거나, 전혀 저장되지 않을 수 있습니다. 이러한 증상은 각각 데이터, 주소 및 제어 라인의 배선 문제로 설명 할 수 있습니다.

데이터 라인에 문제가 있는 경우 여러 데이터 비트가 “stuck togeter”것처럼 보일 수 있습니다 (예 : 전송 된 데이터에 관계없이 둘 이상의 비트가 항상 같은 값을 포함). 비슷하게, 데이터 비트는 “Stuck-at high”(항상 1) 또는 “Stuck-at low”(항상 0) 일 수 있습니다. 이러한 문제는 각 데이터 핀을 다른 모든 것과 독립적으로 0과 1로 설정할 수 있는지 테스트하도록 설계된 일련의 데이터 값을 작성하여 감지 할 수 있습니다.

주소 표시 줄에 배선 문제가 있는 경우 두 메모리 위치의 내용이 겹치는 것처럼 보일 수 있습니다. 즉, 한 주소에 기록 된 데이터는 실제로 다른 주소의 내용을 덮어 씁니다. 이것은 쇼트 또는 오픈 된 어드레스 비트가 메모리 장치로 하여금 프로세서에 의해 선택된 어드레스와 다른 어드레스를 보게하기 때문에 발생합니다.

또 다른 가능성은 제어 선 중 하나가 단락되거나 개방된다는 것입니다. 이론적으로 제어 라인 문제에 대한 특정 테스트를 개발하는 것이 가능하지만, 일반적인 테스트를 설명하는 것은 불가능합니다. 많은 제어 신호의 동작은 프로세서 또는 메모리 아키텍처에 따라 다릅니다. 다행히도 제어 라인에 문제가 있으면 메모리가 전혀 작동하지 않으며 다른 메모리 테스트에 의해 감지됩니다. 컨트롤 라인에 문제가 있다고 생각되면 특정 테스트를 수행하기 전에 디자이너의 조언을 구하는 것이 가장 좋습니다.

누락 된 메모리 칩

누락 된 메모리 칩은 분명히 발견되어야하는 문제입니다. 불행하게도, 연결되지 않은 전선의 용량 성(전압을 부여했을 때 그대로 전기적인 전압을 일시적으로 유지하는 것. 접지 되지 않았기 때문)으로 인해 일부 메모리 테스트는이 문제를 감지하지 못합니다. 예를 들어, 다음 테스트 알고리즘을 사용하기로 결정했다고 가정하십시오. 메모리의 첫 번째 위치에 값 1을 쓰고, 값을 다시 읽음으로써 값을 확인하고, 두 번째 위치에 2를 쓰고, 값을 확인하고, 세 번째 위치에 3을 쓰고, 확인하는등, 각각의 판독은 대응하는 기입 직후에 발생하기 때문에, 판독 된 데이터는 이전의 기입으로 부터의 데이터 버스 상에 남아있는 전압 이상을 나타낼 수 없다. 데이터를 너무 빨리 다시 읽으면 버스의 다른 끝에 메모리 칩이 없더라도 데이터가 메모리에 올바르게 저장되었다고 표시됩니다!

누락 된 메모리 칩을 감지하려면 테스트를 변경해야합니다. 대응하는 기록 직후에 검증 판독을 수행하는 대신에, 수 개의 연속 기록을 수행 한 다음 동일한 갯수를 연속으로 읽어오는 것이 바람직합니다. 예를 들어 값 1을 첫 번째 위치에, 2를 두 번째 위치에, 3을 세 번째 위치에 기록한 다음 첫 번째 위치, 두 번째 위치 등에서 데이터를 확인하십시오. 데이터 값이 고유 한 경우, 누락 된 칩이 감지됩니다. 첫 번째 값은 처음 값 (1)이 아닌 마지막 값 (3)에 해당합니다.

부적절하게 삽입 된 칩

메모리 칩이 있지만 소켓에 잘못 삽입 된 경우, 시스템은 일반적으로 배선 문제 또는 누락 된 칩이있는 것처럼 동작합니다. 즉, 메모리 칩의 일부 핀은 소켓에 전혀 연결되지 않거나 잘못된 위치에 연결됩니다. 이 핀은 데이터 버스, 주소 버스 또는 제어 배선의 일부가됩니다. 따라서 배선 문제와 칩 누락 여부를 테스트하는 동안 부적절하게 삽입 된 칩은 자동으로 감지됩니다.

메모리 테스트 전략

계속하기 전에 우리가 탐지 할 수 있어야 하는 메모리 문제 유형을 빠르게 검토합니다. 메모리 칩은 내부 오류가 있는 경우는 드물지만, 실제로 발생하면 치명적일 수 있으며 모든 테스트에 의해 감지됩니다. 일반적인 문제의 근원은 배선 문제가 발생할 수 있거나 메모리 칩이 누락되거나 부적절하게 삽입 될 수있는 회로 기판입니다. 다른 메모리 문제가 발생할 수 있지만 여기에 설명 된 문제가 가장 일반적입니다.

테스트 데이터와 주소 테스트 순서를 주의 깊게 선택하면 위에서 설명한 모든 메모리 문제를 감지 할 수 있습니다. 일반적으로 메모리 테스트를 작고 단순한 부분으로 나누는 것이 가장 좋습니다. 이렇게 하면 전체 테스트의 효율성과 코드의 가독성을 향상시킬 수 있습니다. 보다 구체적인 테스트를 통해 문제가 발생한 경우 문제의 원인에 대한 자세한 정보를 제공 할 수도 있습니다.

데이터 버스 테스트, 주소 버스 테스트 및 장치 테스트라는 세 가지 개별 메모리 테스트를 하는 것이 가장 좋습니다. 앞의 두 가지 테스트는 전기 배선 문제를 및 부적절하게 칩을 삽입하는 문제을 탐지하는 반면 세 번째 테스트는 누락 된 칩 문제와 치명적인 오류를 감지하기 위한 것입니다. 의도하지는 않은 결과지만, 장치 테스트는 제어 버스의 문제점을 밝혀 낼 것이지만, 그러한 문제의 근원에 대한 유용한 정보는 제공하지 않을 것이다.

이 세 가지 검사를 실행하는 순서가 중요합니다. 적절한 순서는 데이터 버스 테스트, 주소 버스 테스트, 장치 테스트 순입니다. 이는 주소 버스 테스트가 작동중인 데이터 버스를 가정하고, 주소 버스와 데이터 버스가 모두 양호한 것으로 알려지지 않는 한 장치 테스트 결과가 의미가 없기 때문입니다. 테스트 중 하나라도 실패하면 보드 설계자와 협력하여 문제의 원인을 찾아야 합니다. 테스트가 실패한 데이터 값 또는 주소를 살펴봄으로써 회로 기판에서 문제를 신속하게 격리 할 수 ​​있어야 합니다.

데이터 버스 테스트

우리가 테스트하고자하는 첫 번째 일은 데이터 버스 배선입니다. 프로세서가 데이터 버스에 입력 한 값이 다른 쪽 끝의 메모리 장치에서 올바르게 수신되는지 확인해야합니다. 테스트 할 수있는 가장 확실한 방법은 가능한 모든 데이터 값을 쓰고 메모리 장치가 각 데이터를 성공적으로 저장하는지 확인하는 것입니다. 그러나 이것이 가능한 가장 효율적인 테스트는 아닙니다. 더 빠른 방법은 한 번에 한 비트 씩 버스를 테스트하는 것입니다. 데이터 버스는 각 데이터 비트가 다른 데이터 비트와 독립적으로 0 및 1로 설정 될 수 있는지 테스트를 통과합니다.

00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000

표 1. “워킹 1의 테스트”에 대한 연속적인 데이터 값

독립적으로 각 비트를 테스트하는 좋은 방법은 소위 “워킹 1의 테스트”를 수행하는 것입니다. 표 1은 이 테스트의 8 비트 버전에서 사용 된 데이터 패턴을 보여줍니다. 이 테스트의 이름은 단일 데이터 비트가 1로 설정되고 전체 데이터 단어를 통해 “걷는”사실로 부터 옵니다. 테스트 할 데이터 값의 수는 데이터 버스의 너비와 같습니다. 이렇게 하면 테스트 패턴 수가 2n에서 n으로 줄어 듭니다. 여기서 n은 데이터 버스의 너비입니다.

이 시점에서 데이터 버스 만 테스트하기 때문에 모든 데이터 값을 동일한 주소에 쓸 수 있습니다. 메모리 장치 내의 모든 주소가 수행합니다. 그러나 데이터 버스가 둘 이상의 메모리 칩으로 갈 때 분할되면 각 칩 내 하나씩 여러 주소에서 데이터 버스 테스트를 수행해야합니다.

워킹 1의 테스트를 수행하려면 테이블에 첫 번째 데이터 값을 쓰고 다시 읽은 다음 두 번째 값을 쓰고 확인하는 등의 방법으로 확인하십시오. 테이블 끝에 도달하면 테스트가 완료됩니다. 이번에는 해당 칩을 찾지 않아도됩니다. 사실 이 테스트는 메모리 칩이 설치되지 않은 경우에도 의미있는 결과를 제공합니다.

typedef unsigned char datum;    /* Set the data bus width to 8 bits.  */

/**********************************************************************
 *
 * Function:    memTestDataBus()
 *
 * Description: Test the data bus wiring in a memory region by
 *              performing a walking 1's test at a fixed address
 *              within that region.  The address (and hence the
 *              memory region) is selected by the caller.
 *
 * Notes:       
 *
 * Returns:     0 if the test succeeds.  
 *              A non-zero result is the first pattern that failed.
 *
 **********************************************************************/
datum
memTestDataBus(volatile datum * address)
{
    datum pattern;

    /*
     * Perform a walking 1's test at the given address.
     */
    for (pattern = 1; pattern != 0; pattern <<= 1)
    {
        /*
         * Write the test pattern.
         */
        *address = pattern;

        /*
         * Read it back (immediately is okay for this test).
         */
        if (*address != pattern) 
        {
            return (pattern);
        }
    }

    return (0);

}   /* memTestDataBus() */

Listing 1. 데이터 버스 테스트

Listing 1의 memTestDataBus () 함수는 C에서 walking 1의 테스트를 구현하는 방법을 보여줍니다. 호출자가 테스트 주소를 선택하고 해당 주소에서 전체 데이터 값 세트를 테스트 한다고 가정합니다. 데이터 버스가 제대로 작동하면 함수는 0을 반환하고 그렇지 않으면 테스트에 실패한 데이터 값을 반환합니다. 반환 값에 설정된 비트는 첫 번째 오류 데이터 행이있는 경우 해당 행에 해당합니다.

주소 버스 테스트

데이터 버스가 제대로 작동하는지 확인한 후 다음에 주소 버스를 테스트해야합니다. 주소 버스 문제로 인해 메모리 위치가 겹치는 것을 기억하십시오. 가능한 많은 주소가 중복 될 수 있습니다. 그러나 모든 가능한 조합을 확인할 필요는 없습니다. 위의 데이터 버스 테스트의 예를 따르고 테스트하는 동안 각 주소 비트를 격리해야합니다. 각 주소 핀을 다른 주소에 영향을주지 않고 0과 1로 설정할 수 있는지 확인하면됩니다.

모든 가능한 조합을 포함 할 수있는 가장 작은 주소 집합은 “2의 제곱 (power-of-two)”주소 집합입니다. 이 주소는 걷기 1의 테스트에 사용 된 데이터 값 세트와 유사합니다. 해당 메모리 위치는 0001h, 0002h, 0004h, 0008h, 0010h, 0020h 등입니다. 또한 주소 0000h도 테스트해야합니다. 위치가 겹칠 가능성이 있기 때문에 주소 버스 테스트를 구현하기가 더 어려워집니다. 주소 중 하나에 기록한 후 다른 주소가 겹쳐 쓰여져 있지 않은지 확인해야 합니다.

이 방법으로 모든 주소 라인을 테스트 할 수있는 것은 아닙니다. 주소의 일부 – 가장 왼쪽 비트 -는 메모리 칩 자체를 선택합니다. 데이터 버스 폭이 8 비트보다 크면 다른 부분, 즉 가장 오른쪽 비트가 중요하지 않을 수 있습니다. 이러한 추가 비트는 테스트를 통해 일정하게 유지되며 테스트 주소의 수를 줄입니다. 예를 들어 프로세서에 32 개의 주소 비트가 있으면 최대 4GB의 메모리를 주소 지정할 수 있습니다. 128K 메모리 블록을 테스트하려면 15 개의 최상위 주소 비트가 일정하게 유지됩니다. 이 경우, 주소 버스의 가장 오른쪽 17 비트(=128k) 만 실제로 테스트 할 수 있습니다. (128K는 총 4GB 주소 공간의 1 / 32,768 번째입니다.)

두 개의 메모리 위치가 겹치지 않았음을 확인하려면 먼저 디바이스 내의 2의 거듭 제곱 오프셋에 초기 데이터 값을 써야합니다. 그런 다음 새로운 값 (초기 값의 반전 된 복사본)을 첫 번째 테스트 오프셋에 쓰고 다른 모든 오프셋에서 초기 데이터 값이 저장되는지 확인합니다. 방금 작성한 위치가 아닌 다른 위치에서 새 데이터 값을 찾으면 현재 주소 비트에 문제가 있음을 발견했습니다. 겹침이 없으면 나머지 각 오프셋에 대해 절차를 반복하십시오.

/**********************************************************************
 *
 * Function:    memTestAddressBus()
 *
 * Description: Test the address bus wiring in a memory region by
 *              performing a walking 1's test on the relevant bits
 *              of the address and checking for aliasing. This test
 *              will find single-bit address failures such as stuck
 *              -high, stuck-low, and shorted pins.  The base address
 *              and size of the region are selected by the caller.
 *
 * Notes:       For best results, the selected base address should
 *              have enough LSB 0's to guarantee single address bit
 *              changes.  For example, to test a 64-Kbyte region, 
 *              select a base address on a 64-Kbyte boundary.  Also, 
 *              select the region size as a power-of-two--if at all 
 *              possible.
 *
 * Returns:     NULL if the test succeeds.  
 *              A non-zero result is the first address at which an
 *              aliasing problem was uncovered.  By examining the
 *              contents of memory, it may be possible to gather
 *              additional information about the problem.
 *
 **********************************************************************/
datum * 
memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes)
{
    unsigned long addressMask = (nBytes/sizeof(datum) - 1);
    unsigned long offset;
    unsigned long testOffset;

    datum pattern     = (datum) 0xAAAAAAAA;
    datum antipattern = (datum) 0x55555555;




    /*
     * Write the default pattern at each of the power-of-two offsets.
     */
    for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
    {
        baseAddress[offset] = pattern;
    }

    /* 
     * Check for address bits stuck high.
     */
    testOffset = 0;
    baseAddress[testOffset] = antipattern;

    for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
    {
        if (baseAddress[offset] != pattern)
        {
            return ((datum *) &baseAddress[offset]);
        }
    }

    baseAddress[testOffset] = pattern;

    /*
     * Check for address bits stuck low or shorted.
     */
    for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1)
    {
        baseAddress[testOffset] = antipattern;

if (baseAddress[0] != pattern)
 {
 return ((datum *) &baseAddress[testOffset]);
 }

        for (offset = 1; (offset & addressMask) != 0; offset <<= 1)
        {
            if ((baseAddress[offset] != pattern) && (offset != testOffset))
            {
                return ((datum *) &baseAddress[testOffset]);
            }
        }

        baseAddress[testOffset] = pattern;
    }

    return (NULL);

}   /* memTestAddressBus() */

Listing 2. 주소 버스 테스트

Listing 2의 memTestAddressBus () 함수는 이것을 어떻게 실제로 수행 할 수 있는지 보여준다. 함수는 두 개의 매개 변수를받습니다. 첫 번째 매개 변수는 테스트 할 메모리 블록의 기본 주소이고 두 번째 매개 변수는 크기 (바이트)입니다. 크기는 테스트 할 주소 비트를 결정하는 데 사용됩니다. 최상의 결과를 얻으려면 기본 주소는 각 비트에 0을 포함하고 있어야 합니다. 주소 버스 테스트가 실패하면 첫 번째 오류가 감지 된 주소가 반환됩니다. 그렇지 않으면 이 함수는 성공을 나타 내기 위해 NULL을 반환합니다.

장치 테스트

주소 및 데이터 버스 배선이 작동 중임을 알게되면 메모리 장치 자체의 무결성을 테스트해야 합니다. 테스트 할 것은 디바이스의 모든 비트가 0과 1을 모두 유지할 수 있다는 것입니다. 이것은 구현하기에 상당히 간단한 테스트이지만 이전 두 개보다 실행 시간이 훨씬 오래 걸립니다.

완벽한 장치 테스트를 위해서는 모든 메모리 위치를 두 번 방문 (쓰기 및 확인)해야합니다. 두 번째 패스에서 해당 값을 반전하기 전 까지는 자유롭게 첫 번째 패스의 데이터 값을 선택할 수 있습니다. 그리고 메모리 칩이 누락 될 가능성이 있기 때문에 주소가 바뀌는 (그러나 같지는 않은) 데이터 세트를 선택하는 것이 가장 좋습니다. 간단한 예제는 “증분 테스트”입니다.

Offset
Value
Inverted Value
00h
00000001
11111110
01h
00000010
11111101
02h
00000011
11111100
03h
00000100
11111011
FEh
11111111
00000000
FFh
00000000
11111111

표 2. 증가 테스트의 데이터 값

증분 테스트의 오프셋 및 해당 데이터 값은 표 2의 첫 번째 두 번째 열에 표시됩니다. 세 번째 열은이 테스트의 두 번째 단계에서 사용 된 반전 된 데이터 값을 보여줍니다. 마지막 열은 감소 테스트를 나타냅니다. 가능한 다른 많은 데이터 선택이 있지만 증가하는 데이터 패턴은 적절하고 계산하기 쉽습니다.

/**********************************************************************
 *
 * Function:    memTestDevice()
 *
 * Description: Test the integrity of a physical memory device by
 *              performing an increment/decrement test over the
 *              entire region.  In the process every storage bit 
 *              in the device is tested as a zero and a one.  The
 *              base address and the size of the region are
 *              selected by the caller.
 *
 * Notes:       
 *
 * Returns:     NULL if the test succeeds.
 *
 *              A non-zero result is the first address at which an
 *              incorrect value was read back.  By examining the
 *              contents of memory, it may be possible to gather
 *              additional information about the problem.
 *
 **********************************************************************/
datum * 
memTestDevice(volatile datum * baseAddress, unsigned long nBytes) 
{
    unsigned long offset;
    unsigned long nWords = nBytes / sizeof(datum);

    datum pattern;
    datum antipattern;




    /*
     * Fill memory with a known pattern.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        baseAddress[offset] = pattern;
    }

    /*
     * Check each location and invert it for the second pass.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        if (baseAddress[offset] != pattern)
        {
            return ((datum *) &baseAddress[offset]);
        }

        antipattern = ~pattern;
        baseAddress[offset] = antipattern;
    }

    /*
     * Check each location for the inverted pattern and zero it.
     */
    for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++)
    {
        antipattern = ~pattern;
        if (baseAddress[offset] != antipattern)
        {
            return ((datum *) &baseAddress[offset]);
        }
    }

    return (NULL);

}   /* memTestDevice() */

Listing 3. 디바이스 테스트

Listing 3의 memTestDevice () 함수는 이러한 2 회 증가 / 감소 테스트를 구현한다. 호출자로부터 두 개의 매개 변수를받습니다. 첫 번째 매개 변수는 시작 주소이고 두 번째 매개 변수는 테스트 할 바이트 수입니다. 이 매개 변수를 사용하면 덮어 쓸 메모리 영역을 최대한 제어 할 수 있습니다. 이 함수는 성공시 NULL을 반환합니다. 그렇지 않으면 잘못된 데이터 값을 포함하는 첫 번째 주소가 반환됩니다.

전부 모아보면..

토론을 보다 구체적으로 하기 위해 실제적인 예를 생각해 봅시다. 주소 00000000h에 SRAM의 64K chunk를 테스트 한다고 가정합니다. 이를 위해 Listing 3과 같이 세 가지 테스트 루틴을 적절한 순서로 호출한다. 각각의 경우 첫 번째 매개 변수는 메모리 블록의 기본 주소이다. 데이터 버스의 너비가 8 비트보다 큰 경우 몇 가지 수정이 필요합니다.

int
memTest(void)
{
#define BASE_ADDRESS  (volatile datum *) 0x00000000
#define NUM_BYTES     (64 * 1024)

    if ((memTestDataBus(BASE_ADDRESS) != 0) ||
        (memTestAddressBus(BASE_ADDRESS, NUM_BYTES) != NULL) ||
        (memTestDevice(BASE_ADDRESS, NUM_BYTES) != NULL))
    {
        return (-1);
    }
    else
    {
        return (0);
    }
 
}   /* memTest() */

Listing 4. 메모리 테스트 엔진

개별 메모리 테스트 루틴 중 하나가 0이 아닌 (또는 NULL이 아닌) 값을 리턴하는 경우, 빨간색 LED를 켜서 오류를 시각적으로 표시 할 수 있습니다. 그렇지 않으면 세 가지 테스트가 모두 성공적으로 완료되면 녹색 LED가 켜질 수 있습니다. 오류가 발생하면 실패한 테스트 루틴은 발생한 문제에 대한 정보를 반환합니다. 이 정보는 하드웨어 설계자 또는 기술자와 문제의 본질에 대해 의사 소통 할 때 유용 할 수 있습니다. 그러나 디버거 또는 에뮬레이터에서 테스트 프로그램을 실행하는 경우에만 표시됩니다.

대부분의 경우 전체 스위트를 다운로드하고 실행하게 됩니다. 그런 다음 메모리 문제가있는 경우에만 디버거를 사용하여 프로그램을 단계별로 실행하고 메모리 장치의 개별 기능 반환 코드와 내용을 검사하여 어떤 테스트가 실패했는지, 왜 실패했는지 확인해야합니다.

불행히도 고급 언어로 메모리 테스트를 작성할 수 있는 것은 아닙니다. 예를 들어 C 언어에서는 스택을 사용해야 합니다. 하지만 스택 자체에는 작업 메모리가 필요합니다. 이것은 하나 이상의 메모리 장치가 있는 시스템에서 합리적 일 수 있습니다. 예를 들어 다른 메모리 장치를 테스트하는 동안 이미 작동 중인 RAM 영역에 스택을 만들 수 있습니다. 이와 같은 상황에서는, 작은 SRAM이 조립 된 상태에서 시험 될 수 있으며 그 후에 스택이 그 곳에 생성 될 수 있습니다. 그런 다음 더 나은 테스트 세트를 사용하여 더 큰 DRAM 블록을 테스트 할 수 있습니다. 테스트 프로그램의 스택 및 데이터 요구에 대해 충분한 RAM을 사용할 수 없다면 이 메모리 테스트 루틴을 어셈블리 언어로 완전히 다시 작성해야합니다. 또 다른 옵션은 인 서킷 에뮬레이터에서 메모리 테스트 프로그램을 실행하는 것입니다. 이 경우 에뮬레이터 자체의 내부 메모리 영역에 스택을 배치하도록 선택할 수 있습니다. 타겟 메모리 맵에서 에뮬레이터의 내부 메모리를 이동하면 타겟에서 각 메모리 장치를 체계적으로 테스트 할 수 있습니다.

메모리 테스트의 필요성은 하드웨어의 안정성과 설계가 아직 입증되지 않은 제품 개발 중에 가장 명백한 부분입니다. 그러나 메모리는 임베디드 시스템에서 가장 중요한 리소스 중 하나이므로 소프트웨어의 최종 릴리스에 메모리 테스트를 포함하는 것이 바람직 할 수 있습니다. 이 경우 메모리 테스트 및 기타 하드웨어 신뢰성 테스트는 시스템의 전원을 켜거나 재설정 할 때마다 실행해야합니다. 함께, 이 초기 테스트 슈트는 일련의 하드웨어 진단을 구성합니다. 하나 이상의 진단이 실패하면 수리 기술자를 불러서 문제를 진단하고 결함이있는 하드웨어를 수리 또는 교체하십시오.

무료 소스 코드

이 메모리 테스트를위한 C 소스 코드는 공개 된 것으로 http://www.barrgroup.com/code/memtest.zip에서 전자 형식으로 구할 수 있습니다. 8051 및 Phillips XA 프로세서에 대한 포트는 http://www.esacademy.com/faq/progs/ram.htm에서 찾을 수 있습니다.

SW역량에 부쳐

“문제해결 능력으로만 보면 삼성 인력의 1~2%만 구글에 입사할 수 있는 수준이다.”

클리앙에서 요새 가장 핫하게 떠오른 이슈였다. SW개발자로서 구글 입사는 꿈과 같은 일로, 누구에게나 주어지는 일도 아니지만 누구나 할 수 있는 일은 아니다. 그 관문을 통과하려면 SW의 기초부터 가장 난해한 알고리즘까지 완전히 이해하여야 가능하다.

나 같이 대기업을 다니고 있거나, 일반 회사를 다니고 있는 SW개발자들은 그저 그들이 가진 수학적 능력의 탁월함을 감탄하는 선에서 그친다. 왜냐하면 10년 넘게 일반 대기업을 다니면서 SW를 수행하다 보니 저러한 능력을 필요로 하는 일은 대게 주어지지 않았기 때문이다. 우리 회사가 다루고 있는 일이 하이테크 임에도 불구하고 . 그래서 우리가 하는 일이 실패하였는가?에 대해서는 다시 한번 생각해 볼 필요가 있다.

당연한 이야기지만 우리가 개발하고 있는 방법들은 우리가 하고 있는 일과 많이 잇닿아 있다. SW의 계층적 구조가 어플리케이션, 미들웨어, OS, 드라이버등, 다양한 역할을 가진 계층이 모여서 하나의 SW제품을 이루고 있듯이 우리가 적재적소에 배치되어 개발하고 있는 것들은 하나의 방법으로 하나의 기술적인 역량으로 만들어 지는것은 아니다. 어플리케이션의 HMI개발자들은 좀 더 창의적이고 혁신적인 UI를 고민할 필요가 있으며, 가장 아랫단의 드라이버 개발자들은 HW 구조를 이해해야 할 필요가 있다. 또한 중간의 미들웨어나 OS개발자들은 수많은 TASK를 어떻게 할당하고 운용해야 할 지 알고리즘을 고민해야 할 수도 있을 것이다.

이러한 것들은 비단 개인을 넘어서 조직 문화에도 은연중에 영향을 미치고 있다. 우리회사의 UX팀의 파티션에는 나뭇잎 모양의 그늘막이 설치되어 있는데 창의적인 업무를 수행하는 팀 분위기와 어느 정도 연관성이 있지 않을까. 반대로 모델 개발팀은….(후략).

이는 산업의 특성과도 맞닿아 있다. 웹서비스를 개발하는 회사에는 왜 SW프로세스에 대해 보수적으로 접근하지 않을까? 자동차 업계에서는 SW를 체계적으로 개발하기 위해 CMMI와 ASPICE를 도입하고 Supplier에게 준수할 것을 강제하고 있다.  항공기 시스템을 개발하기 위해서는 DO-178B를 왜 준수하여야 할까.  웹서비스를 개발하는 회사에서는 좀 더 창의적인 서비스에 집중할 필요가 있고, 자동차SW를 개발하는 회사에서는 신뢰성 있는 SW를 개발해야 하고 이를 놓치지 않게 프로세스를 강제할 필요가 있다. 몇몇의 자동차 OEM에서는 프로세스를 강제하기 위해 ALM과 PLM을 도입하여 운용하기도 한다.

당장 우리의 동료가 구글에 입사할 능력이 안되니까 회사가 굴러가지 않을꺼라는 말도 안되는 이야기는 하지 않았으면 좋겠다. 또한 우리가 그만한 능력이 안된다고 해서 풀이 죽을 필요도 없다. 개발하고자 하는 것들을 잘 이해하고 업무를 극대화 하는 것. 그리고 더 나아가 통찰력을 얻을 수 있다면 우리는 충분히 해야할 일을 하고 있는 것이라고 생각한다.

SW역량의 극대화는 SW의 특성을 이해하고 적재 적소로 인재를 운용하는 것. 만들고자 하는 것과 그것을 만들어 내는 방법은 잇닿아 있다.

 

ISO26262 – Part 3.

부서에서 진행하고 있는 기능안전 활동. 제대로 된 교육은 아직 못들었고…계속 어디서 주워 듣는데 주워 들은 내용들을 정리하고 궁금한 것도 기록해 본다. 26262는 11년에 나왔지만, 아마 내년이 원년이 될 것 같다. 전장에 전자 부품 비율이 폭발적으로 높아지다 보니 자동차 업체들도 이제 슬슬 챙기기 시작한다. 뭐 아직은 ‘아몰라 해왕…’수준이지만..ㅋㅋㅋ 역량을 잘 쌓아두면 최소 5년은 먹고 살 수 있겠다. 그래봐야 테크니컬한 부분보다는 챙기고 문서질하는 일이 대부분이겠지만…

  1. Part 3 – 아이템 정의
    • 갑의 정의 한다. 아이템을 선정하고 바운더리를 정하는 것은 매우 중요함. Supplier 입장에서는 차량 업체가 적절한 아이템을 선정해 주기를 바랄뿐.
    • ISO26262의 Part 1에 보면 아이템, 시스템, 엘리먼트, 컴포넌트, 유닛이 잘 정의 되어있다. 그럼에도 불구하고 어느 레벨까지 시스템 바운더리로 정해야 할 것인지는 아직 모호하다.
    • 이유인 즉슨…
      • Service 측면에서는 차량, 서비스, 네트워크까지를 전체 시스템으로 볼 수 있겠으며…
      • OEM 측면에서는 차량을 시스템으로 간주할 수 있다.
      • Supplier 측면에서는 제공하는 유닛을 시스템으로 이해할 수 있다.
    • Automotive 영역에서는 차량까지를 시스템으로 고려하고, 실제로 Supplier는 개발을 담당하는 영역을 시스템으로 한정한다.
    • 하위 내용의 이해를 돕기 위해 예제를 다음과 같이 가정한다.(예제는 특정 도서를 참조하였다)
      • 차량의 각 컴포넌트(조향장치, 브레이크)는 ECU를 기반으로 제어할 수 있다.
      • 각각의 컴포넌트는 WLAN통신을 수행한다
      • 기능 안전 활동을 수행할 아이템은 WiFi-Com System(WLAN System)이다.
  2. Part 3 – HARA
    • OEM이 수행하는 활동으로 아이템의 위험 상황을 정의 하고 영향도를 분석하여 ASIL을 추정하고 SG(Safety Goal)를 정의 하는 활동이다.
    • 명확한 SG와 ASIL을 전달해주면 매우 감사하겠으나, 경험적으로 OEM은 아이템, ASIL Level 만 전달해 준다. 따라서 이 내용 또한 수행해 보지 않으면 이후 TSR(Tech. Safety Req.)를 도출하는데에 매우 애를 먹을 수 있다.
    • 길게 설명해 봐야 이해가 안갈 수 있으니 예제를 바로 도입한다.
      • 1. Communication Breakdown
        • Operation Situation : high speed
        • Impact : no communication possible between ECUs
        • Example : breaking request is not transmitted
        • Severity : S3(Life-threatening injuries(survival uncertain), fatal injuries)
        • Exposure : E4(High probability)
        • Controllability : C3(Difficult to control or uncontrollable)
        • This result in an ASIL D
        • To mitigate the risk deriving from this hazard, we have formulated safety goal
        • SG.a : The communication links for breaking, steering, and engine control shall be maintained at all times.
      • 2. Falsified signals transmitted
        • Operation Situation : high speed
        • Impact : wrong signals are transmitted to an ECUs
        • Example : wrong request for breaking transmitted
        • Severity : S3(Life-threatening injuries(survival uncertain), fatal injuries)
        • Exposure : E4(High probability)
        • Controllability : C3(Difficult to control or uncontrollable)
        • This result in an ASIL D
        • To mitigate the risk deriving from this hazard, we have formulated safety goal
        • SG.b : Erroneous reactionss of the control units for breaks, steering, and engine control resulting from corrupted transmission shall be prevented at all times
    • 정리 하자면 다음과 같은 내용이 도출되어야 한다.
      • 위험 상황
        • 기능 오류는 일반적으로…다음과 같은 범주 내에 있다. 몇 개 더 추가되어야 할 것으로 보이지만…
          • 동작 안함
          • 지연
          • 오동작(통신의 경우 위변조)
      • 위험이 발생할 수 있는 동작 상황, 영향, 심각도, 노출도, 제어 가능 정도
      • 심각도, 노출도, 제어가능도를 통해 ASIL 산출
      • 기능 오류를 극복하기 위한 Safety Goal. 마찬가지로 대부분 아래와 같은 유형에서 크게 벗어나지 않는다.
        • 기능 오류를 막는다
        • 정상적인 상태를 유지한다
      • 대게 26262를 처음 접하는 사람들의 도메인들이 다양하므로 위와 같은 SG를 보고 기가 찰 수도있다. 또는 복구 메커니즘 등을 SG라고 우기는 경우도 간혹 있겠고… 하지만 예제와 같이 아주 추상적 레벨을 가리킨다. 비지니스 레벨의 요구사항.
  3. Part3 – FSC(Functional Safety Concept)
    • 이 단계 역시 OEM이 수행하는 활동으로 SG를 바탕으로 FSR(Functional Safety Req.)을 도출하는 활동이다.
    • 약자가 매우 많아 짜증날 수 있겠지만 자주보면 매우 익숙해진다.
    • OEM이 FSR을 매우 잘 도출해 준다면 감사하도록 한다. Volvo의 경우 TSR 수준까지 전달해 주기도 한다.
    • 마찬가지로 예제부터 간다.
      • FSR.a : A redundant emergency WLAN must be available
      • FSR.b : The WiFi-Com System shall detect WLAN failures.
      • FSR.c : In case of WLAN failure, a switch to an emergency WLAN system is made
      • FSR.d : In case of a switch to the emergency WLAN system, the WiFi-Com System shall inform the driver about the situation
    • 유의할 점은 다음과 같다.
      • FSR 도출은 시스템 레벨 시스템 보다 상위 레벨에서 SG를 어떻게 충족시킬수 있을지 고민한다
      • 당연히 추적성 반영(SG-FSR). 추적성은 인과관계의 증거가 된다.
      • 디테일 할 필요 없다. 그건 추후 Supplier가 시스템 레벨에서 고려한다. 이는 TSC(Tech. Safety Concept)에서 수행하게 된다.
    • 기능 오류를 막거나, 정상적인 상태를 유지하려면 사실 아래와 같은 몇 가지 유형을 크게 벗어나지 않는다
      • 시스템을 중복으로 구성한다
      • 시스템을 감시한다
      • 기능오류를 탐지(감시와 동일)하여 복구한다. 또는 리셋한다.
      • 시스템에 오류가 있거나 변동사항이 있는 경우 운전자에게 알린다

갑님 까지의 항목은 정리 되었고, OEM이 수행하여야 하는 활동은 다음에 정리하는걸로.. To be continue

  1. 다음에 정리 할 내용
    1. TSC : FSR + 시스템 레벨의 컨셉 아키텍처 -> TSR(여기에선 HW, SW할당까지만 이루어진다) + 안전 요구사항이 반영된 시스템 레벨 아키텍처
    2. 정의된 디자인, 구현된 시스템 -> 위험 요소 분석(SW FMEA) -> 안전 요구사항 작성 -> 안전 요구사항을 반영하여 디자인, 구현의 iteration
    3. SW FMEA
      1. 이거 왜함? -> 현재의 SW 디자인이 SG를 만족하는지 확인하기 위함. 만족한다면 evidence(현재 적용된 Safety 매커니즘(detection, prevent, avoid등…)이 SG에 도달함)가 있어야 하고 만족하지 않는다면 추가로 SSR이 도출되고 디자인에 반영되어야 함.
      2. http://www.autoelectronics.co.kr/article/articleView.asp?idx=1666
    4. SW 테스트
      1. 정적 분석
      2. 단위/통합 테스트 : 원 시스템의 단위/통합테스트 항목 + 안전 요구사항으로 부터 구현된 유닛의 단위/통합테스트 항목
  2. 그 외 아직 정리가 안되는 내용
    • 혹자는 SG 이외에 Safety State를 정의 하여야 한다는데…위에 언급한 것은 항상 시스템이 정상이거나 복구하는 상황(state) 뿐이다. 시스템이 완전히 맛이 간 경우에는 어떤 Goal과 FSR이 도출 되어야 하는가?
    • 인증을 받기 위한 단위/통합 테스트 항목은 safety항목과 기존 항목을 모두 포함하는가? 당연히 요구하겠지만…
    • HW 유닛의 경우 각 유닛이 하는 일이 명확한 반면, SW는 요구하는 기능이 컴포넌트 별로 부분부분 구현되는 일이 빈번하다. 이건 뭐 SW역량에 따른 문제고(우리도 구글같은 애들 데려다 놓으면 잘 짜겠지만).어쨌든 이와 같이 Supplier가 ISO26262를 요구하는 항목이 컴포넌트 별로 나뉘어 구현되는 경우에는 어떻게 정리 해야할 까?

Practical UML Statecharts

내년에 Model 기반의 개발을 진행할 수도 있어 모델링과 State machine쪽 스터디를 진행하고 있다. 요 며칠 과음으로 골골하다가 정말 어디 안나가고 집에 있으려고 뒹굴뒹굴 하면서 이것저것 찾아보다가 마침 괜찮은 내용이 있어 가져와 봤다.

practical_uml_statecharts.pdf에 액세스하려면 클릭하세요.

사실 이 슬라이드는 Practical UML Statechart in C/C++를 쓴 저자가 어디 외부 강연나가서 책의 내용 중 큰 줄기를 요약한 내용으로 보인다.

• Event-driven programming

기존의 순차적 프로그래밍은 GUI 기반의 시스템과 같이 인터렉티브한 시스템을 구현하는데에 필연적으로 IF,ELSE, SWITCH등의 스파게티 코드가 발생하게 된다. 또한 이벤트를 순차적으로 처리하도록 구현한 경우에는 작업을 대기하는 등의 낭비도 생기게 된다.

반면 이벤트 기반의 프로그래밍은 이벤트에 큐가 쌓이고, 이벤트 디스패쳐가 각각의 핸들러에게 이벤트를 전달하고 처리하게 함으로서 이벤트 각각의 처리를 독립적으로 분리시킬 수 있다.

그럼에도 불구하고 이벤트 기반의 프로그래밍은, 이벤트 처리 로직에 전역변수나 조건에 대한 Flag를 고려해야하는 등, 또 다른 스파게티 코드를 양산할 소지가 있다.

• Hierarchical state machines

CPU 사용에 있어서 Statechart와 Flowchart는 완전히 다르게 구현된다. Statechart에서는 상태 천이시의 Action에만 CPU를 사용하지만 Flowchart 에서는 로직이 돌아가는 경우에 CPU를 항상 사용하고 있다.

문제는 로직이 복잡한 경우, 상태 천이가 매우 많이 발생하고 이러한 것들이 설계나 구현의 복잡성을 높인다. 하지만, 이러한 상태 천이가 발생하는 조건이나 목적이 각 상태별로 유사하므로 이러한 상태 천이의 행위를 상속의 개념으로 추상화 할 수 있다. Slide 14->15

State nesting을 통해서 상위 State에 하위 State를 구현하여 하위 State의 공통되는 상태 천이는 상위 State에 구현하도록 한다.

• Real-time frameworks

Each state machine executes in its own thread of control
–> (State Machine + Event Queue + Thread) = Active Object

IVI(In Vehicle Infotainment) 동향 – 제품군

간략하게 요새는 어떤 제품군들이 있고 어떤 플랫폼들이 사용되고 있는지 정리해 본다.

제품군

자사 제품군에 한정짓지 않더라도, IVI는 크게 다음과 같은 제품군으로 나눌 수 있다.

1. Head unit.

– 가장 쉽게 자동차에서 찾을 수 있는 라디오 쪽의 유닛을 일컫는다. 이 유닛의 기능은 기본적으로 오디오, 라디오의 역할을 수행하지만 요즘에는 적게는 라디오 부터 크게는 차량 전체의 샤시 제어까지 기능이 확장 되었다. 후자의 경우는 CID(Central Information Display)라고도 불리기도 한다.

– 다음과 같은 기능을 수행한다

  • audio
  • radio
  • video
  • navigation
  • Telematics(별도의 제품군으로 구분함)
  • 윈도우, 공조기등 사용자 위주의 샤시 제어

2. Cluster

– 계기판을 클러스터라 부른다. 여전히 기계식 계기판이 주류이지만, 요즘에는 계기판 대신에 LCD화면을 채용한다.

– 주로 하이엔드 급 모델에 전자식 계기판이 점차 채용되는 추세. 당연한 이야기지만, 계기판에서 조작할 수 있는 것은 트립 컴퓨터 정도이고 일반적으로 차량에서 정보를 받아 사용자에게 디스플레이 하는 역할을 한다.

– 요새는 네비게이션과 연동하여 HID, 클러스터에도 동시에 트랙을 표시하기도 한다.

3. Telematics

–  사실 헤드유닛에 셀룰러 모뎀(3G, LTE)이 달려 있으면 텔레매틱스라 불러도 무방하다. 별도의 제품군으로 나눠야 할 필요가 있을지 모르겠지만, 셀룰러 기능이 추가 됨으로서 V2X로서의 중요한 기능을 수행하기도 한다. 텔레매틱스는 별도의 단말로 개발 되기도 하고 헤드 유닛에 통합되기도 한다.

– 모뎀이 달리게 되면서 확장될 수 있는 기능은 다음과 같다.

  • 차량 상태 모니터링.
  • 도난 차량 추적
  • 응급 상황시 구조 요청
  • 최적화 경로 라우팅(online navigation)
  • POI 제공

– 아직은 모뎀을 활용하여 충분한 기능을 제공하고 있는 제품은 시장에 없는 것으로 보여진다. 이는 Supplier 또는 OEM의 특정 서비스를 제공하기 위한 용도로서 활용되고 있기 때문이다.

4. CID

– 마찬가지로 제품 군에 별도로 두어야 할 필요가 있는지…텔레매틱스의 기능과 헤드유닛의 모든 기능을 통합한다. Central Information Display.

– 별도의 기능 설명은 생략한다. 위에 언급된 헤드유닛과 텔레매틱스 기능을 모두 통합할 수도 있고, 헤드유닛의 기능만 수행할 수도 있다. 테슬라에 탑재된 메인 디스플레이가 대표적인 제품.

다시금 깨닫는 진리 – high cohesion, low coupling.

적지않은 기간 동안 SW를 개발함에 있어서 개발 전에 설계를 진행하거나 기존의 SW를 유지보수하기 전에 설계 구조를 리뷰한다는 이야기를 자주 듣지 못했던 것은 아쉬운 일이다. 처음에는 나 역시도 소스가 모든 것을 말해준다고 생각했기 때문에 필요한가 싶기도 했고.

객체지향적 설계에 있어서 대부분의 클래스들이 유사한 역할과 기능을 한다고 생각하면 오산이다. 클래스라는 개념이 모두 동일하다고 해서 클래스의 특성을 클래스 자체라고 생각하는 경우도 있다. 큰 틀에 있어서는 당연히 클래스라는 것은 어떠한 행위와 속성이 결합된 추상체라고 정의 할 수 있겠지만 같은 클래스 형식이라고 해도 실제로 어떠한 역할을 하는지에 따라 성격은 판이하게 달라질 수 있다.

예를 들어보자. 안드로이드에서 액티비티클래스가 있고 연락처의 기본 단위인 컨택트 클래스가 있다고 가정할 때 이 둘의 성격은 동일 할까? 액티비티 클래스의 경우 유저와 인터랙션이 이루어지므로 많은 로직들이 구현될 수밖에 없다. 이것은 요구사항에 따른 것이므로 필연적 이기도 하다. 반면에 컨택트 클래스의 경우 연락처를 추상화 한 클래스로 간결하기도 하고 연락처의 기능을 잘 집약하여 응집도 높게 설계되어야 한다. 이는 자동차로 비유를 한다면 조향기와 조향기를 구성하는 나사나 스프링에 대한 차이로 비유할 수 있다.

누구나 알 수 있는 이야기를 왜 이리 설명하는지 이해가 잘 안 갈수도 있겠지만 구현에 있어서 이러한 클래스의 차이, 아키텍처간 계층, 추상화 레벨 등에 대한 것들을 고려하여야 함에도 실상 대부분 간과하기 때문이다. 그저 기능구현에 충실하여 클래스에 기능들을 덕지덕지 붙이는 일이 물론 업무에 충실함에 있어서는 나무랄데가 없겠지만 생산성과는 거리가 있기 때문이다.

그렇다면 계층,추상화를 고려할 때 복잡도가 높은 영역, 쉽게 예를 들면 액티비티 같은 것들은 복잡하게 설계 될 수 밖에 없는 것인가? 요구사항이 그렇다고 한다면 별 수 없겠지만 그래도 최대한 간결하고 단순하게 구현하는 것이 완성도가 높을 수 밖에 없다. 간결한 테스트 케이스의 도출이 가능하기 때문에 검증도 쉽기 때문이다. 그러므로 이와 같이 클래스의 성격이 다르더라도 High cohesion Low coupling의 진리는 모든 객체에 평등하다.

따라서 만듦새 있는 SW를 만들고 싶으면 기획단계에서 사용자의 요구사항을 최대한 집약하여 응집성 있게 도출하는 것이 사실 설계보다, 개발보다 선행 되었으면 하는게 바램이다. 잘 짜여진 디자인이 보는 사람들로 하여금 직관적으로 판단할 수 있게 하는 것 처럼.

TDD는 죽었다. 테스트여 영원하라.

번역에 앞서 알아두면 좋은 것들.

※TDD, Agile쪽 소양이 부족해 test-first를 테스트 우선 대신 테스트 선행 개발로 의역하였음. 그 외에 다수 의역. 죄송합니다. 성문기초영어로는 이 정도로 밖에 해석이 안됩니다. 해석이 안되는 부분은 회색 처리 합니다.

http://david.heinemeierhansson.com/2014/tdd-is-dead-long-live-testing.html 의 원문을 번역함

테스트가 선행되어야 한다는 근본주의는 순결을 강조하는 성교육과 유사하다: 자기 혐오에서 비롯된 비현실적이고, 효과없는 도덕적인 캠패인과 같은

이것은 처음에 이렇게 시작하지는 않았다. 내가 처음 TDD를 찾아 보았을 때, 그것은 마치 소프트웨어를 작성함에 있어서 좀 더 나은 세상으로 인도하는 친절한 초대 처럼 여겨졌다. 예전에는 실행하지 않았던 (영역에 대한) 테스트를 실행하도록 마음가짐을 바꾸도록 했다. 잘 테스트 된 코드에 대한 안정감과 소프트웨어에 (긍정적인) 변화를 주는 확신에 대한 축복에 눈을 뜨게 했다.

테스트를 선행하는 부분은 좀 더 깊은 레벨에서 테스트하는 것에 대해 어떻게 생각해야하는지를 가르쳐 주는 훌륭한 보조바퀴 역할을 했을 뿐만 아니라 but also some I quickly left behind 몇개는 빠르게 남겨 두었다.(?)

몇 년이 지나 테스트를 선행해야 한다는 목소리가 좀 더 커지고 강해졌다. 그럼에도 불구하고 좀 더 많은 의미를 내포하게 되었다. 때때로 (테스트 선행)근본주의의 소용돌이에 빠져 진리를 따르지 않는 것(테스트 선행 개발을 따르지 않는 것)에 대한 불편함을 느꼈다. 그리고 나서 몇 주 동안 테스트 선행 개발을 진행하였고, 그것들이 내 디자인을 망치기 시작하고 있을 때 중단할 수 밖에 없었다.

이것은 가르침에 대한 것들을 적용할 수 있을 때는 자부심을 느끼도록, 그럴 수 없을때는 절망을 느끼도록 하는 요요 사이클과 같았다. 몇 개는 그냥 조용히 유지해야 하고, 확실히 몇 개는 공개적으로 허용되어서는 안된다. 공개적으로는 항상 테스트를 선행하여 개발하는 것은 아니라고 언급하는 것이 내가 하는 최선이고, 올바른 방법 인양 그저 (테스트 선행 개발에 대한) 관습을 지원하고 있었던 것은 최악이다. 나는 지금 이렇게 했던 일에 대해 후회하고 있다.

아마도 산업 전반에 자동화 회귀 테스트의 결여에 대한 유감을 깨버리기 위해 사용되는 직관에 반하는 도구로서 테스트 선행 개발이 필요했던 것은 아닌가 싶다. Maybe it was a parable that just wasn’t intended to be a literal description of the day-to-day workings of software writing. 아마도 의도 하지는 않았지만 소프트웨어를 작성하는 하루하루 진행되는 업무를 있는 그대로 묘사하기 위해 테스트 선행 개발을 비유할 수도 있다(?) 그러나 어떤 의도에서 비롯되었든, 이것은 곧 변질 되었다. 리트머스 테스트 처럼 믿지 않는 사람들을 패배시키고 소프트웨어를 작성함에 있어서 그들을 비전문적이고 맞지 않는 사람으로 매도 하기 위한 망치로서 사용 되었다. 

충분하다. 더 이상 (언급 할)필요는 없다. 내 이름은 데이비드이고 더 이상 테스트 선행으로 개발을 진행하지 않는다. 이 것(이 언급)에 대해 사과하지 않으며, 숨기는 것도 없다. TDD가 자동화 회귀 테스트에 대한 내 눈을 뜨게 해준 것에 대해 감사하게 생각하지만 but I’ve long since moved on from the design dogma.디자인 도그마로부터 옮겨오기 전 부터 진행해 왔다.(?) 

나는 여러분에게 시스템 디자인의 무결성을 잘 (유지) 하기 위해 테스트 선행 개발 접근이 무슨 역할을 하는지 잘 살펴 보기를 제안한다. 만일 그것이 좋지 않은 것이라는 가능성에 대해 솔직하게 고려 해 볼수 있다면 그것은 붉은약을 먹는 것과 같을 것이다.(역주, 매트릭스 – 네오가 먹은 붉은약. 환상에서 깨는 것). 그 이후에 보는 것은 전과 같지 않을 것이다.

그래서 여기서 어디로 옮겨가야 하는가?

첫 번째 단계는 문제가 있다고 인정하는 것이다. 내 생각에는 우리는 지금 그 것을 알고 있다. 두 번째 단계는 테스팅 스팩트럼을 유닛에서 시스템으로 옮겨오도록 재 분배를 해야한다. 현재 광신적인 TDD 경험은 주된 관심을 유닛 테스트로 이끌고 있다. 왜냐하면 이 테스트들이 코드 디자인을 고려 할 수(capable of driving) 있는 것들이기 때문이다.(이것이 원래 테스트 선행 개발의 정당성을 갖도록 하는 것임)

나는 이것이 건강하다고(잘 짜여진 디자인) 생각하지 않는다. 테스트 선행 유닛은 ‘느린’ 어떤 것들을 하는 것을 피하기 위한 매개 객체들과 간접 참조로 웹을 복잡하게 한다. 마치 과열된 데이터 베이스 또는 파일 I/O 또는 going through the browser to test the whole system. 전체 시스템을 테스트 하기 위한 브라우저를 겪는 것 처럼(?) . 이것은 괴이한 아키텍처를 낳는 결과를 가져다 준다. 서비스 객체가 빡빡하게 존재한다던지, 커맨드 패턴이라던지, 그 보다 더 나쁜 것들도.

나는 모든 의존관계에 있어서 mock이 수립된 경우, 그리고 수 초 내에 수 천개의 테스트가 종료 될 수 있는 경우에 드물게 전통적인 방법으로 유닛 테스트를 수행한다. 이것은 단지 레일즈 응용프로그램의 테스팅을 다루는 유용한 방법은 아니다. 나는 직접 또는 그것들을 데이터베이스에 넣어보는 방법으로 또는 기구(구조? 기법)를 사용하여 액티브 레코드 모델을 테스트 한다.  그리고 나서 상위 계층인 컨트롤러 테스트의 집합들은 Capybara나 유사한 것들을 통해 좀 더 하이 레벨의 시스템 테스트로 대채하는 것이 낫다고 생각한다.

내 생각에는 이것이 우리가 취해야 할 방향이라고 생각한다. 더 이상 디자인 관습으로서의 테스트 선행 개발을 하지 않기 때문에 유닛 테스트를 덜 강조하고, 시스템 테스트에 더 중점을 두어야 한다. (병렬화와 클라우드 인프라의 발전으로 인해 시스템 테스트는 느리지 않다)

레일즈는 이러한 전이를 도울 수 있다. 오늘날 전체 시스템 테스트에 직면할때 우리는 아무것도 하지 않는다. 계층에 있어 정답은 존재하지 않는다. 이것이 우리가 고쳐햐 할 실수라고 생각한다. 그러나 그것들이 일어나기 전까지 기다릴 필요는 없다. 오늘날 Capybara가 주어졌고 우리가 취해야할 방향에 대해 좋은 의견을 얻게 될 것이다.

그러나 먼저 심호흡을 가다듬어야 한다. 우리는 당장 도축을 기다리는 소들과 같다. 그것은 비참하고 참혹하다. TDD는 많은 개발자들의 아이덴티티와 엮여지면서 성공적으로 진행되어 왔다. TDD는 그들이 무엇을 하고 있는지 뿐 만 아니라 그들이 누구인지를 이야기 해 준다. We have some serious deprogramming ahead of us as a community to get out from under that, and it’s going to take sometime. 기저에 있는 그런 것들을 끄집어 내기 위해 모임으로서의 선두에 있던 사람들 몇몇은 빠져나오고 있고 당분간은 이러한 일들이 계속 진행될 것이다.(?)

최악의 상황은 다른 테스팅 교조주의에 빠질 수 있다는 것이다. “오직 시스템 테스트”라는 금 송아지를 떠올려 볼 수 있다. 제발 그 곳에 가지 마라.

그렇다. 나에게 테스트 선행 개발은 죽었다. 그러나 무덤에서 춤을 추기 보다는, 그것들을 우스꽝스럽다고 이야기 하기 보다는 그것들이 기여한 것들에 대해 경의를 표하는 것이 낫다. 그것은 우리의 역사에서 중요한 방점을 찍었고, 아직 진행되고 있는 중이다.

테스트여 영원하라.

—————————————————————–

+ 다 읽고나서…

아직도 SW 필드 중 아주 일부분만 경험했음을 감안할 때 David가 이야기 한 것처럼 어떤 부분은 unit test로, 어떤 부분은 regression test로 진행될 필요가 있다고 본다. 특히나 대규모의 시스템의 경우 각각의 unit test를 수립하기 어려우므로 일부는 regresstion으로 일부는 unit으로 진행이 가능하다고 보고, 현재 내가 개발하는 영역에 대해서는 하위의 model 레벨의 객체에 대해서는 unit을 유저 시나리오를 다루는 상위 레벨의 객체에 대해서는 regression이 적합하다고 본다.  특히 유저 시나리오를 다루는 상위 레벨의 경우는 때에 따라서는 객체가 커질 수도 있고, 이러한 큰 객체에 대해서는 완벽한 unit test를 수립할 수 있을지가 의문. 물론 unit test전에 최대한 작은 모듈로 쪼갤수 있을 정도의 충분한 리팩토링이 수행된다면 unit test를 수행할 수 있는 해피한 case가 도출되겠지만, 시스템의 특성상, 구현 환경상, 아키텍처 구조 상 항상 unit test가 가능한 완벽한 형태의 객체를 또는 unit을 도출할 수는 없다고 본다.

+ 토론

40분쯤에 갑자기 꺼지길래 헐…싶었더니 원래 30분짜리. 30분을 가지고 이야기 하기에는 너무 짧았다.

영어…귀머거리인 탓에 뭔 이야기를 하는지 제대로 건지지도 못함.

David 존잘.

+ 흥미로운 질문들

Where is defined that “Test First” means Test Units first? What about “Test Behaviour first” (Acceptance Test first)?

If one was to disregard the mantra ‘Write tests first”, WHEN would be a good time to write tests?

I’d like to hear from David, Martin, and Kent where their experience with TDD (positive or negative) comes from. What kinds of projects (domain, size, schedule, client vs. in-house, etc.) have you used TDD on, that have lead you to your perspective?

링크 추가:

http://www.youtube.com/watch?v=z9quxZsLcfo

https://www.destroyallsoftware.com/blog/2014/tdd-straw-men-and-rhetoric

왜 숫자는 0에서부터 세어야 하는가

알고스팟에 링크가 있다고 하여 검색하였으나 실패.

원문은 아래 링크에 있다.

http://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html

친절하게 누가 번역해 주신 분이 계셔서 링크를 걸어둔다.

http://blog.gorekun.com/1142

감사합니다. 그리고 번역부터 찾는걸 보면 나는 이미 글른듯.

1982년.