Portable Stimulus와 UVM 레지스터 모델을 이용해 생성하는 SoC 통합 테스트
상태바
Portable Stimulus와 UVM 레지스터 모델을 이용해 생성하는 SoC 통합 테스트
  • Matthew Ballance
  • 승인 2019.06.11 09:00
  • 댓글 0
이 기사를 공유합니다

레지스터를 읽고 쓰는 것은 대부분의 IP에서 작동을 제어하고 질의하는 주된 방법이다. 또한 디자인이 정상 작동하기 위한 측면에서도 레지스터는 핵심적인 역할을 수행하므로 레지스터 테스트는 설계와 검증, 구현 등의 과정에서 매우 중요하다고 할 수 있다.

따라서 우선 IP 수준에서 레지스터가 제대로 구현됐는지 검증해야 한다. 즉, IP 블록의 인터페이스에서 엑세스할 수 있는지, 올바른 리셋 레벨을 가졌는지 확인해야 하는데, 서브 시스템 수준에서 레지스터에 대한 엑세스를 검증하면 인터커넥트 네트워크와 어드레스 디코딩이 사양대로 구현됐음을 확인하는 데 도움이 된다.

이와 함께 SoC 수준에서는 레지스터에 대한 액세스를 검증함으로써 프로세서의 바이트 순서가 인터커넥트 구현과 일치하는지 여부, 부트 코드가 메모리 관리 장치(MMU)를 제대로 구성해 결과적으로 IP 레지스터가 프로세서에 가시적인 성질을 갖는지 확인할 수 있다. 

본고에서는 Portable Stimulus가 어떻게 Accellera의 Portable Stimulus Standard(PSS)를 통해 레지스터 모델에서 수집한 정보를 활용하는지, 나아가 블록과 서브 시스템, SoC 레지스터 액세스 테스트를 자동화할 수 있는지 살펴보고자 한다.

UVM 내장 레지스터 테스트

UVM은 레지스터 공간을 모델링 하기 위한 레지스터 모델을 제공하는데, 이는 사용 가능한 레지스터와 필드, 이들의 어드레스와 엑세스, 리셋 등의 속성이다. UVM 라이브러리는 내장된 유도 시퀀스도 함께 제공한다. 우리는 UVM 레지스터 모델을 통해 반복 작업을 수행하고 레지스터의 리셋 값을 확인하며, 읽기 쓰기가 가능한 모든 레지스터 필드의 수정과 점검에서 해당 레지스터가 접근할 수 있음을 확인한다.

이런 내장 테스트는 IP 수준에서 개별 IP블록 내의 레지스터가 제대로 구현되었는지 확인하는 데 있어 상당히 유용하다. 하지만 테스트 시퀀스를 서브 시스템 수준에서 재사용하기는 어려운데, 이는 서브 시스템에 들어 있는 레지스터의 수와 함께 내장 UVM 시퀀스가 디자인 내의 모든 레지스터를 테스트하기 위한 유도 시퀀스이기 때문이다.

결국, 내장 테스트 시퀀스를 SoC 수준에서 재사용하기 위한 시도 역시 어려워진다. 이 밖에도 내장 시퀀스가 SystemVerilog로 구현된 자체 점검 시퀀스라는 이유도 있다. 만약 이를 SoC의 내장 프로세서에서 실행시키려면 베어 메탈 C나 어셈블리 코드가 필요하다.

Portable Stimulus를 이용해 테스트 인텐트(test intent)를 모델링하면 테스트 공간을 보다 작은 여러 테스트로 분할할 수 있다. 내장 소프트웨어 테스트를 C나 어셈블리 코드로 작성함으로써 SoC 수준의 테스트를 겨냥할 수 있는 유연성도 갖게 된다.

 

테스트 인텐트와 테스트 실현

Portable Stimulus는 유도 테스트에서 가장 일반적으로 병합되는 두 개의 테스트 요소를 분리한다. 테스트 인텐트(Test intent)는 무엇을 테스트할 것인가에 대한 상위 수준 설계이고 테스트 실현(test realization)은 테스트 실행에 사용되는 메커니즘이다.

