Universidad de Costa Rica Facultad de Ingenierı́a Escuela de Ingenierı́a Eléctrica Verificación funcional pre-silicio del UART 16550 de OpenCores mediante la metodologı́a UVM Por: José Andrés Pacheco Castro Ciudad Universitaria “Rodrigo Facio”, Costa Rica Diciembre 2013 Verificación funcional pre-silicio del UART 16550 de OpenCores mediante la metodologı́a UVM Por: José Andrés Pacheco Castro IE-0499 Proyecto eléctrico Aprobado por el Tribunal: Ing. Enrique Coen Alfaro Profesor guı́a PhD Randolph Steinvorth Fernández Profesor lector PhD Lucky Lochi Yu Profesor lector Resumen El presente trabajo detalla los pasos generales que requiere un esfuerzo de verificación. Este comprende del estudio de la especificación del dispositivo, el plan de verificación, el desarrollo del ambiente de verificación y el análisis de los resultados. Para esta verificación se decidió utilizar la metodologı́a uvm, un estándar en la industria que tiene un gran nivel de penetración. Se concluye con este trabajo que sı́ se pudo verificar que el envı́o y recepción de paquetes en el uart funciona correctamente. La metodologı́a uvm provee un marco de referencia que permite el desarrollo de ambientes de verificación de manera rápida y efectiva, que pueden ser reutilizados. Se recomienda buscar herramientas de software libre para impulsar un laboratorio de verificación en la ucr. Como candidato principal está SystemC, un lenguaje que se puede usar para verificación desarrollado a partir de C++. v Se agradece a: Enrique Coen, por la idea y su guı́a, Hewlett-Packard, por la colaboración, mis padres, por su apoyo. vi Índice general Índice de figuras viii Índice de cuadros viii Nomenclatura 1 Introducción 1.1 Alcance del proyecto 1.2 Objetivos . . . . . . 1.3 Metodologı́a . . . . . 1.4 Contenido . . . . . . ix . . . . 1 1 1 1 2 2 Antecedentes 2.1 El proceso de diseño de circuitos integrados . . . . . . . . . . . 2.2 Conceptos generales de verificación . . . . . . . . . . . . . . . . 2.3 La metodologı́a UVM . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Desarrollo 3.1 Especificación del UART 16550 . . . . . 3.2 Plan de verificación . . . . . . . . . . . . 3.3 El ambiente de verificación . . . . . . . 3.4 Implementación del plan de verificación 3.5 Análisis de resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 14 15 18 27 4 Conclusiones y recomendaciones 43 Bibliografı́a 45 vii Índice de figuras 2.1 2.2 El modelo de reconvergencia (Bergeron, 2003) . . . . . . . . . . . . Diagrama de un uvc genérico . . . . . . . . . . . . . . . . . . . . . 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 Diagrama de bloques del uart 16550 (Gorban, 2002) . . Diagrama del ambiente de verificación (Cadence, 2012) . Cobertura funcional register test . . . . . . . . . . . . . Cobertura de código register test . . . . . . . . . . . . Cobertura funcional send frame . . . . . . . . . . . . . Cobertura de código send frame . . . . . . . . . . . . . Cobertura funcional receive frame y receive frame err . Cobertura de código receive frame y receive frame err . Cobertura funcional loopback . . . . . . . . . . . . . . . Cobertura de código loopback . . . . . . . . . . . . . . Cobertura funcional uart<->SoC . . . . . . . . . . . . Cobertura funcional out<->uart . . . . . . . . . . . . Cobertura de código por módulo . . . . . . . . . . . . . Cobertura de código por instancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 9 . . . . . . . . . . . . . . 13 16 29 30 31 32 33 34 35 36 37 38 39 40 Casos de prueba . . . . . . . . . . . . . . . . . . . . . . . . . . . . Puntos de cobertura . . . . . . . . . . . . . . . . . . . . . . . . . . 14 15 Índice de cuadros 3.1 3.2 viii Nomenclatura ASIC Application-specific integrated circuit (Circuito integrado para aplicación especı́fica). CI Circuito Integrado. DU T Device Under Testing (Dispositivo bajo pruebas). EDA Electronic Design Automation (Diseño electrónico automático). F P GA Arreglo de compuertas reprogramable por sus siglas en inglés. F SM Máquina de estados finita por sus siglan en inglés. Overf low En un arreglo, escribir más datos que el espacio disponible para mantenerlos. Riesgo En verificación, probabilidad de que existan defectos sin detectar en un diseño. RT L Lenguaje de descripción de hardware al nivel de registros. También se refiere al modelo que lo utiliza. Secuencia Método que aplica un estı́mulo al dut. SoC System-on-Chip. Se refiere a la combinación de múltiples bloques funcionales en un diseño final. Startbit En una trama, bit que indica el inicio de la misma. Stopbit En una trama, bit que indica el final de la misma. T estbench Banco de pruebas. T rama Unidad de envı́o de datos. ix 1 Introducción 1.1 Alcance del proyecto En la escuela de ingenierı́a eléctrica de la Universidad de Costa Rica no se imparte un curso de verificación. Esto a pesar del creciente papel que esta disciplina juega en el ciclo de desarrollo de circuitos integrados. Este proyecto pretende introducir el tema mediante la implementación de un plan de verificación para un diseño de código abierto. Se realizará una verificación pre-silicio de la versión 0.6 del uart 16550 de OpenCores, con un banco de verificación basado en un ejemplo publicado por la fundación Accellera y Cadence. Mediante este se configurará al dispositivo de diferentes maneras y se enviarán y recibirán paquetes. 1.2 Objetivos Objetivo general Verificar la implementación del uart 16550 de OpenCores, de acuerdo con su especificación técnica. Objetivos especı́ficos Para el desarrollo de este proyecto se establecieron los siguientes objetivos: • Estudiar el proceso de diseño de un circuito integrado, y el rol de la verificación en éste. • Desarrollar un plan de verificación para el uart 16550. • Implementar un ambiente de verificación uvm que permita aplicar el plan, basado en los modelos de referencia publicados por Accellera. • Verificar la funcionalidad de la implementación del uart 16550 utilizando el ambiente desarrollado y las herramientas eda de Cadence. 1.3 Metodologı́a 1. Desarrollo de un plan de verificación basado en la especificación del uart 16550. 1 2 1 Introducción 2. Implementación de un ambiente de verificación para aplicar el plan. 3. Aplicar las pruebas repetidamente sobre el dut. 4. Analizar los resultados de cobertura y funcionamiento correcto. 5. Evaluar el desempeño el dut y el ambiente de verificación basado en los resultados obtenidos. 1.4 Contenido Antecedentes Se provee un resumen de las ideas básicas sobre las que se fundamenta la metodologı́a uvm para realizar su trabajo. Se incluye además una explicación de la estructura que toma un banco de pruebas bajo ésta. Desarrollo En el desarrollo se estudia la especificación del uart 16550, se desarrolla un plan de verificación para revisar las caracterı́sticas de interés, se explica la implementación del ambiente de verificación y se analizan los resultados obtenidos al ejecutar el plan. Conclusiones y recomendaciones En este trabajo sı́ se pudo verificar que el envı́o y recepción de paquetes en el uart funciona correctamente. La metodologı́a uvm provee un marco de referencia que permite el desarrollo de ambientes de verificación de manera rápida y efectiva, que pueden ser reutilizados. Adicionalmente de recomienda buscar herramientas de software libre para impulsar un laboratorio de verificación en la ucr. Como candidato principal está SystemC. 2 Antecedentes La disciplina de la verificación ha evolucionado de manera tan vertiginosa como lo ha hecho la tecnologı́a de CI en sı́. Con la creciente complejidad en los diseños, la tarea de verificar que estos se ajustan a la especificación llega a consumir un 70 % del esfuerzo total (Bergeron, 2003). Es por esto que hoy en dı́a existen decenas de metodologı́as y procesos que intentan facilitar la tarea de verificación 2.1 El proceso de diseño de circuitos integrados Todo circuito integrado empieza como una idea innovadora. Esta se pasa a un equipo de mercadeo que estudia las necesidades del mercado y genera una lista de caracterı́sticas que debe tener el producto. Esta lista llega a un equipo de diseñadores de alto nivel (conocidos como arquitectos) que determinan cuáles caracterı́sticas son posibles de implementar, y planean la estructura general del circuito. Estos tres actores se ponen de acuerdo y finalmente producen la especificación del producto. Esta especificación incluye detalles de funcionamiento, interfaces y caracterı́sticas, más no de implementación. Los diseñadores se encargan de implementar los requisitos de la especificación en un lenguaje de descripción a nivel de registros (rtl por sus siglas en inglés). Este modelo lo reciben los diseñadores fı́sicos, que crean lo que se conoce como un mapa de nodos. En esencia, se describe al circuito al nivel de transistores y conexiones entre éstos. La tecnologı́a actual automatiza gran parte de esta conversión de rtl a mapa de nodos, y ya se está logrando incluso automatizar la generación de rtl. Finalmente, este mapa de nodos es enviado a una fábrica en donde se imprime el producto en silicio. Los avances en miniaturización de transistores, y las complejas tecnologı́as que esto implica, ha provocado que el costo de producir un prototipo sea extremadamente alto, superior a un millón de dólares. Esto hace que reducir el peligro de que este prototipo tenga un defecto sea una tarea muy importante. Es aquı́ donde entra la verificación. Paralelo al proceso descrito anterirormente, trabajan los verificadores. Éstos indican alternativas arquitecturales que pueden facilitar la verificación del diseño; verifican que la funcionalidad implementada en el rtl corresponda con 3 4 2 Antecedentes lo que define la especificación, y finalmente comprueban que el mapa de nodos sea equivalente al rtl. Esto se conoce como verificación pre-silicio. Para repartir el trabajo de manera efectiva, la verificación pre-silicio se realiza a nivel de bloque y a nivel de sistema. La especificación original se subdivide en bloques que contienen un sólo aspecto de la funcionalidad del todo. Éstos se revisan individualmente en la verificación de bloque. En la verificación de sistema todos son conectados y se ejecutan pruebas de más alto nivel. Una vez que se tiene el prototipo, se debe verificar que esté libre de defectos. Esto se realiza en la verificación post-silicio, que vuelve a aplicar muchas de las pruebas pre-silico. Finalmente, se crea una baterı́a de pruebas a las que se somete la lı́nea de producción para evitar la distribución de errores de impresión. 2.2 Conceptos generales de verificación Modelo de reconvergencia y el factor humano En el proceso de diseño de un asic, se parte de una especificación para crear un producto. Dicha implementación es exitosa en la medida en que su comportamiento sea correcto. Hoy en dı́a esta tarea no es automatizable, requiriendo la imaginación y habilidad de un diseñador para realizar la conversión. Esta intervención introduce un riesgo, la falibilidad humana. Esta puede darse de dos maneras principales: una equivocación, donde el diseñador introduce un error en el diseño que causa que se comporte de manera diferente a la que él espera. La segunda manera es un error, donde el diseñador se equivoca al interpretar la especificación y su diseño produce el resultado que él espera, pero que no es correcto. De estos dos problemas el segundo resulta ser más serio y difı́cil de detectar y corregir. Para mitigar el riesgo del factor humano, se ha desarrollado el modelo de reconvergencia. En éste se separan los roles de diseño y verificación. A partir de una misma especificación, se crea el rtl y el ambiente de verificación de manera independiente. Esto permite que diferentes interpretaciones de la especificación se revisen unas contra otras, mejorando la detección de errores. 2.2. Conceptos generales de verificación 5 Figura 2.1: El modelo de reconvergencia (Bergeron, 2003) Tipos de verificación Una vez establecido el modelo de reconvergencia, la verificación en sı́ se puede realizar de muchas maneras diferentes. Éstas varı́an en la minuciosidad con la que se analiza el rtl y el esfuerzo requerido para implementarlos. Algunas de las opciones que existen son: • Verificación formal: En este se utilizan algoritmos para probar matemáticamente que el código cumple con ciertos requisitos, como transiciones de estado especı́ficas. En general sólo es aplicable a módulos pequeños, que se reutilizan frecuentemente en todo un diseño. Esto debido a que con diseños más grandes la complejidad crece exponencialmente, impidiendo que se obtengan resultados en un marco de tiempo práctico. • Emulación: El circuito es sintetizado en un fpga o dispositivo similar. Esto permite ejercitar el código mucho más rápido que una simulación de rtl. La mejora en velocidad es gracias a que la simulación está restringida por la naturaleza secuencial de el procesador. • Chequeo de equivalencia: Es una verificación de más bajo nivel, verifica que el mapa de nodos se comporte igual al rtl del que se derivó. • Modelado: Se crea un modelo funcional del diseño en un lenguaje de más alto nivel que rtl. Luego ambos son sometidos al mismo estı́mulo y se verifica que produzcan el mismo resultado. Para este proyecto se utilizó verificación mediante simulación. El rtl es estimulado mediante un programa que captura sus reacciones, y éstas son evaluadas para determinar si son aceptables. Esta captura y creación de estı́mulo se realiza mediante una banca de pruebas (testbench), que se desarrolló mediante la metodologı́a uvm. 6 2 Antecedentes Verificación de caja negra, blanca o gris Este concepto, prestado de la disciplina de análisis de sistemas, se usa de forma muy similar. La aplicación de estı́mulo y recolección de respuesta puede darse a diferentes niveles en el rtl: En el caso de caja negra, éstos sólo se dan en la interfaz especificada. En los otros dos, se «abre la caja» y se observa el comportamiento interno del código en mayor o menor medida. Estos tres métodos tienen diferentes ventajas y desventajas. El modelo de caja negra respeta totalmente el modelo de reconvergencia, evitando que cualquier detalle de la implementación se incluya en la verificación, maximizando el beneficio de tener a varios ingenieros trabajando en un mismo problema. La ventaja del modelo de caja blanca es que puede llegar a crear escenarios en el rtl que resultan extremadamente difı́ciles de alcanzar mediante uso solamente de la interfaz externa. Banco de pruebas auto-verificado (self-checking testbench) En el pasado, las pruebas que se aplicaban para realizar verificación tenı́an una envergadura muy grande. Por sı́ solas definı́an el estı́mulo que se aplicaba al dut, observaban la respuesta, y determinaban si esta era correcta. Esta y la siguiente sección separan la tradicional «prueba» que se aplica al rtl en dos componentes separados: el estı́mulo y la verificación de la respuesta del dut. En una banca de pruebas auto-verificada se incluyen componentes que comparan el estado del dut a un modelo definido por el verificador. Éstos componentes se encuentran habilitados durante todas las pruebas, y constantemente revisan que el comportamiento se ajuste a la especificación. Esto se realiza mediante monitores y tablas de verifiación, que se verán en la siguiente sección. Es importante notar que debido a esto una prueba que verifica un sumador en un alu puede encontrar una defecto aparentemente no relacionado en los registros de control, por ejemplo. Pruebas aleatorias restringidas (constrained random testing) Debido a que la verificación de comportamiento es realizado automáticamente por el testbench, las pruebas que se desarrollan se limitan a generar suficiente estı́mulo para que el dut atraviese todo su espacio de estados. En el pasado esto se lograba mediante una gran cantidad de pruebas dirigidas, que aplicaban un estı́mulo especı́fico para activar una caracterı́stica determinada de la especificación. Este método se ve limitado por la imaginación del verificador, y resulta insuficiente para cubrir grandes diseños. Esto se resuelve mediante verifica- 2.2. Conceptos generales de verificación 7 ción limitada aleatoria (constrained random testing). Con este método cada prueba define aspectos generales de la caracterı́stica que se va a verificar, y los detalles se rellenan de manera aleatoria. Luego cada prueba se corre de manera repetida, con una semilla diferente, generando cientos de escenarios de prueba que consiguen una cobertura más efectiva, en lo que se conoce como una regresión. Modelado a nivel de transacciones El modelado a nivel de transacciones (tlm por sus siglas en ingles) es el lenguaje con el que se comunican todos los componentes de una banca de pruebas. Este abstrae los datos funcionales de los detalles de la implementación y la capa en que se generaron, permitiendo que estos pasen de una abstracción a otra y de un estándar a otro de manera compatible. ¿Cuándo se completa la verificación de un modelo? El proceso para determinar que un diseño ha sido verificado es impreciso en el mejor de los casos. Esto se debe a que el ideal, probar todas las posibles combinaciones de entradas con todos los estados posibles de dut, requiere de una cantidad de ciclos de procesador astronómica para todos excepto los más simples diseños. Para solucionar este problema se han desarrollado una serie de métricas que aproximan el nivel de verificación alcanzado para un diseño particular (Vasudevan, 2006). Los dos métodos que se utilizaron en este trabajo son: • Cobertura de lı́nea o código: Evalúa la cantidad de lı́neas de código que han sido ejecutadas en el rtl. Cuando se alcanza un 100 % en esta métrica se puede decir que la implementación está completamente ejercitada. Esta cobertura comprende más que la ejecución individual de cada lı́nea: cuenta los caminos posibles que se pueden dar debido a expresiones condicionales, y además cuenta las formas en que dichas expresiones condicionales pueden activarse. La cantidad de posibilidades que estos factores generan causa que alcanzar una cobertura del 100 % sea imposible en la práctica. Además, que todo el código haya sido ejercitado no garantiza que se haya comportado de manera correcta. Esto depende de los chequeadores, que analizan la respuesta del dut. • Cobertura funcional: es definida de manera manual por el verificador. Éste se da a la tarea de marcar estados interesantes en los que puede entrar el dut, y se recolecta información de la simulación cuando se 8 2 Antecedentes dan. Esto es más limitado que la cobertura de lı́nea, pero reduce la información que se graba a niveles más manejables. Ésta cobertura sı́ puede alcanzar el 100 % en un esfuerzo de verificación real. 2.3 La metodologı́a UVM La metodologı́a de verificación universal (uvm por sus siglas en inglés) fue desarrollada por Accellera Systems Initiative. Esta es una organización sin fines de lucro creada para avanzar los estándares de diseño de sistemas, modelado y verificación en la industria electrónica. Entre sus miembros se encuentran amd, arm, Cadence, Synopsys, Mentor Graphics, Intel, entre otros. uvm fue creado con la idea de establecer un estándar de verificación en la industria. Este modulariza muchos de los componentes usados en verificación, permitiendo un gran alto grado de reúso, y la integración del código de muchos proveedores en una sola banca de pruebas. Esto se logra mediante tres conceptos clave que dan gran flexibilidad a un banco de pruebas que se aprovecha de éstos. Estructura del ambiente de verificación Un testbench uvm se compone de una serie de componentes de verificación universal (uvc pos sus siglas en inglés) interactuando entre sı́. Cada uvc encapsula la información relacionada con un bloque especı́fico (conocido como componente de verificación de módulo (module uvc)) o con una interfaz o protocolo (componente de verificación de interfaz (interface uvc))(Robinson, 2007). Además de estos elementos, el testbench cuenta con un manipulador de secuencias que controlan los componentes para generar el estı́mulo, un mapa de señales que permite que los componentes se conecten con el rtl, y otras utilidades que refinan el comportamiento del ambiente. Componentes de verificación universal Los componentes de verificación universal son la unidad más pequeña reutilizable en uvm. Éstos incluyen una serie de bloques que permiten enviar estı́mulo al dut, monitorear su respuesta y realizar la verificación al nivel de capa del componente. Estos bloques son (Rosenberg y Meade, 2010): Agente El agente instancia y configura todos los demás elementos del componente. Puede ser pasivo, en cuyo caso sólo realiza labores de monitoreo, 2.3. La metodologı́a UVM 9 Figura 2.2: Diagrama de un uvc genérico o activo, generando estı́mulo que se pasa al dut u otro componente. Un sólo uvc pude tener varios agentes, por ejemplo para soportar interfaces múltiples. Manipulador de secuencias (sequence driver) El manipulador de secuencias es utilizado por los agentes activos para estimular al dut. Aquı́ se generan transacciones tlm que son pasadas de un módulo a otro hasta que llegan a un modelo funcional de bus. Modelo funcional de bus El modelo funcional de bus (bfm por sus siglas en inglés) se encarga de estimular directamente al dut, mediante el mapa de señales que le provee el ambiente. Monitor El monitor puede recibir el estı́mulo directamente del dut o de monitores subalternos en la forma de transacciones tlm. Éste se encarga de la auto verificación mediante una tabla de verificación y recopila los datos de cobertura funcional. Tabla de verificación Este es el elemento clave para realizar la verificación. Cada vez que se genera una transacción de estı́mulo tlm, esta viaja a través de los monitores instanciados y es añadida a la tabla apropiada, dependiendo de las caracterı́sticas de la transacción: contenido de datos, dirección ip, dirección de memoria, paridad, codificación, etc. Como puede observarse, las transacciones codifican cualquier tipo de paquete o trama que puede usarse en un diseño. 10 2 Antecedentes Luego de que la transacción es generada, se pasa al bfm donde es convertida en estı́mulo. Si el dut está funcionando correctamente, este responde de cierta manera que un monitor capta y usa para generar una transacción de repuesta, que llega a ser emparejada con la primera transacción en la tabla. Si al final de la prueba todas las transacciones están emparajedas, el dut funciona correctamente. Estos componentes cambian dependiendo de si el uvc que los contiene es de módulo o de interfaz. En el caso del module uvc, el monitor recibe las transacciones de monitores subalternos en los interface uvc. Este es el componente que contiene a la tabla de verificación principal donde se verifica la funcionalidad de bloque. Dado que no interactúa directamente con el dut, no tiene un bfm. En su lugar, el manejador de secuencias se conecta directamente con uvcs subordinados. En los interface uvc, el monitor genera transacciones directamente de las respuestas que observa en el dut. También es el punto final donde las transacciones se convierten en estı́mulo que se envı́a mediante el bfm. Las tablas de verificación en este punto no son comunes, y se limitan a verificar el protocolo relacionado con la interfaz que le concierne. 3 Desarrollo 3.1 Especificación del UART 16550 El transmisor-receptor ası́ncrono universal (uart por sus siglas en inglés) es un bloque que se encarga de traducir datos de forma paralela (como se utilizan dentro del SoC) a forma serial, para ser transmitidos a un dispositivo externo. Éste es a su vez configurable para diferentes formatos y velocidades de tramas. Un uart se puede utilizar para comunicarse con estándares seriales como el rs-232, comunes en una gran variedad de aparatos. El dut que se verificó en este proyecto es un diseño de código abierto que pertenece a la fundación OpenCores.org. Pretende ser funcionalmente equivalente al dispositivo 16550A de National Semiconductors. Además, se comunica con el resto del SoC a través de la interface WISHBONE System-on-Chip (SoC) Interconnect Architecture for Portable ip Cores, una interfaz estándar para conectar los bloques de rtl de OpenCores. Este uart funciona mediante 2 registros fifo principales. Uno contiene la información que se está enviando y otro la información que se está recibiendo. Otros registros controlan la comunicación con el SoC (interrupciones, nivel en los fifos, etc) y la comunicación con otros dispositivos (disponibilidad del SoC, etc). Finalmente, el uart puede ponerse en modo loopback, donde se conectan de manera interna los fifos de entrada y salida. Interfaces (ver figura 3.1): • wishbone: Estas señales implementan la interfaz para integrar el chip con el resto del SoC. Consisten de: – clk: Señal de reloj. – wb rst i: Reset ası́ncrono. – wb addr i: Dirección del registro (3 bits). – wb sel i: Selección del bloque (4 bits). – wb dat i: Salida de datos (8 bits). – wb dat o: Entrada de datos (8 bits). – wb we i: Selección de lectura o escritura. 11 12 3 Desarrollo – wb stb i: Ciclo de transferencia. – wb cyc i: Ciclo de bus en progreso. – wb ack o: ack de transferencia. • Información: int o Salida de interrupción. • Salida: Son la señales que salen del chip, implementan una interfaz serial de entrada y salida. – stx pad o: Salida serial. – srx pad i: Entrada serial. – rts pad o: Petición de envı́o. – dtr pad o: Terminal de datos disponible. – cts pad i: Permiso de envı́o. – dsr pad i: Datos disponibles. – ri pad i: Indicador de anillo. – dcd pad i: Detección de portador de datos. Registros: • Búfer receptor (Receiver buffer): Registro fifo que almacena hasta 16 bytes recibidos. • Registro de cola de transmisión (Transmitter holding register): Registro fifo que almacena hasta 16 bytes por enviar. • Habilitación de interrupción (Interrupt enable): Máscara para las interrupciones: Datos recibidos, cola de envı́o vacı́a, estado de lı́nea de recepción, y estado de modem. • Identificación de interrupción (Interrupt identification): Identifica la fuente de interrupción: Esta de lı́nea de recepción, datos recibidos, timeout, cola de envı́o vacı́a y estado de modem. • Control de fifo (fifo control): Escribir a este registro puede resetear el fifo de entrada o salida, y define el nivel al que se dispara la interrupción por nivel en el fifo de recepción. • Control de lı́nea (Line control): Controla la codificación de las tramas: paridad, stopbits, startbits, largo de trama. Además puede forzar la salida serial a 0. 3.1. Especificación del UART 16550 13 • Control de módem (Modem control): controla las señalesde salida rts pad o, dtr pad o y pone al uart en modo loopback. • Estado de lı́nea (Line status): Indica al SoC el estado de la lı́nea: datos disponibles, error de overflow, paridad, stopbit, o trama, transmisor vacı́o. • Estado de módem (Modem status): Contiene las entradas de control al uart: cts pad i, dsr pad i, ri pad i, dcd pad i. • Registro de división (Divisor latches): Controla el divisor de reloj para la Reloj del sistema velocidad en baudios de transmisión. (Divisor del reloj = 16·Velocidad en baudios ) Figura 3.1: Diagrama de bloques del uart 16550 (Gorban, 2002) 14 3 Desarrollo 3.2 Plan de verificación El plan de verificación desarrollado con la metodologı́a UVM consta de dos partes: casos de prueba y puntos de cobertura funcional. Los casos de prueba consisten de una serie de secuencias que definen el estı́mulo que le será introducido al dut. Éstos buscan lograr que el dispositivo atraviese su espacio de estados completamente de manera eficiente (es decir, minimizando los recursos de simulación requeridos). Debido al uso de aleatorización restringida, la cantidad de casos de prueba que se deben definir es pequeña en comparación con las que se utilizarı́an en un enfoque de pruebas dirigidas. Sin embargo, esta misma aleatoriedad hace necesaria la segunda parte del plan de verificación, la cobertura funcional. Cuadro 3.1: Casos de prueba Nombre de la prueba Descripción register test.e send frame.e receive frame.e receive frame err.e loopback.e Escribir y leer todos los registros del dut Mandar tramas con todos los parámetros posibles aleatorizados. Recibir tramas con todos los parámetros posibles aleatorizados Recibir tramas con errores de paridad, stopbit, startbit. Habilitar modo loopback y enviar tramas. Comprobar que regresan. Parámetros que se aleatorizan en las pruebas de transmisión: • Número de tramas (Hasta que el fifo este vacı́o, medio y lleno) • Número de bits por trama (5,6,7 u 8) • Número de bits de parada (1 o 2) • Paridad (Par o impar) Estos casos de prueba en teorı́a bastan para verificar toda la funcionalidad del dut. En la práctica el limitante más importante es la cantidad de regresiones que se pueden correr: Pueden requerirse muchos cientos de semillas para que se dé alguna configuración especı́fica. Si en un punto avanzado del proceso de verificación hay algún estado interesante que no se ha probado, se crea una nueva prueba con más restricciones para verificar esa funcionalidad. La segunda parte del plan de verificación consiste de los puntos de cobertura. La idea es medir el porcentaje de estados que el dut ha recorrido. Esto permite determinar el progreso que se ha hecho en el esfuerzo de verificación y la necesidad de crear nuevas pruebas. Los puntos de cobertura se definieron en 2 grupos: La interfaz de uart con el SoC y las tramas que atraviesan al uart. 3.3. El ambiente de verificación 15 • Interfaz uart<->SoC cubre los registros de información del dut. Además cubre la señal de salida de interrupciones. • Interfaz out<->uart cubre las posible tramas que el dut puede enviar o recibir. Los puntos que cubren tramas ilegales sólo se evalúan con paquetes entrantes: Si el uart genera una trama errónea el test falla y se ha encontrado un defecto en el dispositivo. UART<->SoC Cuadro 3.2: Puntos de cobertura Grupo de cobertura Nombre del punto Descripción Buckets cov tx fifo level e cov rx fifo level e cov int register e tx fifo level rx fifo level intr type rx data avai tx fifo empty rx line status modem status uart int uart loobpack Uso del tx fifo Uso del rx fifo Interrupciones generadas Intr. «Datos recibidos» Intr. «Nada que transmitir» Intr. «Estado de lı́nea» Intr. «Estado de módem» Salida de intr. «int o» Modo loopback habilitado Vacı́o, 1 a 31, Lleno Vacı́o, 1 a 31, Lleno 0,1,2,3 0,1 0,1 0,1 0,1 0,1 1 parity type with parity stopbit type databit type delay to next frame legal frame has startbit err has parity err has stopbit err payload lsb payload msb payload msb payload lsb databit type stopbit type parity type cross parity type has parity err Paridad en trama de salida Trama incluye bit de paridad Cantidad de bits de parada Cantidad de bits en trama Retraso entre tramas Trama legal Error en bit de inicio Error en bit de paridad Error en bit final Bit menos significativo Bit más significativo Punto de cruce Punto de cruce Punto de cruce Par, Impar Si, no 1,2 5,6,7,8 0 a 255 en grupos de 64 Si, no Si, no Si, no Si, no Si, no Si, no cov int mask register e OUT<->UART cov uart int e cov uart loopback mon frame done Por último, además de la cobertura funcional definida manualmente, en la regresión se recopilará cobertura de código, para disponer de una métrica con la cual evaluar el esfuerzo de verificación. 3.3 El ambiente de verificación El ambiente de verificación utilizado en este proyecto se basa en un ejemplo desarrollado por Cadence para demostrar la implementación de uvm en el lenguaje Specman e. Este se compone de 3 uvcs. Se instancian bajo el ambiente principal, son interconectados y configurados para trabajar en conjunto. La funcionalidad se divide en la interfaz con el SoC, la interfaz uart, y las caracterı́sticas particulares del uart 16550. Para implementar las pruebas requeridas por el plan de verificación se expandió la capacidad de generación de estı́mulo y recolección de cobertura. 16 3 Desarrollo Figura 3.2: Diagrama del ambiente de verificación (Cadence, 2012) APB Interface UVC El APB Interface UVC modela el resto del SoC al que está conectado el uart. Éste se conecta directamente con la interfaz wishbone del rtl y se utiliza para realizar operaciones de escritura y lectura a los registros del DUT. Las secuencias que provee son del más bajo nivel, es decir, escribir o leer a una dirección del espacio de memoria. Estas secuencias son utilizadas por el uart Module UVC para crear secuencias de más alto nivel, como «configurar uart para recibir tramas de 6 bytes a 9600 baudios por segundo». La interfaz wishbone usa un esquema maestro-esclavo para manejar transacciones. En un SoC el uart funciona como un esclavo, por lo que se instancia un agente maestro del APB. Éste agente maestro usa un bfm para estimular al dut. Además de esto, se instancia un agente esclavo, que monitorea el bus para detectar las repuestas del uart. Además de generar estı́mulo, esta interfaz implementa un evaluador de señales. De esta manera se verifica que el wishbone esté implementado correctamente en el dut. 3.3. El ambiente de verificación 17 UART Interface UVC El uart Interface uvc es un análogo del componente anterior. En este caso modela un dispositivo con interfaz serial que es conectado al dut. Se instancian dos agentes: uno funciona como emisor y el otro como receptor. El emisor puede ser configurado para enviar tramas con errores, como el bit de paridad, de parada o de inicio. Esto permite observar el comportamiento del uart cuando se le conecta un dispositivo errático. El receptor se limita a detectar errores en las tramas, y levantar un error cuando el dut se comporta de manera inesperada. En este nivel, como en en APB Interface UVC, se realiza verificación al nivel de señales solamente. UART Module UVC Este módulo es el director de orquesta en la verificación a nivel de bloque. Cumple una gran cantidad de tareas bastante diversas, cada una vital para la ejecución efectiva de la pruebas del plan de verificación. En primer lugar, instancia y configura a los interface uvcs, de manera que éstos funcionen de manera compatible con el dut. Cuando las diferentes partes del ambiente son instanciadas, reciben un paquete de datos llamado config. Éste indica el nombre de las señales a monitorear en el rtl (utilizado por los bfm), el scoreboard al que los monitores deben pasar las transacciones generadas, y el estado del dut: Velocidad en baudios de la interfaz, paridad, cantidad de stopbits, cantidad de startbits, etcétera. En segundo lugar, define un número de secuencias estándar que las pruebas del plan pueden usar para generar el estı́mulo apropiado. Estas van desde una secuencia que configura los registros del uart, a escribir al registro fifo de salida para enviar una serie de paquetes, a leer del fifo de llegada, a añadir una trama a la cola de envı́o del agente emisor uart. En tercer lugar, ejecuta la verificación a nivel de transacciones. Esto se hace a través de una tabla de verificación que recibe transacciones del apb y del uart, y verifica que todos los paquetes enviados y recibidos del dut lleguen a su destino final. Tres elementos claves estan operando en este proceso: Cuando el apb Interface uvc escribe al fifo de salida, o lee del fifo de llegada, genera una transacción apb que se pasa al module uvc. El uart Interface uvc genera transacciones uart que también se pasan al module uvc cuando recibe o envı́a una trama. Por último, el module uvc define una serie de reglas para emparejar las transacciones apb con las transacciones uart y verificar que el paquete haya sido transmitido de manera correcta. En cuarto y último lugar, el uart module uvc define los puntos de cobertura con los que se evaluará el desempeño de las pruebas del plan de verificación. 18 3 Desarrollo Esto se hace definiendo una serie de puntos claves en las secuencias; cada vez que son tocados indican al simulador que recopile la información indicada del ambiente. 3.4 Implementación del plan de verificación Para implementar las pruebas requeridas por el plan de verificación se expandió la capacidad del ejemplo de Cadence en 3 áreas diferentes: En verificación, se expandió la tabla de verificación implementada para que identificara transacciones de loopback. En cobertura, se modificó la implementación del uart module e interface uvc para que incorporara los puntos indicados por el plan de verificación. Por último, se crearon las pruebas para generar el estı́mulo que requerı́a el plan. Verificación del DUT La verificación de transacción de paquetes se realiza en el uart module uvc mediante una tabla de verificación. Esta recibe transacciones de tipo uart y de tipo apb. Estas se emparejan dependiendo de la prueba que está siendo aplicada: Cuando se envı́a una trama, se genera una transacción apb al escribir al fifo de salida del uart. Esta es añadida a la tabla. Cierto tiempo después, el monitor del uart interface uvc detecta actividad en el dut y genera una transacción uart. La tabla recibe la segunda transacción e intenta emparejarla con las transacciones que ya ha recibido. Si coinciden la transmisión fue exitosa. En las pruebas de receive frame, se crea una transacción uart que se añade a la tabla. Al leer el fifo de entrada del dut, el monitor de la interfaz apb genera una transacción apropiada que la tabla de verificación empareja. Si coinciden la recepción fue exitosa. Por último, en la prueba loopback se emparejan dos transacciones del tipo apb. //uart_ctrl_scoreboard.e unit uart_ctrl_scoreboard like uvm_scoreboard { //Un port es el punto donde las transacciones son recibidas por la tabla de verificación //receive_frame y receive_frame_err scbd_port uart_frame_add : add uart_frame_s; scbd_port apb_trans_match : match apb_trans_s; //send_frame scbd_port apb_trans_add : add apb_trans_s; scbd_port uart_frame_match : match uart_frame_s; //Se a~ nade un UART frame al scoreboard: receive_frame y receive_frame_err uart_frame_add_predict(item : uart_frame_s) is only { add_to_scbd(item); set_match_port(item, apb_trans_match); 3.4. Implementación del plan de verificación 19 message(LOW,"Adding the UART Frame into the scoreboard ") { print item using hex; }; }; //Se a~ nade un APB frame al scoreboard: loopback y send_frame apb_trans_add_predict(item : apb_trans_s) is only { //Se descartan escrituras que no sean al FIFO de salida del UART if(item.addr == base_addr + UART_TX_FIFO and item.direction == WRITE) { //Si la transacción es loopback, se empareja con una transacción APB, si no, UART if (item.loopback_transaction) { add_to_scbd(item); set_match_port(item, apb_trans_match); message(LOW,"Adding the APB Frame into the scoreboard ") { print item using hex; }; } else { add_to_scbd(item); set_match_port(item, uart_frame_match); message(LOW,"Adding the APB Frame into the scoreboard ") { print item using hex; }; }; }; }; // apb_trans_add_predict() //Método para emparejar transacciones APB: loopback y receive_frame apb_trans_match_reconstruct( item : apb_trans_s) is only { if(item.addr == base_addr + UART_RX_FIFO and item.direction == READ) { match_in_scbd(item); message(LOW,"Looking for APB Frame match into the scoreboard ") { print item using hex; }; }; }; //Método para emparejar transacciones UART: send_frame uart_frame_match_reconstruct( item : uart_frame_s) is first {; message(LOW,"Looking for UART Frame match into the scoreboard ") { print item using hex; }; }; //Por último, este método se llama para determinar si 2 transacciones coinciden: en este caso //se utilizan los datos transmitidos para comparar. compute_key(item : any_struct ): uint is only { var data_list: list of bit; if item is a apb_trans_s then { data_list = pack(NULL, item.as_a(apb_trans_s).data); }; if item is a uart_frame_s then { data_list = pack(NULL, item.as_a(uart_frame_s).payload); }; data_list.resize(data_len, TRUE, 0, TRUE); data_list.resize(8, TRUE, 0, TRUE); return data_list.crc_32(0, 1); }; }; 20 3 Desarrollo Recolección de cobertura La recolección de cobertura se da en dos de los módulos: el interface y module uart uvcs. El interface UVC procesa las tramas que son enviadas y recibidas por el uart. En este módulo se implementó el punto de cobertura mon frame done, en dos versiones diferentes. La primera recoge información de tramas que son enviadas al dut, por lo que recopila información de errores inyectados con los ı́tems has startbit err, has stopbit err, has parity err. La segunda versión los omite, ya que cubre las tramas emitidas por el dut: un error en éstas muestra un defecto en el rtl. Además no cubre el retardo entre tramas, que es constante en el caso del dut. //uart_coverage.e extend TX_AGENT uart_monitor_u { cover mon_frame_done_tx using per_unit_instance is { //Aquı́ se declara el punto de cobertura // Protocolo item stopbit_type:uart_frame_stopbit_t=current_frame.stopbit_type; //Aquı́ se declaran los ı́tems asociados. item parity_type:uart_frame_parity_t=current_frame.parity_type using ignore= parity_type==SPACE; item databit_type:uart_frame_databit_t=current_frame.databit_type; item with_parity:bool=current_frame.with_parity; // Retardo de trama item delay_to_next_frame: uint(bits :8)=current_frame.delay_to_next_frame ranges = {range ([0 .. 255],"",64) },at_least = 10; using // Inyección de errores item legal_frame:bool=current_frame.legal_frame; item has_startbit_err:bool=current_frame.has_startbit_err; item has_parity_err:bool=current_frame.has_parity_err; item has_stopbit_err:bool=current_frame.has_stopbit_err; // Datos enviados item payload_lsb: bit = current_frame.payload[0]; item payload_msb: bit = current_frame.payload[current_frame.p_len-1]; // Puntos de cruce cross payload_msb, payload_lsb; cross databit_type, stopbit_type, parity_type; cross parity_type, has_parity_err using ignore = parity_type==NONE; }; }; extend RX_AGENT uart_monitor_u { cover mon_frame_done_rx using per_unit_instance is { // Protocolo item parity_type:uart_frame_parity_t=current_frame.parity_type using ignore= parity_type==SPACE; item stopbit_type:uart_frame_stopbit_t=current_frame.stopbit_type; item databit_type:uart_frame_databit_t=current_frame.databit_type; item with_parity:bool=current_frame.with_parity; // Datos enviados item payload_lsb: bit = current_frame.payload[0]; item payload_msb: bit = current_frame.payload[current_frame.p_len-1]; // Puntos de cruce cross payload_msb, payload_lsb; cross databit_type, stopbit_type, parity_type; }; }; 3.4. Implementación del plan de verificación 21 Se observa que la distinción entre trama saliente o entrante depende del agente en que el monitor ha sido instanciado: RX AGENT observa los paquetes emitidos por el dut, y TX AGENT los que emite el testbench. Sin importar el agente, el monitor detecta de manera automática actividad en los puertos y determina cuando una trama se ha completado para recolectar la cobertura: //uart_monitor_h.e unit uart_monitor_u like uvm_monitor { //Eventos de cobertura event mon_frame_done_tx; event mon_frame_done_rx; //Cuando el monitor detecta el final de una trama, emite el evento mon_frame_ended. //Este a su vez emite el evento que realiza la cobertura on mon_frame_ended { frame_done(current_frame); emit current_frame.frame_done; emit mon_frame_done_tx; emit mon_frame_done_rx; }; }; El module UVC se encarga de la cobertura de los registros y algunas señales del uart 16550. Esta cobertura no se realiza mediante un monitor; se accesan de manera directa las señales internas de rtl. Esto permite recolectar información de cobertura más precisa y de una manera más simple. Al no limitarse a usar la interfaz externa del bloque, se rompe con el modelo de caja negra, reduciendo la portabilidad del código. También queda atado el ambiente a algunos detalles de la implementación. En esta situación, se decidió que la reducción en el tiempo de implementación ameritaba el uso de esta técnica. //uart_ctrl_cover.e unit uart_ctrl_env_cover_u { // Tx FIFO level //Primero se declara un puerto que se conecta al RTL specificado por hdl_path() tx_fifo_level_p: in simple_port of uint(bits:5) is instance; keep bind(tx_fifo_level_p, external); keep soft tx_fifo_level_p.hdl_path() == "regs.transmitter.tf_count"; //Luego se asocia el evento de cobertura al puerto creado event cov_tx_fifo_level_e is change(tx_fifo_level_p$) @sim; //Finalmente se indican los ı́temes a cubrir cover cov_tx_fifo_level_e using per_unit_instance is { item tx_fifo_level : uint(bits:7) = tx_fifo_level_p$ using ranges = { range([0], "Empty"); range([1..14], "1 to 14"); range([15], "FIFO full"); }; }; // Rx FIFO level rx_fifo_level_p: in simple_port of uint(bits:5) is instance; keep bind(rx_fifo_level_p, external); keep soft rx_fifo_level_p.hdl_path() == "regs.receiver.rf_count"; event cov_rx_fifo_level_e is change(rx_fifo_level_p$) @sim; cover cov_rx_fifo_level_e using per_unit_instance is { 22 item rx_fifo_level ranges = { range([0], range([1..14], range([15], }; }; 3 Desarrollo : uint(bits:7) = rx_fifo_level_p$ using "Empty"); "1 to 14"); "FIFO full"); // Interrupt Channel Status Reg int_register_p: in simple_port of uint(bits:4) is instance; keep bind(int_register_p, external); keep soft int_register_p.hdl_path() == "regs.iir"; event cov_int_register_e is change(int_register_p$) @sim; cover cov_int_register_e using per_unit_instance is { item intr_1 : uint(bits : 1) = int_mask_register_p$[0:0]; item intr_2 : uint(bits : 1) = int_mask_register_p$[1:1]; item intr_3 : uint(bits : 1) = int_mask_register_p$[2:2]; item intr_4 : uint(bits : 1) = int_mask_register_p$[3:3]; }; // Interrupt Mask Reg int_mask_register_p: in simple_port of uint(bits:4) is instance; keep bind(int_mask_register_p, external); keep soft int_mask_register_p.hdl_path() == "regs.ier"; event cov_int_mask_register_e cover cov_int_mask_register_e item rx_data_avai : bit = item tx_fifo_empty : bit = item rx_line_status : bit = item modem_status : bit = }; is change(int_mask_register_p$) @sim; using per_unit_instance is { int_mask_register_p$[0:0]; int_mask_register_p$[1:1]; int_mask_register_p$[2:2]; int_mask_register_p$[3:3]; // Top level interrupt signal uart_int_p: in simple_port of bit is instance; keep bind(uart_int_p, external); keep soft uart_int_p.hdl_path() == "int_o"; event cov_uart_int_e is change(uart_int_p$) @sim; \\$ cover cov_uart_int_e using per_unit_instance is { item uart_int: bit = uart_int_p$; \\$ }; // Loopback Mode enabled //En este caso, en lugar de declarar un puerto, se conecta el evento //de manera directa al RTL event cov_uart_loopback is change(’~/top.uart_dut.regs.loopback’) @sim; cover cov_uart_loopback using per_unit_instance is { item uart_loopback: bit = ’~/top.uart_dut.regs.loopback’ using ignore = uart_loopback == 0; }; }; La cobertura se recolecta cada vez que cambia el estado de una de las señales observadas. Implementación de las pruebas Las pruebas del plan de verificación se implementan mediante un archivo de prueba. En uvm, este tiene la capacidad de cambiar la configuración de los diferentes módulos, generar secuencias, y crear estı́mulo de manera directa. Las primeras etapas de ejecución son comunes a todas las pruebas. 3.4. Implementación del plan de verificación 23 En primer lugar, se saca al dut de reset. Esto lo realiza de manera automática el apb interface uvc: \\apb_master_bfm.e tf_reset() @tf_phase_clock is also { message(LOW, "Starting RESET phase "); p_smp.sig_penable$ = 0; \\$ }; tf_hard_reset() @tf_phase_clock is also { message(LOW, "Starting HARD_RESET phase "); p_smp.sig_presetn$ = 0; \\$ }; tf_init_dut() @tf_phase_clock is also { p_smp.sig_presetn$ = 1; \\$ }; Los métodos tf reset, tf hard reset y tf init dut son llamados de manera automática por el simulador. Esto permite que todos los interface uvcs salgan de reset simultáneamente. En el caso de este proyecto, sólo el apb interface uvc controla el reset del dut. Una vez afuera del reset, se inicia la prueba. Ésta procede a configurar el ambiente: \\<nombre del test>.e extend sys { // Eventos de sincronización event VR_SB_uart_config_done; event VR_SB_uart_data_done; event VR_SB_uart_read_done; event VR_SB_uart_div_config_done; event reset_done is @uart_ctrl_sve.uart_sync.reset_ended; // Duración máxima del test setup() is also { set_config(run, tick_max,MAX_INT); }; }; «sys» es el ambiente global. Los eventos declarados en él se usan para señalizar el progreso de la prueba entre métodos. No todas las pruebas usan todos los eventos. Además de eventos, se configura la duración máxima de la prueba, para prevenir ciclos infinitos. \\<nombre del test>.e extend uart_env_config { keep parity_type in [ODD, EVEN, NONE]; keep stopbit_type in [ONE,TWO]; keep databit_type in [FIVE,SIX,SEVEN,EIGHT]; keep parity_type == NONE => with_parity == FALSE; //Restricciones interesantes keep databit_type == FIVE => stopbit_type == ONE; keep with_parity == TRUE; }; «uart env config» guarda la configuración del dut. Éste se pasa a los diferentes monitores y bfms para que generen las tramas apropiadas. Se pueden observar 2 restricciones interesantes cuyo propósito no resulta obvio: 24 3 Desarrollo • «with parity == TRUE»: Esta restricción sólo se añade en el caso de la prueba receive frame err.e, ya que para insertar un error de paridad se necesita que haya un bit de paridad en primer lugar. • «databit type == FIVE =>stopbit type == ONE»: El dut presenta un caso esquina: cuando las tramas son de cinco bits, configurar dos bits de parada hace se generen 1.5 bits de parada. Esto causa que los monitores se desincronizen y generen errores de manera esporádica. Una vez configurado el ambiente, es necesario configurar al dut de la misma manera. La prueba usa al apb interface uvc para escribir a los registros del uart y configurarlo. extend MAIN !div_lsb !div_msb !lcr !mdm MAIN_TEST apb_master_sequence { : WRITE apb::apb_master_transaction; : WRITE apb::apb_master_transaction; : WRITE apb::apb_master_transaction; : WRITE apb::apb_master_transaction; config : uart_env_config; keep config == get_enclosing_unit(uart_ctrl_sve_u).uart_if.config; !parity_sel !parity_val !stopbit_val !datalen_val !data_value : : : : : uint(bits:1); uint(bits:1); uint(bits:1); uint(bits:2); uint(bits:32); body() @driver.clock is only { wait [400] * cycle; driver.wait_for_grant(me); //Se escribe la máscara de interrupciones al registro de control de interrupciones gen trans keeping { it.addr == UART_INTR_EN_REG; it.data == intr_mask; it.direction == WRITE; }; driver.deliver_item(trans); driver.wait_for_item_done(me); //Se escribe al line_control_register para poder escribir al divisor de reloj. gen trans keeping { it.addr == UART_LINE_CNTRL_REG; it.data == 32’h83000000; it.direction == WRITE; }; driver.wait_for_grant(me); driver.deliver_item(trans); driver.wait_for_item_done(me); //Se escribe al divisor de reloj. do div_msb keeping { it.addr == UART_DIVISOR_MSB_REG; it.data == 0; }; do div_lsb keeping { it.addr == UART_DIVISOR_LSB_REG; it.data == 1; }; 3.4. Implementación del plan de verificación 25 //Se escribe al registro de control de modem. Esto habilita la prueba de loopback do mdm keeping { it.addr == UART_MODEM_CNTRL_REG; it.data == 32’h10; \\ ó 32’h00; }; //Por último se lee la configuración del ambiente para generar el valor a escribir al line_control_register var stop_bits := config.stopbit_type; var parity := config.parity_type; var data_len := config.databit_type; case (parity) { EVEN : { parity_sel ODD : { parity_sel SPACE: { parity_sel NONE : { parity_sel }; = = = = 1’b1; 1’b1; 1’b0; 1’b0; parity_val parity_val parity_val parity_val = = = = 1;}; 0;}; 0;dut_error("UNSUPPORTED PARITY");}; 0;}; case (stop_bits) { ONE : { stopbit_val = 0}; TWO : { stopbit_val = 1}; default : { }; }; case (data_len) { SIX : { datalen_val = 1}; SEVEN : { datalen_val = 2}; EIGHT : { datalen_val = 3}; }; data_value = pack(packing.high, 3’b0, parity_val , parity_sel, stopbit_val, datalen_val , 24’b0); do lcr keeping { it.addr == UART_LINE_CNTRL_REG; it.data == data_value ; }; wait [100] * cycle; //Se emite este evento para indicar que el DUT está configurado y la prueba puede proceder. emit sys.VR_SB_uart_config_done; }; }; Esta última secuencia varı́a entre pruebas, ya que cada una necesita configurar al dut de manera distinta. La estructura sin embargo se conserva. Para el caso de register test, la prueba termina en este punto. Ésta escribe a todos los registros con valores seleccionados al azar. Las demás pruebas divergen lo suficiente para que se estudien por separado. Loopback La prueba de loopback no utiliza uart interface uvc. Esta se limita a escribir al fifo de salida del uart, esperar un perı́odo de tiempo, y leer el fifo de entrada. //Esta es la secuencia principal de la prueba. Se limita a esperar a que la //secuencia maestra del APB termine para terminar la prueba. extend MAIN uart::uart_sequence { body() @driver.clock is only { driver.raise_objection(TEST_DONE); 26 3 Desarrollo wait @sys.VR_SB_uart_config_done; wait @sys.VR_SB_uart_read_done; driver.drop_objection(TEST_DONE); }; }; //Esta es la secuencia maestra del APB. Cuando termina la prueba, emite sys.VR_SB_uart_read_done extend MAIN MAIN_TEST apb_master_sequence { !fifowr: WRITE apb::apb_master_sequence; !fiford: READ apb::apb_master_sequence; body() @driver.clock is also { wait @sys.VR_SB_uart_config_done; do fifowr keeping { it.addr == 0; it.loopback_transaction == TRUE; }; wait [10000]; for i from 0 to tx_count { do fiford keeping { it.addr == 0; }; }; emit sys.VR_SB_uart_read_done; }; }; fifowr es una secuencia que escribe una serie de paquetes al fifo de salida del uart. El campo loop indica que la transacción es loopback, por lo que la tabla de verificación del module uvc la emparejará con otra transacción apb en lugar de una transacción uart. fiford lee del fifo de entrada del dut. Las transacciones generadas se emparejarán con las de fifowr. Si todas se correponden apropiadamente, la prueba pasa de manera exitosa. Send frame Send frame verifica que las tramas viajen correctamente del SoC a un dispositivo externo. extend MAIN uart::uart_sequence { body() @driver.clock is only { driver.raise_objection(TEST_DONE); wait @sys.VR_SB_uart_config_done; wait @sys.VR_SB_uart_data_done wait [10000]; driver.drop_objection(TEST_DONE); }; }; extend MAIN MAIN_TEST apb_master_sequence { !fifowr: WRITE apb::apb_master_sequence; body() @driver.clock is also { wait @sys.VR_SB_uart_config_done; for i from 0 to tx_count { do fifowr keeping { it.addr == 0; }; }; emit @sys.VR_SB_uart_data_done; }; }; 3.5. Análisis de resultados 27 Esta prueba usa al apb interface uvc para escribir al fifo de salida del uart. Como éste no esta configurado en modo loopback, empezará a transmitir las tramas por el puerto de salida. Éstas son captadas por el monitor del uart interface uvc. El monitor genera transacciones uart que son emparejadas con las transacciones apb que se crearon al escribir al fifo. Se verifica que estas coincidan para que la prueba pase. Receive frame y receive frame err Por último, estos tests configuran al uart interface uvc para emitir tramas. extend MAIN uart::uart_sequence { body() @driver.clock is only { driver.raise_objection(TEST_DONE); wait @sys.VR_SB_uart_config_done; for i from 0 to tx_count { do frame keeping { it.legal_frame == FALSE; it.has_startbit_err == FALSE; it.has_parity_err == FALSE; //Este campo es TRUE para recieve_frame_err it.has_stopbit_err == FALSE; }; }; emit sys.VR_SB_uart_data_done; wait @sys.VR_SB_uart_read_done; driver.drop_objection(TEST_DONE); }; }; extend MAIN MAIN_TEST apb_master_sequence { !fiford: READ apb::apb_master_sequence; body() @driver.clock is also { wait @sys.VR_SB_uart_data_done; for i from 0 to tx_count { do fiford keeping { it.addr == 0; }; }; emit sys.VR_SB_uart_read_done; }; }; El comando «do frame» genera una trama uart con las propiedades especificadas. Ésta es añadida a la cola de emisión del bfm y finalmente emitida. El monitor uart detecta esta transmisión, genera una transacción uart y la añade a la tabla de verificación. Luego se ejecuta la secuencia fiford, que lee el fifo de entrada del dut. El monitor apb genera transacciones apb que son emparejadas con las transacciones añadidas previamente. Si los datos difieren, se genera un error. 3.5 Análisis de resultados Para la evaluación final del dut, se corrió cada prueba 500 veces, con una semilla diferente cada vez. Esto da un total de 2500 escenarios posibles usados 28 3 Desarrollo para evaluar el uart. Para un producto comercial, estas pruebas se corren durante muchos meses, con miles de semillas diferentes, para reducir el riesgo de defectos no detectados en el rtl a la hora de fabricar el SoC. La regresión realizada se vio limitada principalmente por el espacio en disco que ocupan los datos de cobertura. Cada ejecución de una prueba genera 10 MiB de datos, para un total de 23 GiB. Por último, esta regresión tomó 41 horas para completarse. En cada corrida se recopiló información de la cobertura funcional obtenida, ası́ como de la cobertura de código. Ninguna prueba detectó un error en el dut, como es de esperarse en un rtl que ha estado en circulación desde el año 2002. Sin embargo, los datos de cobertura obtenidos permitirán evaluar el nivel de éxito obtenido en la implementación del plan de verificación: Una alta cobertura funcional indica que las pruebas desarrolladas ejercitan apropiadamente los estados interesantes del dut. Una alta cobertura de código indica que el plan de verificación en sı́ es minucioso. La verificación es una disciplina que intenta reducir el riesgo al máximo con la mı́nima cantidad de recursos. La información de cobertura recopilada permite identificar pruebas con alto desempeño, que proveen alta cobertura con tiempos de ejecución bajos. Desempeño por prueba register test Esta prueba es la más rápida de las pruebas implementadas. Al no tener que simularse el intercambio de datos con un dispositivo, este tipo de pruebas se desarrollan temprano en el ciclo de verificación de rtl. En promedio cada corrida de esta prueba toma 45 segundos. En un ambiente de producción, con varios diseñadores y verificadores produciendo código para el ambiente de verificación, esto resulta muy útil. Sirve como una prueba de sanidad para el testbench y el dut, permite determinar rápidamente si un cambio introducido ha quebrado el ambiente de verificación. La cobertura funcional obtenida con las 500 corridas se puede observar en la figura 3.3. Se logró una cobertura del 88 % en la interfaz uart<->SoC, que corresponde a los registros en los que se enfoca la prueba. No se llegó a cubrir un 100 % debido a la cantidad limitada de pruebas que se ejecutaron. En la interfaz out<->uart se dio una cobertura del 0 % para tramas entrantes y salientes como se esperaba. La cobertura de código (3.4) obtenida revela una de las limitaciones de este enfoque. Sólamente sacar al dut de reset ejercita una gran parte del rtl, por lo que rápidamente se obtiene valores «altos» como el 72 % en este caso. 3.5. Análisis de resultados 29 Figura 3.3: Cobertura funcional register test Estimular 30 % restante resulta mucho más trabajoso que lo que indican los primeros esfuerzos. A pesar de las limitaciones mencionadas en los antecedentes de la cobertura de código, esta métrica resulta útil para comparar el desempeño de una prueba contra otra, y el progreso que ha tenido la regresión. Se analizarán los resultados acumulados por prueba, y los resultados acumulados totales. 30 3 Desarrollo Figura 3.4: Cobertura de código register test send frame Esta prueba configura al dut para enviar una cantidad de tramas, variando la información enviada, ası́ como la paridad, startbit y stopbit. El uart se configura solamente una vez por prueba, por lo que estos últimos tres rubros no varı́an durante la ejecución de una misma prueba. La prueba utiliza dos secuencias separadas. En la primera se configura el dut, en la segundo se escriben las tramas a enviar al fifo de salida del uart mediante el apb interface uvc. Paralelo a esto, el uart interface uvc está monitoreando la salida del dut. La escritura genera una transacción que el module uvc añade al scoreboard, y el interface uvc en la salida genera la segunda transacción que es emparejada. Si los datos difieren, se ha encontrado un error en el rtl. Esta prueba dura un promedio de 56 segundos ejecutándose. La carga de las herramientas de simulación consume 40 segundos del tiempo total de simulación. Por eso esta prueba tarda simulando veces más que la ejecución de register test. Los datos de cobertura funcional obtenidos son: Se puede observar inmediatamente que esta prueba se desempeña mejor que register test. Los mismos registros son ejercitados, y ahora se cubren los puntos relacionados a la emisión de tramas. El punto que no se cubre com- 3.5. Análisis de resultados 31 Figura 3.5: Cobertura funcional send frame pletamente es el cruce de tramas de cinco bits con dos bits de parada. Esto se debe al caso esquina mencionado previamente: Estas dos opciones causan que el dut use 1.5 bits de parada en lugar de dos. El monitor uart genera transacciones incorrectas, por lo que el ambiente da un falso positivo. La mejora en la cobertura de código es menos drámatica. Salta a la vista como uart transmitter sube de un 25 % en register test a un 92 %. Se puede decir que la prueba es muy efectiva en el área que se querı́a probar. 32 3 Desarrollo Figura 3.6: Cobertura de código send frame receive frame y receive frame err Esta prueba envı́a una cantidad de tramas al uart, variando la información enviada, ası́ como la paridad, startbit y stopbit. El uart se configura solamente una vez por prueba, por lo estos últimos 3 rubros no varı́an durante la ejecución de una misma prueba. La prueba utiliza tres secuencias separadas. En la primera se configura el dut, en la segunda se envı́an las tramas al uart mediante su interface uvc, y en la tercera se lee el fifo de llegada con el apb interface uvc. El envı́o de la trama genera una transacción que el module uvc añade al scoreboard, y la lectura del fifo genera la segunda transacción que es emparejada. Si los datos difieren, se ha encontrado un error en el rtl. En el caso de tramas con errores, el uart genera una interrupción. El scoreboard sólo compara el contenido de la trama. Esta prueba dura un promedio de 58 segundos ejecutándose. Los datos de cobertura funcional obtenidos son: 3.5. Análisis de resultados 33 Figura 3.7: Cobertura funcional receive frame y receive frame err La cobertura de estas pruebas es similar a la cobertura que logra send frame, excepto que para este caso se extiende la cobertura en la recepción de tramas. Un hueco en la verificación es que solamente se están generando errores de paridad en el test receive frame err. Esto se debe a una limitación del monitor del uart: si el bit de parada o el bit de inicio son errónos, el monitor se desincroniza y no genera la transacción con los datos apropiados. Esto llega a la tabla de verificación y causa un falso positivo. Esta situación muestra una de las limitaciones del método de tabla de verificación. La inyección de errores requiere de una implementación mucho más compleja que otras metodologı́as. Además se sacrifica la modularidad del monitor, ya que éste debe ser informado de los casos especiales por el ambiente. Para propósito de este proyecto se consideró que implementar la inyección de errores de bit de paridad era suficiente para obtener una cobertura aceptable, es decir, superior al 90 %. La cobertura de código muestra la mejora en la verificación de uart reciever, que sube de un 29 % a un 85 %. 34 3 Desarrollo Figura 3.8: Cobertura de código receive frame y receive frame err loopback La prueba de loopback configura al uart para que devuelva al SoC todos los paquetes transmitidos. Esto se logra mediante el registro de control de módem, que deshabilita los puertos externos del dut y conecta internamente el emisor con el receptor. Esta prueba se implementa con dos secuencias que controlan al apb interface uvc. En primer lugar se escribe al fifo de salida del uart, se espera un perı́odo de tiempo prudencial, y se lee del fifo de entrada. Para implementar esta prueba se requirió modificar la tabla de verificación, para que emparajera transacciones apb unas con otras. Los datos de cobertura funcional obtenidos son: 3.5. Análisis de resultados 35 Figura 3.9: Cobertura funcional loopback Se observa que no hay actividad en la interfaz del uart con el exterior. Además, el punto de cobertura cov uart loopback detectó que sı́ se habilitó el modo loopback. Sin embargo, es necesario notar que en el análisis para las pruebas anteriores también este punto de cobertura estaba al 100 %. Esto se debe a un error en la implementación: la señal interna del dut usada para determinar esta funcionalidad se activa al azar cuando se saca al dispositivo de reset, dando falsos positivos. La cobertura de código muestra una situación interesante: el módulo de transmisión y el módulo de recepción alcanzaron una cobertura del 96 % y el 85 % respectivamente. Esto parece indicar que la funcionalidad de loopback implementada se limita a conectarlos internamente, lo que consume poder de manera innecesaria. 36 3 Desarrollo Figura 3.10: Cobertura de código loopback Cobertura general Por último, se analizó los datos de cobertura combinados, para determinar la eficacia general del ambiente. En resumen, la cobertura funcional del dut llegó a un 91 %, y la cobertura de código a un 86 %. Cobertura funcional En lo que respecta a la interfaz uart<->SoC, se logró un 100 % de cobertura. Un resultado tan alto con una regresión tan pequeña indiqua que la cobertura puede ser expandida para obtener información más detallada de los estados del dut. Esta idea se refuerza con los resultados de cobertura de código que se verán más adelante. Un ejemplo para expandir la cobertura es incluir los registros de configuración del uart. Esto puede parecer redundante, ya que en la cobertura de la interfaz out<->uart se recorren todos los tipos de tramas posibles. Sin 3.5. Análisis de resultados 37 embargo, queda como recomendación el realizar este cambio. Figura 3.11: Cobertura funcional uart<->SoC En el segundo rubro, out<->uart, se logró una cobertura del 95 % en general, con 92 % para paquetes entrantes y 98 % para paquetes salientes. Los huecos que quedaron en la cobertura de paquetes entrantes se deben a que no se generaron errores de bit de inicio o bit de parada. Como se mencionó previamente, el monitor del uart no puede distinguir el tipo de error que se genera, creando transacciones incorrectas que terminan la ejecución de la prueba. Es posible corregir esto cambiando la implementación del monitor y la forma en la que éste interactúa con el ambiente. El hueco en paquetes salientes se debe que no se generaron tramas de cinco bits con dos bits de parada. Como se mencionó previamente, esto se debe al comportamiento particular del dut que sólo genera 1.5 bits de parada. 38 3 Desarrollo Figura 3.12: Cobertura funcional out<->uart Cobertura de código La herramienta de cobertura de código utilizada recopiló información de 5 categorı́as distintas: • Ejecución de bloque: Número de lı́neas en el módulo que se han ejecutado. • Ejecución de expresión: Número de valores booleanos que a tomado cada expresión. • Cambio de bit: Cantidad de bits en el rtl que han cambiado su estado. • Máquina de estados finita (estados): Evalúa la cantidad de estados posibles en los que han estados las fsm internas. • Máquina de estados finita (transiciónes): Evalúa las transiciones entre estados que se han dado en las fsm internas. La cobertura de código es una herramienta usada principalmente por el diseñador. Con ésta se puede ver directamente qué partes de la implementación han sido evaluadas, desde un punto de vista meramente programático y no funcional. Gracias a esto, el diseñador puede ver que partes del diseño no están recibiendo la suficiente atención e indicarle al verificador qué nuevas 3.5. Análisis de resultados 39 pruebas deben aplicarse. Como el verificador en principio no deberı́a conocer los detalles de la implementación, la cobertura de código le resulta una métrica menos útil. La cobertura de código tiene dos categorı́as. En cobertura de módulo, se agrupa la información por cada definición de módulo, sin importar cuantas veces este fue instanciado en el diseño. Esto permite analizar el desempeño de cada bloque separado de su contexto. Figura 3.13: Cobertura de código por módulo 40 3 Desarrollo En este rubro se observa que solo tres módulos utilizan máquinas de estado: el transmisor, el receptor y la interfaz wishbone. En el transmisor la cobertura llega al 100 %, en el receptor llega al 93 %, y en el wisbone llega al 78 %. En el último caso, hubo un estado al que nunca se entró. El diseñador serı́a capaz de determinar la funcionalidad de este estado y sugerir una prueba para verificarlo. En el caso del receptor, no se dieron tres transiciones de estado: del estado sr push a sı́ mismo, de sr push a sr rec start, y se sr rec start a sr idle. Este nivel de detalle requiere un conocimiento detallado de cómo funciona la máquina de estado. Es posible que con una regresión mayor se puedan llegar a cubrir estos puntos. El punto de cobertura más bajo está en el fifo de salida, que llega a sólo un 72 %. Esto se debe a que no se llegó a causar una condición de sobrellenado en él, porque la prueba falla en esos casos. Es responsabilidad del SoC no intentar enviar más datos de los que el uart es capaz de manejar. La cobertura de bloque y expresión se analiza con la segunda categorı́a de cobertura de código: Figura 3.14: Cobertura de código por instancia 3.5. Análisis de resultados 41 En la cobertura de código por instancia, se recopila información por cada módulo declarado dentro del rtl. Para ciertos módulos que se reutilizan varias veces en un diseño, como registros, sumadores, muxes, la cobertura por instancia permite observar cuánto se ha ejercitado el código en un punto especı́fico. Para el caso de este uart, la reutilización de módulos se limita principalmente a los registros. En general la cobertura por instancia alcanzó un 89 %, con una baja varianza. No hay módulos que tengan una cobertura particularmente baja, lo que indica que mejorar esta cobertura es cuestión de prolongar la regresión. Un diseñador podrı́a sugerir algunas pruebas especı́ficas para estimular casos esquina. La cobertura de bloque más baja se da para la interfaz wishbone, con un 84 %. Esto se debe a que, siendo el único bloque en bus apb, la señal de selección no varı́a, omitiendo la lógica que maneja esta situación. Este tipo de situaciones se verifican en un ambiente de verificación sistema, no un ambiente de bloque. La cobertura de expresión más baja se da en uart dut.regs (85 %), donde más ejecuciones de register test puede cubrir esta deficiencia. En un escenario de producción real, donde la fabricación de un asic puede costar varios cientos de miles de dólares, una cobertura de código aceptable debe ser superior al 95 %. Esta cobertura mı́nima es una función del tiempo de fabricación, importancia de la tarea que realizará el chip, nivel del madurez del rtl en general, etcétera. Otra consideración es la urgencia de poner el dispositivo en el mercado, caracterı́sticas no crı́ticas que resultan defectuosas pueden deshabilitarse con tal de no desaprovechar una oportunidad importante. 4 Conclusiones y recomendaciones • El plan de pruebas y el ambiente desarrollado permitieron alcanzar una cobertura funcional del 98 % y una cobertura de código del 87 %. De acuerdo a estos resultados se concluye que se pudo verificar que el envı́o y recepción de paquetes en el uart funciona correctamente. • La metodologı́a uvm provee un marco de referencia que permite el desarrollo de ambientes de verificación de manera rápida y efectiva, que pueden ser reutilizados. Esto se aprecia en la integración exitosa de los dos interface uvc usados con el module uvc desarrollado. • La cobertura de código es una herramienta que el diseñador puede utilizar para evaluar el progreso de la verificación. La cobertura funcional es usada por el verificador para comprobar la efectivad de las pruebas aleatorias restringidas. • Los huecos que quedaron luego del esfuerzo de verificación se relacionan a la inyección de errores de bit de inicio y bit de parada. Tampoco se verificó la generación de tramas de cinco bits con dos bits de parada. • Se recomienda reestructurar el monitor del uart interface uvc para que interprete de manera adecuada tramas con errores de bit de inicio y bit de parada. Adicionalmente, que detecte de manera correcta las tramas de cinco bits con 1.5 bits de parada. • Se recomienda el uso de otros métodos de verificación, como verificación formal. Además existen herramientas para chequeo de ambientes de verificación, que introducen errores en el rtl y verifican que el ambiente los detecte. Esto se conoce como análisis de mutación. • Se recomienda buscar herramientas de software libre para impulsar un laboratorio en la ucr. Actualmente las herramientas más populares, Specman e y SystemVerilog no cuentan con compiladores o interpretadores de código libre. Hay una tercera opción más desarrollada, SystemC, que puede resultar apropiada para este propósito. 43 Bibliografı́a Bergeron, J. (2003). Writing testbenches: functional verification of hdl models, 2nd edition. Kluwer Academic Publishers. Cadence (2012). uvm e reference flow user guide, version 1.1. Disponible en: http://forums.accellera.org/files/file/70-uvm-reference-flow-version-11/. Gorban, J. (2002). uart ip core specification revision 0.6. Disponible en: http://opencores.org/project,uart16550. Robinson, D. (2007). Aspect-oriented programming with the e verification language: a pragmatic guide for testbench developers. Morgan Kaufmann Publishers. Rosenberg, S. y Meade, K. A. (2010). A practical guide to adopting the Universal Verification Methodology ( uvm). Cadence Design Systems. Vasudevan, S. (2006). Effective functional verification: Principles and processes. Springer. 45