Como vincular bibliotecas IP do Quartus Prime ao VUnit
Você já quis executar uma simulação VHDL que inclui um núcleo Quartus IP por meio da estrutura de verificação VUnit?
Isso é o que o engenheiro da FPGA Konstantinos Paraskevopoulos tinha em mente, mas ele não conseguiu encontrar um tutorial adequado para isso. Felizmente, ele usou seu talento para descobrir como e teve a gentileza de compartilhá-lo com o VHDLwhiz por meio este artigo convidado .
Vamos dar a palavra a Konstantinos!
Muitas vezes, é desejável incorporar IPs predefinidos do Catálogo de IPs do Quartus em seu design ao simular seu sistema com o VUnit. Assim, o tutorial a seguir visa fornecer ao leitor o conhecimento de geração, incorporação e vinculação de bibliotecas IP externas do Quartus ao ambiente VUnit.
Novo no VUnit? Confira este tutorial:Introdução ao VUnit
Visão geral
Este tutorial consiste em três partes principais:
- Uma breve descrição do IP selecionado
- Etapas necessárias para gerar e vincular as bibliotecas apropriadas
- Verificação usando VUnit e Modelsim
Requisitos
- Quarto
- Baixe o Quartus Prime
- QSYS deve estar em seu PATH para STEP 2 (opção CMD)
- Intel Modelsim
- Consulte este artigo para saber como instalar o ModelSim gratuitamente
- ModelSim deve estar em seu PATH
- VUnit
- Consulte este artigo para saber como instalar o VUnit gratuitamente
- Python 3.6 ou superior
- Baixar Python
- O Python deve estar no seu caminho
Também pressupõe ter conhecimento básico de VHDL e habilidades do ModelSim.
Design em teste
Para nosso cenário, utilizamos o Parallel Adder IP da lista Quartus Integer Arithmetic IP.

Nosso projeto aceita três vetores de entrada de 16 bits e gera o resultado adicionado em um vetor de 17 bits.
Etapa 1:Gerar IP
Geramos nosso somador na janela do catálogo IP clicando duas vezes no componente somador paralelo em Biblioteca/Funções básicas/Aritmética.
Depois de fornecer um nome e personalizar nosso componente com base em nossas necessidades, clicamos no botão Gerar HDL no canto inferior direito.

Neste ponto, uma janela aparecerá, conforme ilustrado na figura a seguir.
Observação: Devemos definir o
Create simulation model
sob o Simulation
seção para VHDL ou Verilog para gerar os arquivos de simulação, pois a opção padrão é none. Se não escolhermos um, o given_ip_name.spd
arquivo não será gerado, fazendo com que a próxima etapa falhe. 
O processo acima gera um arquivo e uma pasta em nosso
quartus
diretório:- Arquivo:
given_ip_name.ip
- Pasta:
given_ip_name
A pasta envolve
.vhd
e .v
arquivos que precisam ser adicionados posteriormente em nosso run.py
roteiro. Etapa 2:gerar arquivos de simulação de IP
- GUI: Selecione Tools ➤ Generate Simulator Setup Script for IP e especifique o diretório de saída na janela de prompt,
- CMD: Utilizando os comandos do Qsys, podemos gerar os mesmos arquivos digitando no terminal o seguinte comando:
ip-setup-simulation --quartus-project= <project's_QPF_filepath> --output-directory= <my_dir>
Usando um dos dois métodos acima, instruímos o Quartus a gerar um diretório para cada simulador suportado que contém um script para criar e compilar as bibliotecas IP.
Etapa 3:gerar e compilar bibliotecas de IP para Modelsim
O próximo passo é encontrar o
msim_setup.tcl
script no mentor
pasta criada pela etapa anterior e duplique-a com o nome setup.tcl
. Em seguida, no setup.tcl
arquivo, descomente os comandos ilustrados e defina o $QSYS_SIMDIR
variável. # # QSYS_SIMDIR is used in the Quartus-generated IP simulation script to # # construct paths to the files required to simulate the IP in your Quartus # # project. By default, the IP script assumes that you are launching the # # simulator from the IP script location. If launching from another # # location, set QSYS_SIMDIR to the output directory you specified when you # # generated the IP script, relative to the directory from which you launch # # the simulator. # # set QSYS_SIMDIR <script generation output directory> # # # # Source the generated IP simulation script. source $QSYS_SIMDIR/mentor/msim_setup.tcl # # # # Set any compilation options you require (this is unusual). # set USER_DEFINED_COMPILE_OPTIONS <compilation options> # set USER_DEFINED_VHDL_COMPILE_OPTIONS <compilation options for VHDL> # set USER_DEFINED_VERILOG_COMPILE_OPTIONS <compilation options for Verilog> # # # # Call command to compile the Quartus EDA simulation library. dev_com # # # # Call command to compile the Quartus-generated IP simulation files. com # #
Após alterar e salvar o
setup.tcl
, podemos executar com segurança o arquivo Tcl usando o vsim
comando. vsim -c -do "do setup.tcl; quit"
Isso gera as bibliotecas compiladas no
mentor
pasta. Etapa 4:link VUnit
Agora que as bibliotecas IP foram geradas, devemos vinculá-las usando o python
run.py
roteiro. Confira a figura abaixo para entender melhor a estrutura de diretórios do nosso exemplo. A topologia inicial consistia na pasta raiz
demo
, o tb
, vunit
e quartus
pastas. Todas as subpastas e arquivos em quartus
são geradas por meio da estrutura do Quartus após criar um projeto e concluir as etapas 1 a 3. Observação: O Quartus gera mais arquivos e pastas, mas a imagem abaixo mostra os que nos interessam.