레지스터 모델을 테스트할 경우, 테스트 인텐트는 대략 다음과 같다.

•    레지스터 블록으로부터 레지스터 하나를 선택
•    레지스터로부터 읽기/쓰기 필드 하나를 선택
•    해당 필드 내에서 테스트할 비트 하나를 선택

위에서 설명한 테스트 인텐트는 검증 대상이 블록인지 서브 시스템인지 또는 SoC 수준의 설계인지 여부와는 관계가 없다. 또한 대상이 SystemVerilog 환경인지 내장 소프트웨어 환경인지 여부도 상관이 없다.

제약사항은 잠시 무시하고, 단일 레지스터의 테스트를 위한 테스트 인텐트를 위의 PSS 코드로 다음과 같이 캡처한다.


•    reg_id 필드에 테스트하려는 레지스터를 캡처한다
•    flip_bit 필드에 테스트하고자 하는 레지스터 내의 비트를 캡처한다
•    reg_addr 필드에 메모리 맵 내 필드의 어드레스를 캡처한다

사실 이 테스트 인텐트를 특정 테스트 환경에 연결시키기 위한 테스트 실현 없이는 우리의 테스트 인텐트도 별로 쓸모가 없다. 다만, 이상적인 경우는 Portable Stimulus를 염두에 두고 테스트를 설계하는 것이다. 그럼으로써 모든 환경에서 사용 가능한 공통 API(common API)를 설계할 수 있기 때문이다.

예를 들어, testbit로 명명된 방법을 모든 환경에서 사용할 수 있으며, 이 방법으로 특정 비트의 수정 기능을 테스트한다고 명시할 수도 있다. 이런 기능의 함수 프로토타입은 아래와 같다.

SystemVerilog IP 또는 서브 시스템 수준의 환경에서 이 방법을 UVM 시퀀스 내의 태스크로서 구현할 수 있다. 아래의 SystemVerilog 태스크는 메모리 액세스 API를 이용해 메모리를 읽고 쓰며, 다음과 같은 방법을 통해 레지스터 비트 테스트 작업을 구현한다.


•    레지스터의 현재 값 읽기
•    레지스터의 목표 비트를 무효화하고 새로운 레지스터 값을 쓰기
•    레지스터 어드레스를 다시 읽어 비트 값이 유지되는지 여부를 확인

위의 예에서 지속적인 실행을 통해 오류가 발생하는지 확인한다. 이는 UVM 테스트에서 매우 전형적인 예시에 속한다. 하지만 내장 소프트웨어 환경의 제약사항은 다르다.

위 예제는 내장 프로세서에서 구현 가능한 testbit 기능을 보여준다. 보다시피 이 접근 방법은  SystemVerilog 버전과 매우 유사한데, 구체적인 내용은 다르다. 가장 큰 차이점 중 하나는 여기에서는 레지스터 비트 테스트가 실패할 경우 테스트 전체를 종료할 것이라 가정하고 있다는 점이다.

테스트 인텐트를 테스트 실현으로부터 분리하는 것이 Portable Stimulus의 핵심 요소로서, 이것이 테스트 인텐트를 여러 환경에 걸쳐 손쉽게 리타겟팅(Re-Targeting)할 수 있는 비결이다.

 

서브 시스템 레지스터 모델의 생성

이처럼 PSS를 이용한 레지스터 테스트를 위해 테스트 인텐트와 테스트 실현을 캡처하는 접근 방법을 살펴보았다. 이번에는 우리의 UVM 레지스터 모델이 어떻게 개발됐는지 잠시 살펴보자. 본 예제의 목적상 그림1과 같이 매우 간단한 서브 시스템을 살펴볼 텐데, 이 서브 시스템은 프로세서 하나와 이더넷 컨트롤러 두 개 그리고 DMA 엔진 두 개를 갖고 있다.

