Automatice el desarrollo FPGA con Jenkins, Vivado y GitHub en un VPS Linux
* Este artículo contiene un enlace de afiliado para VPS UpCloud
La entrega continua y la integración continua son metodologías de desarrollo de software ágil que acortan el tiempo de ciclo entre cambio de código y despliegue. Al usar automatización para verificar cambios de código y construir archivos de release, los equipos pueden volverse más productivos y eficientes.
Las empresas de software han practicado desarrollo continuo durante mucho tiempo, pero también puede usar estos métodos para sus proyectos FPGA. Este tutorial le enseña cómo configurar un servidor de automatización en un Servidor Privado Virtual (VPS) usando Jenkins, Xilinx Vivado, y el sistema de control de código fuente (SCM) Git / GitHub.
¿Qué es Jenkins?
El servidor de automatización Jenkins es un programa gratuito y de código abierto escrito en Java. Se ejecuta en Windows o Linux. Usaremos Linux en esta entrada porque es la plataforma más común para servidores headless.
Jenkins se ejecuta como proceso daemon en Linux o como servicio en Windows. Un servidor web integrado que Jenkins lanza al iniciar proporciona la interfaz de usuario. La mayoría de usuarios interactuarán con Jenkins usando la interfaz web. Puede añadir nuevos proyectos de automatización y gestionar los existentes a través de la GUI web.
La imagen superior muestra la página principal del servidor Jenkins que configuraremos hoy. Por defecto, solo usuarios logueados pueden acceder a Jenkins, pero para este artículo he habilitado acceso público a partes de mi *servidor demo.
* Actualización: Desactivé el servidor demo el 13 de mayo de 2020
Lo que ve en la página principal es una lista de jobs. Estos jobs pueden contener cualquier tarea, y pueden dispararse manualmente desde la GUI web. O pueden dispararse automáticamente a través de scripts, webhooks, o como resultado de otros jobs completando. De ahí el término servidor de automatización.
En nuestro ejemplo, cada job corresponde a un módulo VHDL mantenido en un repositorio GitHub separado. Haremos que Jenkins ejecute una simulación y construya el proyecto cada vez que un desarrollador haga push de código a uno de los repos Git monitorizados. Si un testbench falla o la construcción se rompe, Jenkins marcará el job como fallido en la interfaz web y enviará automáticamente un email a la persona que cometió el código defectuoso.
El proyecto de ejemplo
Los servidores de automatización son más útiles para equipos trabajando en proyectos mayores. Por tanto, he construido un proyecto FPGA de ejemplo que consiste en ocho repositorios Git. El proyecto es el contador de display de 7 segmentos del curso Fast-Track porteado a la Xilinx ZedBoard.
Enlaces a los ocho repos en GitHub:
- packages (paquetes VHDL)
- bcd_encoder (Módulo)
- counter (Módulo)
- digit_selector (Módulo)
- output_mux (Módulo)
- reset (Módulo)
- seg7_encoder (Módulo)
- seg7 (Módulo superior)
Cada repo contiene un módulo VHDL y su testbench. Una excepción es el repo packages, que contiene solo tres paquetes VHDL que definen constantes y tipos. Adicionalmente, el módulo superior seg7 contiene un archivo de constraints que define la velocidad de reloj y asignaciones de pines de la implementación física.
La mayoría de proyectos VHDL a gran escala usan módulos de más de un repositorio. Las empresas típicamente tienen una librería de paquetes y módulos que reutilizan en muchos diseños. Eso es lo que emulo dividiendo este diseño relativamente simple en tantos módulos.
En nuestro ejemplo, todos los módulos dependen del repo packages, y el módulo superior depende de todos los submódulos también. He resuelto esto importándolos según necesidad usando submódulos Git estándar. El gráfico superior muestra el contenido y dependencias de todos los repos en este proyecto.
Los repositorios Git también contienen varios archivos no de diseño, como la configuración de Jenkins y scripts de build. Hablaremos de ellos en las próximas secciones.
Servidor privado virtual (VPS)
Aunque Jenkins puede ejecutarse en cualquier ordenador Windows o Linux, para todos los propósitos prácticos, querrá ejecutarlo en un servidor dedicado. El servidor de automatización tiene que estar siempre activo y accesible para todos los miembros de su equipo. Si tiene un servidor físico con capacidad suficiente por ahí, genial. Pero para la mayoría, una solución más rápida y barata es usar un servidor privado virtual (VPS).
Un VPS es un ordenador virtual que alquila de una empresa de hosting por internet. Aparece como un ordenador Windows o Linux real con el que puede interactuar e instalar el software que quiera. Vamos a usar un ordenador Linux porque es lo que tiene más sentido para nuestro caso.
El sitio VHDLwhiz funciona en un VPS, como lo ha hecho los últimos dos años. Ya pasé por el problema de encontrar el proveedor VPS más rápido y mejor, que es UpCloud. Naturalmente, vamos a usar UpCloud para configurar el VPS para nuestro servidor de automatización.
Obtenga el bono de $25 de UpCloud
Si desea probar UpCloud, tengo un código de referencia que le dará $25 de crédito al registrarse.
>> Haga clic aquí para obtener el bono de $25 de UpCloud <<
O use mi código promocional al pagar: NV78V6
Al usar el código, obtendrá el bono y apoyará a VHDLwhiz al mismo tiempo. Yo puedo recibir fondos creditados en mi cuenta UpCloud por cada cliente que lo use.
Bien, basta de charla de ventas. Vamos a configurar el servidor.
Desplegando el VPS de UpCloud
Después de loguearse en su nueva cuenta UpCloud, puede iniciar el proceso de crear una nueva instancia VPS navegando a Servers → Deploy server.
UpCloud tiene muchos centros de datos por el mundo. Seleccione la ubicación más cercana a usted para alojar su nuevo servidor. Luego tiene que seleccionar un plan para cuántos recursos dar a su máquina virtual. Jenkins no usa muchos recursos, pero Xilinx Vivado es un verdadero devorador de RAM. Por tanto, debería elegir al menos el plan con 4 GB de RAM, como se muestra en la imagen inferior.
Recomiendo echar un vistazo a la página de Recomendaciones de Memoria de Xilinx porque el uso de memoria está estrechamente relacionado con la complejidad del FPGA objetivo. La página lista el uso pico de memoria para el FPGA Zynq-7000 XC7Z045 que uso como 1,9 GB. Encontré que el plan de 2 GB era demasiado poco para rutear el diseño. Vivado se bloqueó, y apareció el siguiente mensaje en el log dmesg:
[807816.678940] Out of memory: Killed process 22605 (vivado) total-vm:2046684kB, anon-rss:782916kB, file-rss:308kB, shmem-rss:0kB
Tenga en cuenta que siempre puede actualizar la RAM y recursos CPU de su servidor fácilmente desde su cuenta UpCloud. No obtendrá automáticamente el espacio extra de disco duro que viene con los paquetes más costosos sin reparticionar el sistema de archivos del VPS, pero funcionará. Como referencia, empecé en el plan con 50 GB de almacenamiento, y he usado el 61% de eso tras completar todo el servidor de automatización. Solo Vivado ocupa 24 GB de espacio.
Recomiendo seleccionar la última distribución Linux CentOS como sistema operativo, como se muestra en la imagen inferior. Xilinx Vivado oficialmente solo soporta Red Hat Linux, que no es gratuito. Pero CentOS es una distribución Linux gratuita, soportada por la comunidad, que sigue de cerca a Red Hat.
Luego hay algunas opciones de red que puede dejar por defecto. También hay una sección en la página web donde puede subir sus claves SSH para login sin contraseña. Siempre puede configurar estas cosas después usando el método convencional de Linux para subir claves SSH.
Finalmente, necesita especificar el hostname y nombre del servidor, como se muestra en la imagen inferior. El hostname es el dominio público que los usuarios introducirán en el navegador para acceder al servidor Jenkins. Si no tiene un dominio o subdominio listo, siempre puede acceder al servidor usando su dirección IP. Cuando esté contento con la configuración, pulse el botón Deploy para crear el servidor.
Después de crear el servidor, la contraseña autogenerada aparecerá como notificación. Puede cambiarla después, usando el comando passwd de Linux. Si subió su clave SSH antes de desplegar el servidor, no necesitará la contraseña en absoluto. Si alguna vez pierde acceso a su servidor, siempre puede loguearse desde su cuenta UpCloud pulsando Open the console connection, como se muestra en la imagen inferior.
Configuración de zona DNS
El nuevo servidor obtiene direcciones IPv4 e IPv6 permanentes, encontradas en su cuenta UpCloud en Servers->Network. Puede acceder al servidor por SSH en la cuenta root de la dirección IPv4 pública.
Usando la dirección IP de ejemplo de la imagen inferior, el comando apropiado para entrar en su ordenador Linux sería:
ssh root@su_direccion_ip
Está bien usar solo la dirección IP si solo está experimentando. Pero una solución más práctica es asignar un nombre de dominio permanente al servidor. Para eso, tiene que comprar un dominio de uno de los muchos registradores disponibles online.
Como ya poseo el dominio vhdlwhiz.com, decidí crear un subdominio para el servidor Jenkins llamado jenkins.vhdlwhiz.com. Configuramos el nombre de dominio correctamente en el servidor UpCloud cuando lo desplegamos. Lo siguiente que necesitamos hacer es apuntar el subdominio a la dirección IPv4 pública.
La imagen inferior muestra la configuración que estoy introduciendo en el archivo de zona DNS de mi registrador de nombres de dominio. Si quisiera el servidor en el dominio principal (vhdlwhiz.com), habría dejado el campo hostname en blanco. Pero lo quiero en el subdominio "jenkins" de vhdlwhiz.com. Por tanto, introduzco el nombre del subdominio.
Tardará un tiempo tras cambiar la configuración DNS antes de poder usar el nombre de dominio para acceder a su web. Normalmente no más de 20 minutos, pero en casos extremos puede tardar hasta 48 horas en propagarse a todos los rincones de internet.
Cuando los cambios hayan surtido efecto, puede usar el nombre de dominio en lugar de la dirección IP al loguearse en el servidor por SSH:
ssh root@tusub.tudominio.com
Instalando Jenkins
Lo primero que debe hacer tras loguearse en la cuenta root de su nuevo servidor Linux es actualizar todos los paquetes instalados. En CentOS Linux, yum es el gestor de paquetes por defecto. Vamos a usar el comando yum para instalar la mayoría del software.
Ejecute el siguiente comando para actualizar todos los paquetes instalados a las últimas versiones:
yum -y update
Ahora que sabemos que nuestro sistema está actualizado, podemos proceder con la instalación. Pero antes de emitir el comando yum para instalar Jenkins, vamos a instalar Java versión 11, explícitamente. Eso nos ahorrará problemas luego cuando instalemos Xilinx Vivado.
Actualmente no hay intérprete Java presente en nuestro servidor, y si decimos a yum que instale Jenkins, va a instalar Java versión 8. Eso funciona bien para Jenkins, pero creará problemas luego porque Vivado depende de Java versión 11.
Instale Java 11 usando este comando antes de instalar Jenkins:
yum -y install java-11-openjdk-devel
Jenkins no está disponible en el repositorio de software por defecto que viene con CentOS. Afortunadamente, podemos importar el repo de Jenkins desde Red Hat usando los siguientes comandos:
wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo rpm --import https://pkg.jenkins.io/redhat/jenkins.io.key
Finalmente, podemos proceder a instalar Jenkins:
yum -y install jenkins
El servidor Jenkins se iniciará automáticamente tras el próximo arranque, pero puede iniciar el servidor sin reiniciar así:
systemctl start jenkins
Siempre puede comprobar el estado del servidor Jenkins usando el comando systemctl:
systemctl status jenkins
Imprimirá el estado del servidor junto con cualquier mensaje de error.
Jenkins sobre HTTP inseguro
En este punto, Jenkins se ejecuta en el puerto 8080 en el VPS, pero no tiene forma de conectarse con su navegador web. Eso es porque el firewall de CentOS bloquea el puerto 8080 y el puerto 80 (HTTP) por defecto. Lo que podemos hacer para arreglar eso es abrir el puerto 80 en el firewall y redirigirlo al puerto 8080 usando iptables.
Pero antes de hacer eso, tiene que decidir si quiere asegurar su sitio con HTTPS. El problema de usar solo HTTP y puerto 80 es que su web va a ser insegura. Si accede usando Wi-Fi público, una persona malintencionada en la misma Wi-Fi con un portátil y software de hacking disponible puede escuchar su conexión y robar sus credenciales de login a Jenkins.
Si quiere evitar el riesgo de seguridad de HTTP sin cifrar, salte a la siguiente sección sobre configurar HTTPS para Jenkins. De lo contrario, siga leyendo.
Habilitar acceso HTTP inseguro a Jenkins es tan fácil como ejecutar los siguientes comandos:
firewall-cmd --permanent --zone=public --add-port=80/tcp firewall-cmd --reload iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Entonces puede introducir su nombre de dominio en su navegador favorito, y la página Getting Started de Jenkins debería aparecer. Al menos en Google Chrome, la advertencia "Not secure" se mostrará en la barra de direcciones.
Salte a la sección Configurando Jenkins si está contento con eso.
Jenkins sobre HTTPS seguro
Tener una web insegura accesible al público es un gran riesgo de seguridad. Jenkins puede acceder a su código fuente, y también cualquier hacker que logre entrar en el servidor. Afortunadamente, asegurar la web está a solo unos comandos copiados y pegados.
Jenkins no puede manejar HTTPS por sí solo. Por tanto, tenemos que instalar un servidor web genérico para redirigir peticiones que lleguen por el canal seguro al servidor Jenkins inseguro. Usaré Nginx, uno de los servidores web gratuitos y de código abierto más populares hoy.
Ejecute el siguiente comando para instalar e iniciar Nginx:
yum -y install nginx systemctl start nginx
Luego necesitamos abrir tanto puertos HTTP como HTTPS en el firewall. Solo serviremos peticiones HTTPS, pero necesitamos mantener el puerto HTTP abierto también porque configuraremos Nginx para redirigir todas las peticiones inseguras al puerto seguro.
Estos comandos abrirán el firewall para tráfico web:
firewall-cmd --permanent --zone=public --add-port=80/tcp firewall-cmd --permanent --zone=public --add-port=443/tcp firewall-cmd --reload
El siguiente paso es instalar un certificado que los navegadores web puedan usar para certificar que es su web con la que interactúan y no un impostor. Vamos a usar la autoridad de certificación gratuita Let's Encrypt para asegurar nuestro sitio. Los pasos individuales son complicados, pero afortunadamente certbot proporciona un script que puede hacerlo automáticamente.
Descargue y prepare el script con los siguientes comandos:
apt update apt install snapd snap install core; snap refresh core snap install --classic certbot
Luego, ejecute el script, que instalará el certificado y hará los cambios necesarios al archivo de configuración de Nginx:
certbot --nginx
Mientras el script se ejecuta, le pedirá información. Responda afirmativamente (Yes, Accept) a todas las preguntas hasta que le pregunte si quiere redirigir tráfico HTTP a HTTPS. El listado inferior muestra las preguntas, y mi respuesta sugerida (2). Permitir a Nginx redirigir peticiones inseguras asegura que nadie pueda introducir explícitamente https://tusitio.com y llegar a la versión insegura de Jenkins. Nginx los redirigirá a la versión segura.
... Deploying Certificate to VirtualHost /etc/nginx/nginx.conf Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Finalmente, debería habilitar un cron job para renovar el certificado periódicamente. De lo contrario, expirará, y los navegadores se negarán a abrir su sitio.
Ejecute el siguiente comando one-liner para añadir el cron job diario:
echo "0 0 * * * /snap/bin/certbot renew --quiet" | crontab -
El daemon cron ejecutará el script de renovación diariamente a medianoche. Puede listar los cron jobs con el comando crontab -l y editarlos con crontab -e. Si visita su web ahora, verá la página de prueba de Nginx, y no Jenkins. Arreglaremos esto en breve, pero asegúrese de que la advertencia "Not secure" ha desaparecido de la barra de direcciones de Chrome.
Para que Nginx sirva Jenkins, necesita hacer algunos cambios al archivo /etc/nginx/nginx.conf. Crédito para este snippet de código va al blog de Kerren en Medium. Usar el editor nano es probablemente la forma más fácil de editar el archivo de configuración:
nano /etc/nginx/nginx.conf
Localice el bloque server que lista su nombre de dominio y añada las líneas resaltadas del listado inferior a su archivo nginx.conf. Tenga en cuenta que la primera de las tres nuevas líneas va encima del bloque server, y el resto va en el bloque location root.
Después de actualizar el archivo de configuración, necesita recargar Nginx para que los cambios surtan efecto. Opcionalmente, puede probar el archivo de configuración antes de recargar usando el comando:
nginx -t
Nginx imprimirá OK o le dirá en qué línea del archivo nginx.conf está el error. Cuando esté contento con los cambios, puede recargar el servidor web dando el siguiente comando:
systemctl reload nginx
Cuando ahora visite el sitio Jenkins en su navegador, debería ver la página Getting Started de Jenkins, servida sobre conexión segura, y podemos continuar configurando Jenkins desde la interfaz GUI web.
Configurando Jenkins
La primera vez que visite su web Jenkins, le pedirá una contraseña encontrada en un archivo del sistema de archivos Linux. Mientras está logueado por SSH, use el comando inferior para mostrar la contraseña. Copie y péguela en el navegador para acceder a la GUI web:
cat /var/lib/jenkins/secrets/initialAdminPassword
En la siguiente pantalla, Jenkins preguntará si quiere instalar los plugins sugeridos, o si quiere especificar cuáles instalar. Vaya con la opción Install suggested plugins por ahora. Siempre puede añadir o quitar plugins después.
En la siguiente página, debe crear un usuario admin. Rellene sus datos y cree una contraseña fuerte para usar con su nueva cuenta. Por defecto, solo usuarios logueados pueden acceder al servidor Jenkins. Usuarios anónimos solo verán el diálogo de login si se aventuran a su web. La única razón por la que pudo acceder a mi *sitio demo jenkins.vhdlwhiz.com es que hice cambios en el servidor. Usé el plugin Matrix Authorization Strategy para dar acceso anónimo a algunas vistas.
* Actualización: Desactivé el sitio demo el 13 de mayo de 2020
Cuando Jenkins termine de instalar plugins, verá el mensaje "Jenkins is ready!", como se muestra en la imagen superior. Pulse el botón que le llevará a la página de resumen vacía de su nueva instalación de Jenkins.
Instalando plugins de Jenkins
Lo primero que necesita hacer es instalar un montón de plugins. Jenkins tiene un gestor de plugins integrado que puede usar para instalar, actualizar y quitar extensiones. Encontrará plugins que pueden cubrir la mayoría de sus necesidades. Solo use la función de búsqueda en el gestor de plugins cuando necesite añadir funcionalidad a Jenkins.
Vamos a instalar los plugins que usé al configurar el servidor Jenkins de ejemplo. Desde la barra lateral, elija Manage Jenkins->Manage Plugins->Available. Tenga en cuenta que no hay plugins listados a menos que introduzca algo en el campo de búsqueda. Una vez escriba, aparecerán.
Blue Ocean
El primer plugin que recomiendo instalar se llama Blue Ocean. Este plugin es una modernización del flujo de trabajo de Jenkins así como de la interfaz de usuario. También trae un montón de otros plugins útiles para que no tenga que instalarlos individualmente. Busque "blue ocean" en el gestor de plugins y selecciónelo para instalación.
En la página de progreso de instalación que aparece tras hacer clic en instalar, tiene la opción de seleccionar Restart Jenkins when installation is complete and no jobs are running. Si marca la casilla, Jenkins se reiniciará tras completar la instalación del plugin. Otra forma de reiniciar Jenkins es loguearse en el servidor por SSH y ejecutar:
systemctl restart jenkins
Aparte de la larga lista de otros plugins que instaló Blue Ocean, no hay cambio notable a primera vista. Pero habrá un nuevo elemento de menú en la barra lateral. Al hacer clic, le llevará a la GUI Blue Ocean, que se ve bastante diferente de la interfaz Normal de Jenkins. ¡Pruébelo!
Green Balls
El siguiente plugin que siempre instalo es puramente estético. El plugin Green Balls no requiere configuración. Simplemente busque "green ball" en el gestor de plugins e instálelo.
Por defecto, Jenkins usa bolas de color azul en la página de resumen para indicar que el estado de un job es exitoso. La razón tiene que ver con que el inventor de Jenkins es japonés. Curiosamente, en Japón, el color azul es intercambiable con verde cuando se trata de indicar un estado OK. Más sobre eso en este artículo, donde puede oír al autor explicar la razón de primera mano.
Usuarios de la mayoría de otras partes del mundo probablemente preferirían bolas de estado verdes. Eso se arregla fácilmente con el plugin Green Balls.
Plugin Locale
El siguiente plugin que instalé se llama Locale. Busque "locale" bajo la pestaña Available en el gestor de plugins. Instale el plugin.
El plugin le permite forzar a Jenkins a usar el mismo idioma en la GUI para todos los usuarios. Por defecto, Jenkins traduce la interfaz de usuario al idioma que usa su navegador web. Soy noruego, pero preferiría tener Jenkins en inglés. La traducción era un poco inadecuada. Además, es mucho más fácil buscar respuestas en inglés si necesita averiguar cómo hacer algo en Jenkins.
Por supuesto, depende totalmente de usted si quiere este plugin. Si lo instala, necesita navegar a Manage Jenkins->Configure System y encontrar la sección llamada Locale. Luego, necesita introducir "en_US" (o el idioma que quiera) y marcar la casilla de abajo para forzar este idioma a todos los usuarios. No olvide desplazarse al final de la página y hacer clic en Save.
El plugin final que necesita para clonar mi configuración es el plugin Sidebar Link. Le permite añadir enlaces personalizados a la barra lateral de Jenkins. Lo usaremos luego para añadir un enlace a los releases FPGA (bitfiles). Busque "sidebar" en el gestor de plugins e instale el plugin.
Conectando Jenkins a GitHub
Independientemente de si su repositorio es público o privado, necesita dar a Jenkins algunos permisos en su cuenta GitHub. Al menos si quiere hacerlo de la forma más fácil, debería dejar que el plugin GitHub de Jenkins gestione la interfaz con GitHub. Blue Ocean ya instaló el plugin GitHub. Estos son los pasos para configurarlo.
Primero, debería instalar Git en su sistema. El plugin GitHub no lo requiere estrictamente, pero cuando empiece a trabajar con jobs de Jenkins, tiene que tenerlo. Ejecute este comando para instalar Git en CentOS Linux:
yum -y install git
Token de acceso personal en GitHub
Inicie sesión en GitHub, haga clic en su foto de perfil en la esquina superior derecha, elija settings, y vaya a Developer Settings. Luego, elija Personal access tokens del menú lateral izquierdo, o haga clic en este enlace que le llevará directamente ahí.
Aquí, necesita hacer clic en Generate new token. GitHub le pedirá su contraseña una vez más. Lo que está haciendo ahora es esencialmente crear una nueva contraseña específica para la aplicación. Eso es mejor que compartir su contraseña real porque puede revocarla, y puede limitar los permisos concedidos al token. Eso es lo que haremos en la página que se abre.
Cuando haya introducido su contraseña, necesita dar un nombre al token. El nombre es solo para usted. Puede ser cualquier cosa, "Jenkins", por ejemplo. Luego, debe habilitar al menos los permisos admin:org_hook, admin:repo_hook, y repo. Puede dejar el resto de casillas sin marcar.
Finalmente, al hacer clic en Generate token, aparecerá el código de acceso. Necesita copiarlo antes de salir de esa página porque no es posible verlo de nuevo. Si lo olvida, borre el token y recréelo.
Introduciendo las credenciales de GitHub en Jenkins
Cuando haya copiado el token, vaya a Jenkins, seleccione Manage Jenkins->Configure System, y encuentre la sección llamada GitHub. Del menú desplegable, seleccione Add GitHub Server->GitHub Server.
En la nueva sección GitHub Server que aparece, marque la casilla Manage hooks. Cuando haga esto, Jenkins instalará webhooks en GitHub para los repos que monitoriza. Veremos luego que esto es particularmente útil porque los usaremos para disparar una simulación o build en Jenkins cuando un usuario haga push de código al repositorio GitHub asociado.
Seleccione Credentials (Add)->Jenkins después de haber marcado la casilla.
En la ventana que se abre, cambie el desplegable Kind a Secret text. Luego, pegue el token de acceso personal que generó previamente en GitHub en el campo Secret. Escriba "GitHub" en el campo ID y pulse Add.
Finalmente, cuando vuelva al menú de configuración principal, tras añadir el secret text, seleccione la nueva clave GitHub del menú Credentials. Luego, pulse Test connection para verificar que Jenkins puede hablar con GitHub usando el token de acceso. Si todo fue bien, debería ver un mensaje como el que se muestra en la imagen inferior.
Asegúrese de desplazarse al final de la página y hacer clic en Save antes de salir de la página de configuración.
Si tiene problemas y acaba añadiendo múltiples credenciales, puede borrarlas yendo a Manage Jenkins->Manage Users, y haga clic en su nombre de usuario. Ahora, en la barra lateral izquierda, habrá un elemento de menú llamado Credentials. Desde ahí, puede ver y editar todas las claves que Jenkins ha almacenado.
Enviando email desde Jenkins
Vamos a configurar Jenkins para enviar emails automatizados cuando algo se rompa en su código. Para que eso ocurra, necesitamos hacer algunos cambios en el menú Manage Jenkins->Configure System.
Lo primero que debe hacer es introducir la dirección from que Jenkins usará al enviar emails automatizados. Encuentre el campo System Admin e-mail address en la página de configuración, e introduzca la dirección que quiera que Jenkins use como remitente.
Una nota rápida: es mejor introducir una dirección que termine con el nombre de dominio de Jenkins. Eso minimiza la posibilidad de que el email acabe en la carpeta de spam. El servidor Jenkins no tiene permiso para enviar emails en nombre de otros dominios, pero la mayoría de servicios de email aceptan direcciones from con el mismo dominio que el servidor emisor. Lea más sobre eso en este artículo de Wikipedia. En la imagen inferior, he introducido una dirección from que termina con el mismo dominio que el servidor Jenkins.
Luego, necesitamos dar a Jenkins una forma de enviar emails. La forma más directa es instalar un servidor SMTP (mail) en el VPS. Puede hacerlo logueándose y ejecutando los siguientes comandos:
yum -y install sendmail systemctl enable sendmail systemctl restart sendmail
Después de instalar sendmail, vuelva a la configuración del sistema de Jenkins e introduzca "localhost" en el campo SMTP server.
En este punto, también puede marcar la casilla para permitir envío a usuarios no registrados. Eso significa usuarios que no tienen cuenta de Jenkins. Luego, configuraremos Jenkins para enviar un email a cualquier persona que haga push de código defectuoso a GitHub. Jenkins obtendrá la dirección de email del culpable de GitHub, pero eso solo funcionará si la persona tiene una cuenta Jenkins coincidente, o si marcamos esta casilla.
Finalmente, puede probar la configuración. Después de pulsar Test configuration, el mensaje "Email was successfully sent" debería aparecer, y el email debería llegar a su bandeja de entrada. Revise su carpeta de spam si no recibe el email en cinco minutos.
Instalando Xilinx Vivado en modo batch
Estoy usando Xilinx Vivado para simular, compilar e implementar el código en este proyecto de ejemplo. El dispositivo objetivo es el FPGA Xilinx Zynq-7000 en la placa de desarrollo ZebBoard. En esta sección, le mostraré cómo instalar Vivado en el VPS usando la licencia gratuita WebPACK.
El primer paso es descargar el Instalador Unificado de Xilinx: Linux Self Extracting Web Installer. Necesitará loguearse o crear una nueva cuenta Xilinx para descargar el instalador.
Cuando la descarga haya completado, debe copiarla de su ordenador de escritorio al servidor Jenkins. Si tiene acceso a un shell Linux en su escritorio, sugiero usar copia segura de archivos como en el siguiente comando:
scp Xilinx_Unified_2019.2_1106_2127_Lin64.bin root@su_servidor:/root/
Antes de poder ejecutar el instalador, necesita instalar unos paquetes para satisfacer las dependencias de Vivado. Ejecute los siguientes comandos para hacerlo:
yum -y install tar yum -y install java-11-openjdk-devel yum -y install ncurses-compat-libs yum -y install gcc
Luego, ejecute el instalador unificado de Xilinx así:
./Xilinx_Unified_2019.2_1106_2127_Lin64.bin --keep --noexec --target Xil_installer
El archivo .bin descomprimirá los archivos de instalación a un nuevo directorio llamado Xil_installer. Si obtiene el error listado inferior, es porque no ha instalado tar.
Verifying archive integrity... All good. Uncompressing Xilinx InstallerExtraction failed. Terminated
El instalador unificado de Xilinx puede instalar muchas herramientas diferentes de Xilinx en su sistema. Por tanto, debemos ejecutar el archivo xsetup en el directorio Xil_installer para especificar qué software nos interesa:
cd Xil_installer/ ./xsetup -b ConfigGen
El script xsetup le pide qué herramientas le gustaría tener. Introduzca "2" para Vivado, y luego "1" para Vivado HL WebPACK.
Select a Product from the list: 1. Vitis 2. Vivado 3. On-Premises Install for Cloud Deployments 4. BootGen 5. Lab Edition 6. Hardware Server 7. Documentation Navigator (Standalone) Please choose: 2 Select an Edition from the list: 1. Vivado HL WebPACK 2. Vivado HL Design Edition 3. Vivado HL System Edition Please choose: 1
Para instalar la edición Xilinx WebPACK, debe loguearse en su cuenta Xilinx durante la instalación. En un ordenador de escritorio, el GUI del instalador le guía en este proceso, pero en el servidor no hay GUI, así que tenemos que autenticar usando el script xsetup. Ejecute el siguiente comando para generar el token de autenticación:
./xsetup -b AuthTokenGen
Tuve que ejecutar el comando unas cuantas veces antes de que funcionara. Al principio, el script se paraba con el error "Internet connection validated, can connect to internet." Sin embargo, tras unos intentos, logré pasar, y pude iniciar sesión. El script pedirá su user ID y contraseña. Ese es el email y contraseña que usó para descargar el instalador de xilinx.com.
Finalmente, está listo para instalar Vivado en modo batch. Al llamar al script de setup, tiene que especificar el archivo de configuración de instalación para que xsetup sepa qué herramientas descargar. El archivo de configuración está en la carpeta .Xilinx en el directorio home del usuario root. Ejecute el siguiente comando para iniciar la instalación usando el archivo de configuración:
./xsetup -a XilinxEULA,3rdPartyEULA,WebTalkTerms \ -b Install -c ~/.Xilinx/install_config.txt
El proceso de instalación tardará mucho en completar. La instalación de Vivado usa 24 GB de espacio. Todo eso se está descargando ahora del servidor de Xilinx, relativamente lento. Para mí, la descarga tomó un poco más de dos horas.
Después de que la instalación complete, debería probar que Vivado inicia correctamente en modo batch. Xilinx proporciona un script de shell que configura el entorno para usted. Antes de poder ejecutar Vivado, necesita usar el comando source para cargar el contenido del script en su shell activo:
source /tools/Xilinx/Vivado/2019.2/settings64.sh
Entonces, está listo para ejecutar Vivado. Pero no hay entorno GUI instalado en su servidor, así que tenemos que iniciarlo en modo batch usando este comando:
vivado -mode batch -source /dev/null
Si Vivado inicia y sale inmediatamente sin imprimir errores, es indicación de que Vivado tiene todo lo que necesita, y está listo para ir. Tenga en cuenta que si obtiene el error listado inferior, es porque no ha instalado el paquete ncurses-compat-libs, como hablamos al inicio de esta sección.
application-specific initialization failed: couldn't load file "librdi_commontasks.so": libtinfo.so.5: cannot open shared object file: No such file or directory
Integrando Vivado en Jenkins
Para preparar Jenkins para Vivado, necesitamos hacer algunos cambios a la configuración general. Vaya a Manage Jenkins->Configure System y verifique que todos los ajustes por defecto tengan sentido para usted.
Como mencioné antes, Vivado usa mucha RAM. El uso de recursos depende de su FPGA objetivo, y puede obtener una indicación de cuánto necesita de la página de Recomendaciones de Memoria de Xilinx. Por tanto, recomiendo que cambie el número por defecto de jobs paralelos que pueden ejecutarse de 2 a 1. A menos que haya asignado vastos recursos de RAM en su VPS, probablemente quiera establecer # of executors a 1.
En lugar de definir variables de entorno en cada job de Jenkins, especificaremos el PATH globalmente para todos los jobs. Eso facilita cambiar a una versión más nueva de Vivado en el futuro. Entonces puede referirse al ejecutable 'vivado' en sus scripts, y siempre apuntará a la última versión, o a la que decida.
Desplácese a la sección Global properties y marque Environment variables. Haga clic en Add para obtener una nueva entrada. Asegúrese de incluir el PATH estándar de Linux también. Usé "PATH=/tools/Xilinx/Vivado/2019.2/bin:/sbin:/usr/sbin:/bin:/usr/bin".
No olvide desplazarse al final de la página y hacer clic en Save.
Proyectos GUI de Vivado en modo batch
Elegí gestionar los proyectos de Vivado en modo GUI. Para cada repositorio, creé un nuevo proyecto desde el GUI regular de Vivado, añadiendo archivos fuente, configurando librerías, y todo eso. Sin embargo, los archivos de proyecto .xpr son binarios y dependen de muchos otros archivos temporales en el directorio del proyecto.
Los archivos binarios no son adecuados para SCMs como Git. Afortunadamente, Xilinx ha pensado en esto y escrito una guía (XAPP1165) para cómo usar Vivado con sistemas de control de versiones. Lo que hacemos es usar el comando write_project_tcl en Vivado para exportar el proyecto completo a un script Tcl. El script contiene código Tcl legible por humanos adecuado para Git.
He organizado todos los repos Git para que todos los archivos que pertenecen a los proyectos Vivado estén en una subcarpeta llamada "vivado", mientras que los archivos fuente VHDL están en el directorio padre. Eche un vistazo al proyecto demo packages en mi GitHub para ver a qué me refiero. Para cada repo, pondremos los scripts Tcl de Vivado en la carpeta vivado. También encontrará el archivo create_vivado_proj.tcl, que es la versión legible por humanos del proyecto Vivado.
Para crear el archivo create_vivado_proj.tcl, empiece configurando el proyecto Vivado como desee en el GUI de Vivado. Asegúrese de que el proyecto Vivado reside dentro de una subcarpeta vivado. Cuando esté contento con el proyecto, expórtelo introduciendo los siguientes comandos en la consola Tcl de Vivado:
cd [get_property DIRECTORY [current_project]] write_project_tcl -force -target_proj_dir . create_vivado_proj
Añada el archivo create_vivado_proj.tcl a Git, y configure el gitignore para ignorar el resto del proyecto Vivado. Aquí está el contenido de mi archivo .gitignore que ignora todo menos scripts Tcl en la carpeta vivado:
vivado/* !vivado/*.tcl
Abriendo el proyecto Vivado en modo batch
Es buena idea probar el proyecto Vivado manualmente en el VPS antes de empezar a crear jobs de Jenkins. Por defecto, el daemon se ejecuta desde una cuenta de usuario llamada jenkins en el servidor Linux. Por tanto, debería probar el proyecto Vivado usando el usuario jenkins.
Asegúrese de tener Git instalado en el servidor Linux antes de empezar este experimento. Ejecute este comando para instalar Git tras loguearse como root:
yum -y install git
No puede loguearse directamente en el usuario jenkins, pero puede cambiar a él desde el usuario root así:
su - jenkins
Si ahora ejecuta un comando pwd, verá que está en /var/lib/jenkins:
[jenkins@jenkins ~]$ pwd /var/lib/jenkins
Eso es porque no es una cuenta de usuario regular que tiene el directorio home bajo /home, como es la norma en sistemas Linux. Es solo para ejecutar el daemon de Jenkins, pero podemos loguearnos para realizar un recorrido manual del proceso de build en el entorno de Jenkins.
La carpeta home está llena de todos los datos dinámicos como logs, configuraciones de usuario, y plugins que ha descargado. Cuando luego empecemos a ejecutar jobs en la GUI de Jenkins, aparecerán en la carpeta jobs.
Vayamos a la carpeta jobs para realizar nuestro experimento:
cd /var/lib/jenkins/jobs/
Puede clonar su repositorio Git directamente en la carpeta jobs. Su repo Git tiene que ser accesible sin usar contraseña. O porque es público, o porque ha configurado login sin contraseña como se describe en las páginas de ayuda de GitHub.
Si no tiene un repositorio Git con el proyecto Vivado listo, siéntase libre de clonar uno de mis repos así:
git clone https://github.com/jonasjj/Jenkins-demo-packages
Luego, cd al nuevo directorio del repositorio Git, y más adentro a la carpeta vivado:
cd Jenkins-demo-packages/vivado/
Si descargó mi ejemplo, encontraría dos archivos Tcl: create_vivado_proj.tcl y check_syntax.tcl. El primero es el proyecto Vivado convertido a archivo Tcl, y el segundo es un script del que no hemos hablado aún. Es para comprobar la sintaxis de archivos VHDL en el proyecto Vivado.
Antes de poder ejecutar cualquier comando Vivado, necesitamos establecer la variable de entorno PATH en el shell actual. En Jenkins, resolvimos esto usando Global properties, pero ahora no venimos a través de Jenkins, así que tenemos que hacer source del script de configuración de Xilinx así:
source /tools/Xilinx/Vivado/2019.2/settings64.sh
Ahora que el ejecutable vivado está en nuestro path, empecemos recreando el proyecto. Este es el comando para hacerlo al ejecutar Vivado en modo batch:
vivado -mode batch -source create_vivado_proj.tcl
Tras pulsar Enter, debería ver un montón de código Tcl echoado a la consola. Es el código para recrear el proyecto Vivado que se está ejecutando. Si no vio errores obvios, escriba el comando "echo $?" en el terminal antes de hacer nada más. La salida debería ser 0 si todo fue bien.
INFO: [Common 17-206] Exiting Vivado at Sun Apr 19 18:32:48 2020... [jenkins@jenkins vivado]$ echo $? 0
El comando "echo $?" le muestra el estado de salida del comando previo que ejecutó en Linux. Un estado de salida de 0 significa que todo está OK, sin error. Cualquier otro estado de salida que no sea 0 es indicación de error. Esas son viejas convenciones Unix que puede leer más aquí. De todos modos, el estado de salida es importante para Jenkins porque así decide si una etapa de job es éxito o fallo.
Si ahora hace un listado de directorio, verá que Vivado ha recreado los archivos binarios del proyecto:
[jenkins@jenkins vivado]# ls -la total 72 drwxrwxr-x. 6 jenkins jenkins 4096 Apr 19 18:32 . drwxrwxr-x. 4 jenkins jenkins 4096 Apr 19 18:18 .. -rw-rw-r--. 1 jenkins jenkins 217 Apr 19 18:18 check_syntax.tcl -rw-rw-r--. 1 jenkins jenkins 23375 Apr 19 18:18 create_vivado_proj.tcl drwxrwxr-x. 3 jenkins jenkins 16 Apr 19 18:32 packages.cache drwxrwxr-x. 2 jenkins jenkins 26 Apr 19 18:32 packages.hw drwxrwxr-x. 2 jenkins jenkins 6 Apr 19 18:32 packages.ip_user_files -rw-rw-r--. 1 jenkins jenkins 9314 Apr 19 18:32 packages.xpr -rw-rw-r--. 1 jenkins jenkins 638 Apr 19 18:32 vivado.jou -rw-rw-r--. 1 jenkins jenkins 20153 Apr 19 18:32 vivado.log drwxrwxr-x. 2 jenkins jenkins 6 Apr 19 18:32 .Xil
Probemos otro experimento ejecutando scripts Tcl en modo batch de Vivado. Cree un script Tcl de una línea usando el siguiente comando:
echo "exit 1" > test.tcl
Ahora, ejecute el script en modo batch de Vivado:
vivado -mode batch -source test.tcl
Después de que Vivado cierre, compruebe el código de salida una vez más usando el comando "echo $?":
[jenkins@jenkins vivado]# echo $? 1
Es 1, que significa fallo de salida en Unix. Si cambia el contenido del script test.tcl a "exit 0", y ejecuta Vivado una vez más, verá que el estado de salida ahora es 0, indicando éxito. ¡Pruébelo!
La palabra clave exit es Tcl estándar. Vamos a usarla como interfaz entre Vivado y Jenkins. Jenkins ejecuta cualquier script Tcl que creemos en Vivado, y mira el estado de salida para decidir si debe marcar la etapa del job como éxito o fallo.
Recuerde borrar nuestro pequeño proyecto de prueba de la carpeta jobs cuando esté contento con el experimento:
cd /var/lib/jenkins/jobs/ [jenkins@jenkins jobs]# rm -rf Jenkins-demo-packages
Script Tcl para comprobar sintaxis de código en Vivado
Este script Tcl ejecuta una comprobación de sintaxis de los archivos VHDL en el proyecto. Si va a simular o implementar el código, no necesitará este script porque cualquier error de sintaxis romperá la compilación. Pero para mi proyecto packages, no tiene sentido crear un testbench. Los archivos solo contienen declaraciones de constantes y tipos. Aún quiero capturar cualquier error de código enviado a este repo, y ahí es donde la comprobación de sintaxis viene bien.
En el script listado inferior, empezamos abriendo el archivo de proyecto. Luego, llamamos al comando check_syntax de Vivado diciéndole que guarde la salida en una variable llamada msg. Después de que la comprobación complete, miramos el mensaje de salida para ver si se reportaron errores. Si check_syntax reportó algo, establecemos el estado de salida a 1 (fallo). Si no hubo errores, salimos con 0 (éxito).
check_syntax.tcl:
# Check for syntax errors
# Return exit code 1 on error, else 0
open_proj packages.xpr
set msg [check_syntax -fileset sim_1 -return_string]
set ret_val 0
if {$msg != ""} {
set ret_val 1
}
puts $msg
exit $ret_val
Vivado soporta todas las palabras clave estándar de Tcl, y también hay muchos comandos integrados como check_syntax. Recomiendo echar un vistazo a estos dos documentos de Xilinx que cubren las capacidades de scripting Tcl en gran detalle:
Vivado Design Suite Tcl Command Reference Guide (UG835)
Vivado Design Suite User Guide Using Tcl Scripting (UG894)
Script Tcl para simular en Vivado
El siguiente script que creé es para ejecutar el testbench en modo batch. Para que esto funcione, tiene que configurar los simulation sets en el GUI de Vivado antes de exportar el proyecto a Tcl. Adelante, recree uno de los proyectos de simulación en su ordenador de escritorio usando el script create_vivado_proj.tcl para ver cómo lo configuré antes. Puede abrir los proyectos reconstruidos en el GUI de Vivado.
Como puede ver en el listado inferior, empiezo abriendo el proyecto. Luego, establezco el nombre del simulation fileset a una variable (normalmente sim_1). Tras lanzar la simulación, también tenemos que cerrarla. De lo contrario, el estado de la simulación no se escribirá en los archivos de log.
run_simulation.tcl:
open_proj seg7.xpr set sim_fileset sim_1 launch_simulation -simset [get_filesets $sim_fileset] close_sim # Look for assertion failures in the simulation log set log_file [glob *sim/$sim_fileset/behav/xsim/simulate.log] set fp [open $log_file] set file_data [read $fp] exit [regex "Failure:" $file_data]
Me costó encontrar una buena forma de obtener el estado de la simulación. Mis testbenches VHDL terminan con una palabra clave VHDL finish en caso de éxito. Los errores resultarán en un fallo de aserción VHDL. No hay forma obvia de averiguar por qué se paró el simulador usando comandos Tcl en Vivado.
Afortunadamente, Tcl es un lenguaje de scripting potente. Mi workaround es abrir el log de simulación y buscar la cadena "Failure:", que indica un fallo de aserción VHDL. Finalmente, salimos con 1 si la palabra está en el log, o 0 si no lo está.
Script Tcl para sintetizar en Vivado
En el script Tcl para sintetizar en modo batch de Vivado, empezamos abriendo el archivo de proyecto. Luego, asignamos el nombre del run a una variable. Debe haber añadido los archivos de diseño al proyecto Vivado antes de exportarlo a Tcl. Si no cambió el nombre del synthesis run en el GUI, probablemente será "synth_1".
Debería establecer la variable de conteo de CPU al número de procesadores lógicos que tiene su servidor. Este número controla el grado de multithreading que usa Vivado. Opté por el VPS con 4 CPUs en UpCloud, y por tanto establecí el conteo de CPU a 4.
run_synthesis.tcl:
open_proj seg7.xpr
set run_name synth_1
set cpu_count 4
reset_runs $run_name
launch_runs $run_name -jobs $cpu_count
wait_on_run $run_name
set status [get_property STATUS [get_runs $run_name]]
if {$status != "synth_design Complete!"} {
exit 1
}
exit 0
El comando launch_runs es no-bloqueante, significando que completará antes de la síntesis real. Si intentamos leer el estado justo después de llamar launch_run, será "Running". Para pausar el script hasta que la síntesis complete, llamamos al comando wait_on_run.
Finalmente, obtenemos el estado del run y salimos con 0 o 1, dependiendo del mensaje de estado.
Script Tcl para ejecutar la implementación en Vivado
El script para ejecutar Place and Route (PAR) en modo batch de Vivado es similar al script de síntesis. La diferencia es que el nombre del run ahora es "impl_1", y que buscamos otro mensaje de éxito.
run_implementation.tcl:
open_proj seg7.xpr
set run_name impl_1
set cpu_count 4
reset_runs $run_name
launch_runs $run_name -jobs $cpu_count
wait_on_run $run_name
set status [get_property STATUS [get_runs $run_name]]
if {$status != "route_design Complete!"} {
exit 1
}
exit 0
Script Tcl para generar el bitstream en Vivado
Finalmente, si la implementación completa con éxito, podemos generar un bitstream para programar el FPGA. El script es similar al anterior, pero el comando launch_runs es ligeramente diferente. Y por supuesto, buscamos un estado diferente al final.
generate_bitstream.tcl:
open_proj seg7.xpr
set run_name impl_1
set cpu_count 4
launch_runs $run_name -to_step write_bitstream -jobs $cpu_count
wait_on_run $run_name
set status [get_property STATUS [get_runs $run_name]]
if {$status != "write_bitstream Complete!"} {
exit 1
}
exit 0
Configurando los jobs de Jenkins
Un job en Jenkins se refiere a un conjunto de tareas de software agrupadas. Jenkins muestra jobs y su estado actual como elementos listados en la página de resumen. Puede iniciar jobs manualmente desde la interfaz web, o pueden dispararse automáticamente, por ejemplo, cuando alguien hace push de código a un repo, o como resultado de otro job completando. Haremos ambas cosas.
Jenkins ofrece varias formas de gestionar jobs. El método tradicional es el Freestyle project, donde especifica cada acción desde la GUI web de Jenkins. La forma más moderna de gestionar jobs de Jenkins es usar un script de pipeline que almacena toda la información sobre el flujo de ejecución. Los scripts de pipeline tienen el beneficio de que puede añadirlos a su SCM.
Para crear un nuevo script de pipeline, seleccione New item de la barra lateral de Jenkins. En el diálogo que se abre, seleccione la opción Pipeline y haga clic OK.
Lo primero que tenemos que hacer en la configuración del job es añadir el repositorio GitHub que contiene el código fuente. En este ejemplo, uso el repo packages, pero el procedimiento es el mismo para todos los otros jobs y repos. Marque la casilla GitHub project e introduzca la dirección en el campo Project url que aparece.
Después, podemos configurar los build triggers para este job. Quiero que este job inicie cuando alguien haga push de código al repo GitHub. Para eso, marcamos la casilla que dice GitHub hook trigger for GITScm polling. Tenga en cuenta que esto solo funcionará si ha marcado la casilla Manage hooks en la configuración global, como hicimos antes.
Al final de la página de configuración del job está la sección Pipeline. Aquí, tiene la opción de introducir el script de pipeline directamente en la página de configuración. Pero queremos controlar la versión del script de pipeline. Por tanto, elegimos la opción Pipeline script from SCM. Asegúrese de que Git está seleccionado.
Pegue la URL de su repositorio GitHub, y seleccione sus credenciales si es un repo privado. El nuestro es público, así que dejaremos las credenciales en blanco. También iremos con la selección por defecto de rama master.
Finalmente, tenemos que seleccionar la ruta al script de Jenkins dentro del repositorio Git. He creado un archivo llamado Jenkinsfile en la raíz de cada repo. No olvide hacer clic en Save antes de salir de la página.
Scripts de pipeline de Jenkins
Los scripts de pipeline siguen las mismas reglas de sintaxis que el lenguaje de programación Apache Groovy, que debo admitir nunca había oído antes. Sin embargo, no tendrá dificultad para entender scripts de pipeline si ha hecho cualquier tipo de programación moderna. A primera vista, parece un esquema JSON sin las comas separando los elementos de datos.
Los scripts son bastante versátiles, y hay muchas opciones para cosas como ejecutar etapas en paralelo o ejecutar tareas en múltiples servidores Jenkins. Sugiero que eche un vistazo a la documentación oficial de pipeline de Jenkins si quiere profundizar en el tema.
Afortunadamente, no necesita saber todo sobre ellos para beneficiarse de scripts de pipeline. Usaremos el formato inferior como plantilla para todos sus scripts. Añadiremos tantas etapas como necesitemos para dividir el job en pasos lógicos.
pipeline {
agent any
stages {
stage('Stage name 1') {
steps {
// Command 1
// Command 2
// Etc.
}
}
stage('Stage name 2') {
steps {
// Command 1
// Command 2
// Etc.
}
}
}
post {
failure {
emailext attachLog: true, body: '''Project name: $PROJECT_NAME
Build number: $BUILD_NUMBER
Build Status: $BUILD_STATUS
Build URL: $BUILD_URL''',
recipientProviders: [culprits()],
subject: 'Project \'$PROJECT_NAME\' is broken'
}
}
}
Si alguien hace push de código defectuoso al repo, queremos que el culpable reciba un email automatizado con información del job fallido. Para eso, usamos una sección failure dentro de una sección post. Esta parte del script solo se ejecutará si alguna de las etapas falla. Entonces, el job se parará. Jenkins no irá a la siguiente etapa si una falla. En su lugar, saltará a la sección failure. Jenkins entonces toma las direcciones de email de los últimos commits de Git y les envía un email con un enlace al build roto.
Job de comprobación de sintaxis VHDL
El único repo en nuestro diseño que no tiene testbench es el repo packages —en su lugar, usamos nuestro script check_syntax.tcl para verificar que el código es al menos VHDL válido.
En el primer paso de nuestro script de pipeline, llamamos a deleteDir(). Ese es uno de los comandos básicos disponibles en scripts de pipeline de Jenkins. Limpia el directorio de trabajo eliminando cualquier resto de builds previos.
En la siguiente línea, llamamos a git. Tenga en cuenta que este no es el comando git de Linux, sino un comando que referencia el plugin git de Jenkins. Le decimos que clone el repositorio en el workspace.
Finalmente, en la tercera línea de la etapa Create project, usamos la palabra clave sh para llamar a un comando de shell Linux. Aquí, cambiamos al directorio vivado y ejecutamos el script create_vivado_proj.tcl en modo batch de Vivado para recrear el proyecto Vivado.
Jenkinsfile:
pipeline {
agent any
stages {
stage('Create project') {
steps {
deleteDir() // clean up workspace
git 'https://github.com/jonasjj/Jenkins-demo-packages'
sh 'cd vivado && vivado -mode batch -source create_vivado_proj.tcl'
}
}
stage('Check VHDL syntax') {
steps {
sh 'cd vivado && vivado -mode batch -source check_syntax.tcl'
}
}
}
post {
failure {
emailext attachLog: true, body: '''Project name: $PROJECT_NAME
Build number: $BUILD_NUMBER
Build Status: $BUILD_STATUS
Build URL: $BUILD_URL''',
recipientProviders: [culprits()],
subject: 'Project \'$PROJECT_NAME\' is broken'
}
}
}
En la segunda etapa, llamada Check VHDL syntax, el proyecto Vivado ya existe, así que podemos saltar a ejecutar nuestro script Tcl. Usamos el comando shell de nuevo para ejecutar el archivo check_syntax.tcl, que saldrá con 0 en éxito, o 1 en error, causando que Jenkins marque el build como fallo.
Jobs de simulación VHDL
Para todos los otros jobs que no sean el repo packages, el comando one-liner git no funcionará para hacer checkout del código de GitHub. El problema es que estos repos tienen dependencias en forma de submódulos. Los submódulos referencian otros repositorios Git, que el simple comando git no trae por defecto. Pero está bien; podemos arreglar el problema usando la llamada checkout más versátil, también bien documentada en la página del plugin Git.
Jenkinsfile:
pipeline {
agent any
stages {
stage('Create project') {
steps {
deleteDir() // clean up workspace
checkout([$class: 'GitSCM',
branches: [[name: '*/master']],
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'SubmoduleOption',
disableSubmodules: false,
parentCredentials: false,
recursiveSubmodules: true,
reference: '',
trackingSubmodules: true]
],
submoduleCfg: [],
userRemoteConfigs: [[
url: 'https://github.com/jonasjj/Jenkins-demo-bcd_encoder'
]]
])
sh 'cd vivado && vivado -mode batch -source create_vivado_proj.tcl'
}
}
stage('Run simulation') {
steps {
sh 'cd vivado && vivado -mode batch -source run_simulation.tcl'
}
}
}
post {
failure {
emailext attachLog: true, body: '''Project name: $PROJECT_NAME
Build number: $BUILD_NUMBER
Build Status: $BUILD_STATUS
Build URL: $BUILD_URL''',
recipientProviders: [culprits()],
subject: 'Project \'$PROJECT_NAME\' is broken'
}
}
}
Finalmente, ejecutamos el script run_simulation.tcl en Vivado en la siguiente etapa.
El listado superior muestra el script usado en el repo bcd_encoder. Scripts idénticos, solo con diferentes URLs de repo, se usan para los repos counter, digit_selector, output_mux, reset, y seg7_encoder también.
Job de implementación FPGA
El repo seg7 contiene el módulo superior para nuestro proyecto FPGA. Trae todos los otros repos como submódulos. El script de pipeline es similar al usado para los jobs de solo simulación, pero con cuatro etapas añadidas: Run simulation, Run implementation, Generate bitstream, y Release bitfile.
Las dos primeras etapas crean el proyecto y ejecutan la simulación. Ya cubrí cómo funcionan en secciones previas. Las tres siguientes etapas funcionan de la misma forma que la etapa de simulación, pero con el script Tcl reemplazado por los relevantes para la tarea.
Jenkinsfile:
pipeline {
agent any
stages {
stage('Create project') {
steps {
deleteDir() // clean up workspace
checkout([$class: 'GitSCM',
branches: [[name: '*/master']],
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'SubmoduleOption',
disableSubmodules: false,
parentCredentials: false,
recursiveSubmodules: true,
reference: '',
trackingSubmodules: true]
],
submoduleCfg: [],
userRemoteConfigs: [[
url: 'https://github.com/jonasjj/Jenkins-demo-seg7'
]]
])
sh 'cd vivado && vivado -mode batch -source create_vivado_proj.tcl'
}
}
stage('Run simulation') {
steps {
sh 'cd vivado && vivado -mode batch -source run_simulation.tcl'
}
}
stage('Run synthesis') {
steps {
sh 'cd vivado && vivado -mode batch -source run_synthesis.tcl'
}
}
stage('Run implementation') {
steps {
sh 'cd vivado && vivado -mode batch -source run_implementation.tcl'
}
}
stage('Generate bitstream') {
steps {
sh 'cd vivado && vivado -mode batch -source generate_bitstream.tcl'
}
}
stage('Release bitfile') {
steps {
sh '''
PROJ_NAME=seg7
RELEASE_DIR=/usr/share/nginx/html/releases/
BASE_NAME=$PROJ_NAME-`date +"%Y-%m-%d-%H-%H:%M"`
BITFILE=$BASE_NAME.bit
INFOFILE=$BASE_NAME.txt
git log -n 1 --pretty=format:"%H" >> $INFOFILE
echo -n " $PROJ_NAME " >> $INFOFILE
git describe --all >> $INFOFILE
echo "" >> $INFOFILE
echo "Submodules:" >> $INFOFILE
git submodule status >> $INFOFILE
cp $INFOFILE $RELEASE_DIR
cp vivado/seg7.runs/impl_1/top.bit $RELEASE_DIR/$BITFILE
'''
}
}
}
post {
failure {
emailext attachLog: true, body: '''Project name: $PROJECT_NAME
Build number: $BUILD_NUMBER
Build Status: $BUILD_STATUS
Build URL: $BUILD_URL''',
recipientProviders: [culprits()],
subject: 'Project \'$PROJECT_NAME\' is broken'
}
}
}
La etapa final del job de implementación se llama Release bitfile. Contiene un script de shell que copia el archivo de programación FPGA recién generado a una carpeta de releases. El comando shell renombra el archivo para incluir el nombre del proyecto y un timestamp.
Para mantener trazabilidad, también generamos un archivo de texto que contiene el hash de Git del repo principal (seg7) y de todos los submódulos. Al trabajar con submódulos Git, no basta con guardar el hash del repo principal. Para generar un hash para el repo principal que incluya cambios en todos los submódulos, tendríamos que hacer commit del repo principal tras hacer pull y actualizar todos los submódulos. No queremos hacer eso automáticamente desde Jenkins.
Tenga en cuenta que implementar el FPGA para cada push, como hago en el ejemplo, probablemente no es lo que quiere para un proyecto Jenkins real. Puede tomar horas construir un proyecto FPGA a gran escala, y eso no funcionaría cuando tiene un equipo de desarrolladores haciendo push múltiples veces al día. En lugar de construir tras cada push al repo, puede configurar Jenkins para rutear el FPGA solo una vez al día. Por ejemplo, a medianoche, como muestra la captura de pantalla inferior de la página de configuración del job.
Disparando builds tras completar otros jobs
Nuestro proyecto de ejemplo consiste en varios repositorios Git, pero todos están unidos como submódulos Git. Excepto packages, todos los otros repos dependen de al menos un otro repositorio. La mayoría dependen del repositorio packages. Eche un vistazo al grafo de dependencias que presenté antes en este artículo para ver cómo encaja todo.
Por tanto, deberíamos disparar jobs no solo por pushes al repo en cuestión, sino también tras tocar cualquiera de los submódulos. Podemos lograr esto en Jenkins visitando cada job y configurando la opción Build after other projects are built apropiadamente.
La imagen inferior muestra el trigger para el proyecto bcd_encoder. Iniciará tras el repo packages, del que depende, complete un build con éxito.
El módulo superior depende de todos los otros repos. Los he añadido como proyectos a vigilar en una lista separada por comas. Tenga en cuenta que puede no querer hacer esto para el job de implementación FPGA si toma mucho tiempo rutear, como mencioné en la sección anterior.
Sirviendo los bitfiles usando Nginx
Como ya tenemos un servidor web corriendo, podemos usarlo para servir los archivos de release por HTTP. Quiero que todos los nuevos bitfiles aparezcan en la URL jenkins.vhdlwhiz.com/releases. Veamos cómo podemos usar Nginx para esto.
Nuestro job de implementación ya copia nuevos bitfiles a un directorio en la raíz HTML de Nginx, pero no lo hemos creado aún. Cree el directorio de releases y dé permisos de escritura al usuario Jenkins ejecutando los siguientes comandos:
mkdir /usr/share/nginx/html/releases/ chown jenkins.root /usr/share/nginx/html/releases/
Luego tenemos que hacer un cambio al archivo /etc/nginx/nginx.conf. Encuentre la sección server en el archivo de configuración con un nombre igual a su dominio. Añada la siguiente sección location dentro de ella, directamente debajo de la sección location root ('/'):
location ^~ /releases {
alias /usr/share/nginx/html/releases/;
autoindex on;
}
Finalmente, después de haber guardado el archivo, pruebe el archivo de configuración, y recargue el servidor Nginx:
nginx -t systemctl reload nginx
Si todo funcionó, debería poder listar el contenido del directorio de releases.
Para vincular la interfaz web de Jenkins al directorio de releases, quiero crear un enlace a él desde la barra lateral de Jenkins. Ya instalamos el plugin Sidebar Link que permite enlaces personalizados en la barra lateral.
El siguiente paso es ir a Manage Jenkins->Configure System y desplazarse a la sección Additional Sidebar Links. Aquí, podemos especificar el nombre y URL del nuevo enlace. El campo icono de enlace es opcional. Reutilicé uno de los iconos que venían con el servidor Jenkins.
Tras completar el paso anterior, debería tener ahora un enlace personalizado a los releases de bitfile en la barra lateral, completo con un bonito icono de carpeta.
Resumen
Jenkins puede ser una herramienta valiosa también para equipos FPGA. Automatizar tareas puede ahorrar tiempo a su empresa y mejorar la calidad de su código. Al usar triggers de build automáticos y pipelines de jobs automatizados, menos errores de código pasarán desapercibidos.
Como hemos visto en el proyecto de ejemplo presentado en este artículo, Jenkins puede implementar una suite completa de tests de regresión para su código VHDL. Muestra la salud actual de su proyecto en una agradable interfaz web gráfica, adecuada incluso para el gestor de proyectos más iletrado en VHDL.
Si desea probar Jenkins para desarrollo FPGA, recomiendo seguir los pasos de este artículo en una instancia VPS de UpCloud. Investigé a fondo todos los proveedores VPS hace unos años antes de mover VHDLwhiz a un VPS. Encontré que UpCloud era la alternativa más rápida y mejor. Sigo 100% satisfecho con el servicio.
Si decide abrir una cuenta en UpCloud, le pido amablemente que use mi enlace de referencia o código: NV78V6. No solo apoya a VHDLwhiz, sino que también obtiene $25 de crédito en UpCloud al usar el código promocional.
VHDL
- Entendiendo la diferencia entre señales y variables en VHDL
- Guía completa de conversiones de tipos VHDL con Numeric_Std y Std_Logic_Arith
- Lectura de imágenes BMP en testbenches VHDL con TEXTIO
- Diseño de un controlador RC Servo basado en FPGA con PWM preciso
- Dominando Xilinx Vivado: Analizador Lógico Integrado (ILA) y Entrada/Salida Virtual (VIO) para Depuración
- Ejemplo de procedimiento VHDL: Incrementando un vector Standard Logic
- Domine VHDL: Introducción Completa para Diseño FPGA y ASIC
- Inicialización de RAM de bloques FPGA desde archivos de texto usando la biblioteca TEXTIO de VHDL
- Domine la verificación aleatoria restringida con OSVVM
- Uso de listas de sensibilidad en procesos VHDL para diseño RTL fiable
- Introducción a VHDL: Construyendo tu primera compuerta AND