Usando esta visão distinta da topologia como referência, podemos especificar nosso caminho ROOT e o(s) caminho(s) para as bibliotecas geradas, conforme mostrado abaixo.
Observe que
sim_files
é o diretório que especificamos na etapa 2 onde a pasta do mentor foi armazenada. from vunit import VUnit from os.path import join, dirname, abspath # ROOT root = join(dirname(__file__), '../') # Path to generated libraries path_2_lib = '/quartus/sim_files/mentor/libraries/' # ROOT
Depois de criar uma instância VUnit chamada
vu
, podemos especificar uma biblioteca de design para nosso código VHDL e vincular quaisquer bibliotecas externas necessárias:# Create VUnit instance by parsing command line arguments vu = VUnit.from_argv() # create design's library my_lib = vu.add_library('my_lib') # Link external library vu.add_external_library("parallel_adder", root + path_2_lib + "parallel_adder")
E, finalmente, adicione nossos arquivos de origem. Eles estão localizados em três subpastas em
given_ip_name
diretório:parallel_add_191
synth
sim
O
synth
e sim
dirs contêm as mesmas informações, ou seja, o design de nível superior do nosso IP. Porém, a formatação desses arquivos no nosso caso é em VHDL. Eles podem estar em Verilog, e isso depende do idioma escolhido na etapa 1. Caso nosso design de nível superior envolva subcomponentes, também devemos incluir seus arquivos de origem. Eles estão localizados em subpastas no
given_ip_name
diretório, como o parallel_add_191
componente no nosso caso. my_lib.add_source_files(join(root,'quartus','parallel_adder','sim','parallel_adder.vhd')) my_lib.add_source_files(join(root,'quartus','parallel_adder','parallel_add_191','sim','parallel_adder_parallel_add_191_oh4guxa.vhd')) my_lib.add_source_files(join(root,'tb','tb_demo.vhd')) testbench = my_lib.entity("tb_demo") vu.main()
Banco de teste
Para começar, você pode conferir este link para aprender sobre os fundamentos da formação do testbench VUnit.
De volta ao nosso testbench, adicionamos as bibliotecas VUnit necessárias junto com qualquer outra biblioteca que gostaríamos de empregar e definir nossos sinais.
Observação: A execução do processo em nosso exemplo é sequencial. Assim, os sinais de controle (chamados de bandeiras ) são usados para notificar um processo se ele deve começar ou terminar.
library IEEE; use IEEE.std_logic_1164.all; use ieee.numeric_std.all; library vunit_lib; context vunit_lib.vunit_context; entity tb_demo is generic ( runner_cfg : string:= runner_cfg_default); end tb_demo; architecture sim of tb_demo is constant clk_period : time := 10 ns; signal clk : std_logic := '0'; signal rst : std_logic := '0'; -- INPUTS signal data_a : std_logic_vector(0 to 15):= (others => '0'); signal data_b : std_logic_vector(0 to 15):= (others => '0'); signal data_c : std_logic_vector(0 to 15):= (others => '0'); -- OUTPUTS signal result : std_logic_vector(0 to 16); -- CONTROL FLAGS signal reset_done :boolean := false; signal sim_done :boolean := false; signal start_sim :boolean := false;
Seguindo, instanciamos nosso UUT. O Quartus fornece exemplos de instanciação de componentes para VHDL e Verilog sob as convenções de nome de arquivo
ip_name_inst.vhd
e ip_name_inst.v
. begin -- Unit Under Test UUT : entity work.parallel_adder port map ( data0x => data_a, -- parallel_add_input.data0x data1x => data_b, -- .data1x data2x => data_c, -- .data2x result => result -- parallel_add_output.result );
Os dois primeiros processos iniciados são
clk_process
e reset_rel
. Enquanto o último está suspenso após redefinir e dirigir o reset_done
sinalizar para true
, o clk_process
opera durante todo o tempo de simulação. clk_process : process begin clk <= '1'; wait for clk_period/2; clk <= '0'; wait for clk_period/2; end process clk_process; reset_rel : process begin rst <= '1'; wait for clk_period*2; wait until rising_edge(clk); rst <= not rst; reset_done <= true; wait; end process reset_rel;
Agora que a redefinição foi feita, podemos invocar o
test_runner
processo para executar nossos testes. Além disso, o executor de teste permanece ativo até o sim_done
sinalizador é direcionado para true
, que ocorre no último processo. test_runner : process begin test_runner_setup(runner, runner_cfg); wait until reset_done and rising_edge(clk); iterate : while test_suite loop start_sim <= true; if run("test_case_1") then info ("Start"); info (running_test_case); wait until sim_done; end if; end loop; test_runner_cleanup(runner); end process test_runner;
Por fim, o
data_generator
processo executa várias adições atribuindo valores às três entradas do nosso somador paralelo utilizando um for
ciclo. Observação: Este processo é acionado quando o
test_runner
o processo o instrui configurando o start_sim
bandeira. Enquanto no final deste processo, ele gera o sim_done
sinalizador, comandando o executor de teste para pausar a simulação. data_generator : process constant tag2 : log_level_t := new_log_level("INFO", fg => blue, bg => black, style => bright); variable a,b,c,d : integer; begin wait until start_sim; wait until rising_edge(clk); show(display_handler, tag2); if running_test_case = "test_case_1" then for i in 0 to 10 loop data_a <= std_logic_vector(to_unsigned(i+10,data_a'length)); data_b <= std_logic_vector(to_unsigned(i+20,data_a'length)); data_c <= std_logic_vector(to_unsigned(i+30,data_a'length)); wait until rising_edge(clk); a := to_integer(unsigned(data_a)); b := to_integer(unsigned(data_b)); c := to_integer(unsigned(data_c)); d := to_integer(unsigned(result)); log( integer'image(a) &" + "& integer'image(b) &" + "& integer'image(c) &" = "& integer'image(d), tag2); end loop; end if; sim_done <= true; end process data_generator;
Verificação
Para executar o caso de teste e verificar se tudo funciona conforme o esperado, podemos executar o
run.py
script do diretório em que está localizado simplesmente digitando no terminal o seguinte comando. python ./run.py -v
Observação: Um registrador personalizado foi usado para melhor ilustração em nossa saída, que é visível ao fornecer o verboso
-v
opção. Além disso, como apenas um caso de teste é definido, não precisamos fornecer uma opção para especificá-lo. 
Por fim, para verificar nossos resultados no ModelSim, podemos digitar o seguinte comando:
python ./run.py --gui

(Clique na imagem para ampliá-la)
Conclusão
Para concluir, aprendemos neste tutorial sobre como incorporar e testar IPs do Quartus que residem no catálogo de IP para o VUnit. Empregamos um IP predefinido. No entanto, também podemos integrar IPs personalizados empacotados dessa maneira em nosso ambiente VUnit.
Confira este tutorial VUnit se ainda não o fez:
Introdução ao VUnit
VHDL
- O que é SigFox?
- Introdução ao VUnit
- Como criar uma lista de strings em VHDL
- Como parar a simulação em um testbench VHDL
- Como criar um controlador PWM em VHDL
- Como criar um testbench de autoverificação
- Como a tecnologia fornece um elo crucial nas cadeias de suprimentos éticos
- Máquina de esgrima de elo de corrente:como funciona e o beneficiário
- como escorvar a bomba de engrenagem hidráulica
- como preparar a bomba hidráulica do trator ford