[그림1] 간단한 서브 시스템
[그림1] 간단한 서브 시스템

UVM 레지스터 모델은 이더넷과 DMA 컨트롤러가 블록 수준에서 검증되었을 때 생성됐다. 이 UVM 레지스터 모델은 수작업으로 작성됐을 수도 있지만, [그림 2]에서 보듯이 레지스터 생성 툴을 이용해 생성됐을 가능성이 더 높다.

[그림2]
[그림2]

레지스터 생성 툴은 다양한 표준(IP-XACT, SystemRDL)과 비표준(예컨대, CSV) 포맷의 레지스터 사양을 수용하며, 이런 레지스터 사양에 대한 다양한 뷰를 생성한다. 레지스터 모델의 RTL 구현물을 생성하면 설계 구현 시간이 절감된다.

UVM 레지스터 모델을 생성하면 테스트벤치 구현 시간이 단축된다. 어떤 경우든 모든 사항이 높은 수준의 레지스터 사양에 부합되도록 하면 상당한 시간을 절약하고 노력의 낭비도 막을 수 있다는 점을 명심하자.

서브 시스템 수준에 도달하면 레지스터 생성 툴의 지원을 통해 서브 시스템 전체를 위한 레지스터 모델을 어셈블 할 수 있다. 또는 단순히 개별적인 블록 수준의 레지스터 모델들을 가지고 서브 시스템 수준의 레지스터 모델을 어셈블 할 수도 있다. 아래의 코드는 이더넷과 DMA 컨트롤러 레지스터 모델로부터 서브 시스템 수준의 레지스터 모델을 어셈블 한다. 보다시피, 필요한 코드는 많지 않다.

•  디자인 내의 사용 가능한 레지스터와 그 어드레스

•  이들 레지스터 내의 필드와 모든 액세스 제약사항

놀라운 점은 레지스터 수가 그야말로 순식간에 늘어난다는 것이다. 단지 두 개의 이더넷 컨트롤러와 두 개의 DMA 컨트롤러를 위한 레지스터 모델을 생성했을 뿐인데, 테스트 가능한 필드를 갖는 레지스터 수가 989개나 된다. 그렇다면 완전한 SoC에는 얼마나 많은 레지스터가 있을 지 상상해보라!

 

레지스터 테스트 PSS 모델의 생성

이렇게 서브 시스템 또는 SoC 수준의 레지스터 모델을 캡처했다. 이제 레지스터 액세스 테스트를 생성하려면 어떻게 해야 할까? 먼저 레지스터 액세스 테스트 인텐트의 PSS 모델을 생성해야 한다. 그 다음에는 이 테스트 인텐트를 테스트 실현을 통해 특정 검증 환경에 연결시켜야 한다.

 

핵심 테스트 인텐트의 생성

앞서 우리는 레지스터 액세스 테스트를 위한 핵심 테스트 인텐트를 살펴보았다. 즉, 레지스터를 선택한 뒤 이 레지스터 내에서 테스트할 비트를 선택하는 것이다. 물론 이 높은 수준의 테스트 인텐트는 테스트 대상 디자인 내의 레지스터를 토대로 제한돼야만 한다. 희소식은 레지스터 모델에서 이러한 제약사항들을 생성하는 데 필요한 모든 정보를 우리가 이미 캡처해 놓았다는 것이다.

우리의 레지스터 생성 툴은 경우에 따라서 PSS 레지스터 테스트를 출력 중 하나로 직접 생성할 수도 있다. 그렇지 않을 경우, 테스트 인텐트를 자동생성하기 위한 접근 방법 중 하나는 일부 SystemVerilog 코드를 실행하는 것이다.

이는 UVM 레지스터 모델을 통한 반복 작업으로 우리의 PSS 레지스터-테스트 인텐트를 작성한다. 아래의 코드에서 볼 수 있는 UVM 테스트는 regmodel2pss라는 명칭의 클래스를 호출하여 PSS 테스트 인텐트를 생성한다.

그 결과 얻어지는 Portable Stimulus 기술내용에는 서브 시스템 레지스터 맵의 레지스터-테스트 인텐트가 포착되어 있다. 아래의 코드는 레지스터 모델의 테스트를 위해 생성된 PSS 구성요소 및 액션의 첫 번째 부분이다.

우리의 액션(명칭은 레지스터 블록의 명칭에서 파생된 것이다)은 세 개의 rand 필드를 선언한다. reg_id 필드에는 대상 레지스터의 ID가 포함되며, 이는 0과 ‘레지스터 수 빼기 1’ 사이의 범위를 갖는다. flip_bit 필드는 테스트할 레지스터 비트를 명시한다. 이 필드도 레지스터 어드레스와 마찬가지로 레지스터 id를 토대로 제한된다.

위의 자동생성 된 제약사항은 우리의 액션을 통해 유효한 레지스터 어드레스와 테스트 대상 레지스터를 토대로 한 flip_bit가 생성되도록 보장해준다.

제약사항의 자동생성 외에도, PSS 커버리지 모델은 모든 레지스터와 레지스터 내의 모든 비트가 망라되도록 자동 생성될 수 있다.

최상위 레벨의 테스트 시나리오 생성

핵심 레지스터 테스트 인텐트를 갖추고 나면 이를 최상위 수준의 PSS 시나리오에 통합시켜야 한다. 핵심 레지스터 테스트 인텐트는 레지스터 모델로부터 자동적으로 얻을 수 있지만, 우리의 최상위 수준 레지스터 테스트 시나리오는 수작업으로 작성된다.

위의 그림은 테스트 시나리오로서, 우리의 테스트 인텐트를 인코딩하는 핵심 액션의 최상단에 구축된다. 테스트 시나리오는 PSS에서 요구되는 대로 최상위 수준의 구성요소에 캡슐화된다. 레지스터 테스트 구성요소의 인스턴스(subsys_reg_block_c)를 최상위 수준의 구성요소 내에 생성하는데, 이는 이 구성요소로부터 액션을 사용할 것이기 때문이다.

우리의 최상위 수준 액션(my_subsys_regtest_a)에서는 testbit라는 명칭을 갖는 subsys_reg_block_regs_a 액션의 인스턴스를 생성한다. 이 액션 내에는 레지스터 테스트 필드와 커버그룹(covergroup)의 인스턴스가 있다. 최상위 수준 액션의 활동에서는 testbit을 100회 실행한다. 즉, 테스트 실행 시마다 100개의 레지스터 비트를 테스트하는 것이다.

 

테스트 실현에 대한 매핑

우리의 최상위 수준 테스트 시나리오가 실제로 하는 일은 아직 아무것도 없는데, 이는 테스트 실현이 결여되어 있기 때문이다. 다행히도, PSS를 통해 핵심 기술내용을 변경하지 않고도 손쉽게 테스트 실현 층을 삽입할 수 있다. 이 경우에는 subsys_reg_block_regs_a 액션이 바로 그것이다.

위의 그림은 PSS 절차적 인터페이스를 이용하는 SystemVerilog에 대한 테스트 실현 기술 내용이다. 외부 함수의 시그너처가 선언되며, 이 함수는 액션의 실행 블록으로부터 호출된다. 이런 스타일의 테스트 실현은 C, C++, SystemVerilog 등과 같이 호출 가능한 절차를 지원하는 모든 환경에서 가능하다.

대부분의 경우, 우리의 SoC를 위한 베어메탈 내장 소프트웨어 테스트는 C로 작성된다. 하지만 어셈블리 언어 테스트를 작성해야 한다면 어떻게 될까? 다행히도, PSS는 이를 위한 방법도 제공한다.

위의 테스트에서는 레지스터 비트의 테스트를 위해 생성해야 하는 어셈블리 코드(이 경우에는 RISC-V)의 조각을 PSS target-template 실행 블록을 이용해 명시하고 있다. 중괄호(예컨대, {{reg_addr}})는 PSS 모델 내 필드의 현행 값을 참조하고 이 값을 생성된 코드에 대체해 넣기 위해 사용된다. 테스트 실현을 어셈블리 언어로 할 경우 분명히 제약이 있지만, 필요한 기법이 이것이라면 PSS는 이를 가능하게 해준다.

 

PSS 테스트의 적용

이제 레지스터에 대한 액세스를 테스트하기 위한 테스트 인텐트와 테스트 실현을 갖췄으므로 테스트 실행을 시작할 수 있다. UVM 환경에서 PSS는 유도 테스트를 사전생성하거나 PSS 솔버 엔진을 실행 중인 시뮬레이션과 함께 실행할 수 있는 유연성을 제공한다. 두 접근 방법 모두 장단점이 있다. 유도 테스트의 경우에는 이해하기가 쉽다.

예를 들어, 위의 UVM 시퀀스는 이해하기가 매우 쉽고 항상 정확히 똑같은 일을 수행한다. 하지만 우리가 정말로 원하는 것은 상이한 시드를 실행할 경우는 테스트가 약간 다른 것을 수행했으면 하는 것이다.

또한 수만 번의 레지스터 테스트를 회귀 방식으로 실행되는 상이한 시뮬레이션들 전반에 걸쳐 손쉽게 분할할 수 있기를 원한다. 시뮬레이션과 함께 실행되는 PSS 솔버 엔진을 사용하는 것이 큰 도움이 되는 것은 바로 이 부분이다.

동일한 시퀀스를 서로 다른 시드로 실행하면 상이한 거동이 야기되며, Questa® inFact와 같은 PSS 툴은 테스트를 회귀 방식으로 실행되는 시뮬레이션 전반에 걸쳐 역동적으로 분할하기 위한 전용 기능을 제공한다.

하지만 어셈블리 언어를 사용해야 한다면 target-template 실행 블록 테스트 실현을 이용해 완전한 독립형 테스트를 생성할 수 있다. 이 테스트의 한 부분은 아래 왼쪽에서 보는 바와 같으며, 두 개의 레지스터/비트 조합에 대한 테스트를 볼 수 있다.

요약

레지스터 테스트는 IP로부터 서브 시스템, 그리고 SoC 수준에 이르기까지 모든 환경에서 매우 유용한 스모크 테스트이다. UVM 라이브러리에 내장된 레지스터 테스트 시퀀스는 주로 IP 수준에서 유용한 반면, 레지스터 테스트의 테스트 인텐트를 PSS 모델에서 캡처하면 레지스터 테스트 기능을 IP 수준에서 SoC 수준으로 이동시킬 수 있으며, 주어진 테스트 실행 시 어떤 레지스터를 확인할 것인지 제어하는 부분에서 보다 큰 유연성을 제공한다.

레지스터 생성 툴에 대한 입력 파일과 그에 따른 UVM 레지스터 모델 모두가 Portable Stimulus 테스트 인텐트를 자동 생성하기에 충분한 정보를 포함하고 있다. 따라서 레지스터 생성 툴이 Portable Stimulus 테스트에 대한 지원을 추가하기 매우 쉬워진다.

또한 Portable Stimulus 테스트를 기존의 UVM 레지스터 모델로부터 이끌어내기도 매우 쉬워진다. 이는 기존의 UVM 레지스터 모델이 어떻게 생성되었든 관계없다.

따라서 Portable Stimulus를 가장 까다로운 테스트용으로만 아껴둘 필요가 없다. 때로는 레지스터 액세스 테스트처럼 일견 간단해 보이는 테스트 작업에도 손쉽고 생산적으로 적용할 수 있다. 이를 통해 우리는 이동성을 실현하고 반복적인 노력을 피할 수 있다.

 

글 | 매튜 밸런스(Matthew Ballance) - 멘토, 지멘스 비즈니스