Artigos anteriores da série
Na verdade, este artigo apareceu como um arquivo DOC em junho. Em seguida, um bloco de cinco artigos foi escrito simultaneamente. Mas fazer o upload de um arquivo DOC para o Habr é outra tarefa. Portanto, aconteceu que o momento para isso apareceu apenas agora (e mais dois estão definhando em antecipação). Ao fazer upload, percebi que se você não ficar saturado com o espírito dos artigos anteriores, este parece meio chato. Portanto, se houver tal desejo, refresque a memória pelo menos o último artigo, ou melhor estes dois ( "Fazendo a cabeça de um analisador de barramento USB ..." e "Simulando o comportamento de um projeto Quartus ..." ).
Introdução
Então, o modelo acabado, onde posso obtê-lo? Existe um projeto que resolve exatamente o mesmo problema do analisador que estamos desenvolvendo, mas tem alguns recursos. O primeiro recurso é para FPGAs Xilinx. Em segundo lugar, é totalmente não documentado. Funciona de alguma forma. Você pode até comprar uma placa de ensaio pronta, preenchê-la com código binário pronto ... e obter algumas funcionalidades. Quem precisa de um dispositivo a qualquer custo pode simplesmente seguir este caminho. Mas ninguém sabe como desenvolvê-lo. Esse projeto está aqui . No diretório \ ulpi_wrapper \ testbenchhá um conjunto de arquivos para testar o subsistema do wrapper em torno do ULPI. Eles recomendam a modelagem no ambiente Icarus Verilog, mas eu vasculhei e não encontrei nenhuma descrição sensata de como fazer isso na linguagem SystemC. Portanto, decidi continuar trabalhando no ambiente ModelSim. Se eu soubesse como ia acabar ... Mas não sabia. Portanto, comecei a pesquisar. No decorrer da apresentação, sucessos e fracassos serão mostrados. Vamos começar com as falhas, para que todos possam ver como não fazer isso.
Tentativa sem sucesso de fazer tudo "de frente"
No início, decidi pegar um exemplo pronto e executá-lo na modelagem. Com o movimento usual de minha mão (e nós colocamos nossa mão no último artigo ), criei um conjunto de testes contendo arquivos em Verilog e SystemC. Aconteceu algo assim: Eu
inicio o ModelSim e não vejo nada no grupo de trabalho que esteja relacionado ao SystemC. Eu vejo o código Verilogo, mas o código Sishny não.
Se você olhar os logs, verá que eles não tentaram coletá-los. Qual é o problema?
Informações úteis sobre como configurar o arquivo * .do
Sabe-se que o arquivo * .do é usado para executar o ModelSim. Mas como amante de fazer tudo com um "rato", nunca olhei dentro dele. Vamos procurar e abrir! Existe apenas um desses arquivos no diretório do projeto. Provavelmente é disso que precisamos.
Nós abrimos. No início - a montagem de todos os tipos de itens de serviço e arquivos incluídos no projeto.
Veja o texto
transcript on
if ![file isdirectory verilog_libs] {
file mkdir verilog_libs
}
if ![file isdirectory vhdl_libs] {
file mkdir vhdl_libs
}
vlib verilog_libs/altera_ver
vmap altera_ver ./verilog_libs/altera_ver
vlog -vlog01compat -work altera_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/altera_primitives.v}
vlib verilog_libs/lpm_ver
vmap lpm_ver ./verilog_libs/lpm_ver
vlog -vlog01compat -work lpm_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/220model.v}
vlib verilog_libs/sgate_ver
vmap sgate_ver ./verilog_libs/sgate_ver
vlog -vlog01compat -work sgate_ver {c:/intelfpga_lite/17.1/quartus/eda/sim_lib/sgate.v}
Mas no final - claramente a montagem das coisas de que precisamos, julgo isso pelo nome do arquivo ulpi_wrapper.v :
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc" lalala
add wave *
view structure
view signals
run 10 us
Sério. Há uma montagem de um módulo Verilog e não há sugestão de montagem de módulos no SystemC. A única pena é que este arquivo DO é criado automaticamente toda vez que você inicia a simulação, então você não pode simplesmente pegá-lo e editá-lo. Ele é criado por um script TCL muito complexo. Não há desejo de governá-lo. Mas depois do artigo sobre o bairro alegre , provavelmente está claro que tal ninharia não é motivo para desistir. Certamente, tudo já está lá. A única pena é que a documentação diz que "você pode fazer o script desta forma, ou você pode fazer isso", e não há dicas para exemplos. Bem, vamos deduzir tudo experimentalmente. Crie um arquivo C: \ Work \ UsbHead1 \ SystemCPlay \ myrun.do e tente transferir o controle para ele. Primeiro, tentamos fazer assim:
O arquivo DO principal ainda continua a ser gerado, mas seu final fica assim:
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_b2p_adapter.sv}
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -L UsbHead1 -voptargs="+acc" lalala
do C:/Work/UsbHead1/SystemCPlay/myrun.do
Vemos que o arquivo Verilog ainda está compilado, então o processo de modelagem ainda é iniciado (embora eu tenha visto isso durante a execução dos testes, mas agora posso dizer com certeza que o comando vsim inicia esse processo), após o qual o controle é transferido para nosso roteiro. Este script deve controlar o processo de exibição. Mas ainda não conseguimos gerenciar a montagem. Se os arquivos coletados não forem suficientes, o sistema cairá por engano antes que possamos fazer qualquer coisa. Bem, ótimo, vamos tentar a última configuração.
E aqui começa a diversão. É tão importante que vou enquadrá-lo.
Seleciono um script, mas não é selecionado. Eu vou para as configurações (eu tenho a opção selecionada anteriormente). Eu escolho, não escolhido. E assim - até com o rosto azul. Até eu perceber, até descobrir como vencer - matei a noite! Descobriu-se que se você apenas selecionar um arquivo, o botão Aplicar permanecerá cinza. E as mudanças não serão lembradas. É imperativo tornar o botão Aplicar preto por meio da edição de outros parâmetros da caixa de diálogo! Na foto acima, é exatamente preto. Se permanecer esmaecido, as alterações não serão salvas e tudo não será reconfigurado para usar o script.
O script ainda está sendo formado, mas seu final tornou-se mais conveniente para nós.
vlog -sv -work UsbHead1 +incdir+C:/Work/UsbHead1/UsbHead1/synthesis/submodules {C:/Work/UsbHead1/UsbHead1/synthesis/submodules/UsbHead1_master_0_timing_adt.sv}
do "C:/Work/UsbHead1/SystemCPlay/myrun.do"
Finalmente, o processo de construção de códigos-fonte para o projeto está totalmente à nossa mercê! Maravilhoso! Naquela época, eu só conseguia encontrar o documento SystemC Verification with ModelSim escrito para Xilinx. Mas ModelSim está na África ModelSim também. Usando os exemplos deste documento e as amostras do arquivo DO criado em experimentos anteriores, fiz o seguinte texto de script (não se assuste com a abundância de chaves, abaixo vamos jogar fora quase tudo, também substituiremos caminhos absolutos por relativos mais tarde, neste estágio eu apenas tirei tudo dos exemplos e amostras geradas automaticamente).
vlog -vlog01compat -work work +incdir+C:/Work/UsbHead1/SystemCPlay {C:/Work/UsbHead1/SystemCPlay/ulpi_wrapper.v}
vlib sc_work
sccom –g –I C:/intelFPGA_lite/17.1/quartus/cusp/systemc/include –work sc_work C:/Work/UsbHead1/SystemCPlay/ulpi_driver.cpp
Rufar de tambores ... E ModelSim nos declara:
Se omitirmos todas as palavras obscenas, então não tenho nada a dizer ... Mas tal caminho já foi passado! E onde posso conseguir outro modelo ULPI? Claro, fiz um acordo com amigos estrangeiros que estão profissionalmente envolvidos em projetos sérios para FPGAs. Especialmente para mim, eles abriram acesso remoto a uma máquina com um ModelSim licenciado para o fim de semana. A segunda panqueca também revelou ser irregular: a versão de 64 bits, mesmo em uma forma licenciada, não funciona com o SystemC. Mas, no final, consegui brincar com a versão de 32 bits do ModelSim licenciado. Portanto, continuamos a história ...
Algumas palavras sobre documentação
Então. Agora que tenho acesso ao software licenciado, é hora de conversar sobre onde procurar informações e onde obter inspiração. Na web, as informações sobre o idioma são bastante vagas. Mas na entrega do sistema, existem os seguintes diretórios úteis:
C: \ modeltech_10.2c \ docs \ pdfdocs - documentação, incluindo arquivos em formato PDF. Gostei dos arquivos modelsim_se_ref.pdf (ModelSim SE Command Reference Manual), modelsim_se_user.pdf (ModelSim SE Manual do Usuário) e modelsim_se_tut.pdf (ModelSim SE Tutorial). Não há muito sobre o idioma em si, mas sobre como conectar arquivos e como resolver problemas de dialeto - bastante.
A seguir, diretório útil C: \ modeltech_10.2c \ examples... Existem exemplos de arquivos * .do prontos e arquivos cpp e h prontos. O exemplo mais útil para nós é C: \ modeltech_10.2c \ examples \ systemc \ vlog_sc . Ele mostra como acessar o código SystemC do código Verilog. Nós, no final, iremos exatamente desta forma.
O diretório C: \ modeltech_10.2c \ include \ systemc contém o código-fonte para a biblioteca de tipo de linguagem. Não é um livro de referência ruim. Como se costuma dizer, há peixes para a ausência de peixes e o câncer.
Tudo, desde catálogos. Agora, o título de um livro maravilhoso, com o qual você pode aprender muito sobre a linguagem e sobre os métodos de programação nela. SystemC - From the Ground Up, Second Edition. Por David C. Black, Jack Donovan, Bill Bunton, Anna Keist.
Dialetos SystemC
Então. Tendo obtido acesso ao sistema de trabalho, montei com alegria o projeto, de acordo com o roteiro criado anteriormente. Ele montou sem erros! O primeiro modelo do GitHub concordou em trabalhar conosco! Desejando executar o benchmark, adicionei o arquivo ulpi_wrapper_tb.cpp do mesmo diretório ao projeto e obtive muitos erros. Digamos que haja um erro na linha:
m_vpi_handle = vpi_handle_by_name ((const char *) name, NULL);
difícil de consertar, mas ainda possível. Mas a linha
// Update systemC TB
if(sc_pending_activity())
sc_start((int)(time_value-m_last_time),SC_NS);
evocou pensamentos ruins. Não há função sc_pending_activity () nas bibliotecas. Existe uma função sc_pending_activity_at_current_time () , mas eu nem me preocupei em lidar com ela. Em vez de mil palavras de explicação, darei um dump:
E havia 44 arquivos com este texto (* .exe, * .dll, etc.).
Você poderia tentar reescrever tudo ... Mas é necessário? Deixe-me lembrar que eu realmente comecei tudo isso, porque queria usar tudo que estava pronto. Posso desenvolver tudo em um ambiente livre em um SystemVerilog puro, se realmente perder muito tempo ... Vim aqui para não perder tempo, mas para economizar! Mas na verdade ... O principal é não esquecer o que estamos fazendo. Queremos usar o modelo de barramento ULPI. Ela se recompôs. Surgiram problemas ao tentar construir um sistema de teste completo a partir do exemplo ... Por que isso? Bem, o sistema completo não funciona, e tudo bem. Vamos dominar um modelo, sem olhar para o funcionamento do sistema, por tentativa e erro.
Eliminando mal-entendidos baseados no dialeto
Então. Estaremos fazendo um sistema misto. O módulo com o modelo será escrito na linguagem SystemC, e irei enviar ações de teste para ele e o módulo que está sendo desenvolvido na linguagem Verilog. Ou seja, você precisa fazer com que o módulo ulpi_driver apareça no grupo de trabalho .
Examinando exemplos de arquivos * .do da entrega do ModelSim, simplifiquei muito o script e, no final, fiz o seguinte:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}
sccom -g ../../SystemCPlay/ulpi_driver.cpp
sccom -link
Não há erros, mas o módulo também não apareceu no grupo. Examinando os arquivos de exemplo (lembre-se, o melhor exemplo que implementa essa combinação de idiomas está no diretório C: \ modeltech_10.2c \ examples \ systemc \ vlog_sc ), percebi que a seguinte linha deve ser adicionada ao final do arquivo ulpi_driver.cpp :
SC_MODULE_EXPORT(ulpi_driver);
A documentação do ModelSim diz que esses são recursos do dialeto. E voila! Aqui está, nosso módulo:
Verdade, o menu Criar Wave (discutimos esse menu no último artigo ) não está disponível para ele. E ele não tem portas. Historicamente, lidei primeiro com os portos, mas metodicamente - vou adiar a história sobre eles para mais tarde. Caso contrário, você terá que editar o código duas vezes. Para evitar isso, vamos primeiro fazer uma pequena preparação.
Fazendo um gerador de relógio
Descobrimos que o modelo tem algumas diferenças do ULPI real. A primeira diferença é que o clock de 66 MHz deve ser gerado pelo chip. O que vemos no modelo?
sc_in<bool> clk_i;
Transtorno! Vamos começar o retrabalho! Todo o trabalho, a menos que indicado de outra forma, é realizado no arquivo ulpi_driver.h.
Altere o tipo de porta. Isso foi:
sc_in<bool> clk_i;
tornou-se (também alterei o nome da porta):
sc_inout<bool> clk;
Aprendi com o livro que um gerador real é inserido adicionando uma variável:
sc_clock oscillator;
Definimos os parâmetros no construtor. Como resultado, o construtor assume a forma:
//-------------------------------------------------------------
// Constructor
//-------------------------------------------------------------
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS))
{
A última linha é apenas para isso. Se desejar, você pode até mesmo iniciar a simulação, clicar duas vezes no módulo usb_driver , puxar clk66 para a cabana temporária e executar um pouco o processo de simulação . Já vimos como funciona o gerador:
Não esqueçamos de mudar o nome do sinal do clock no local onde começa a thread principal. Isso foi:
SC_CTHREAD(drive, clk_i.pos());
Passou a ser:
SC_CTHREAD(drive, clk.pos());
Links internos foram substituídos. Mas como é lindo trazer o sinal para fora, não achei. Talvez eu apenas não tenha qualificações. Mas, de uma forma ou de outra, todas as tentativas de retirar a porta foram malsucedidas. Sempre havia algo no caminho. Eu até encontrei uma discussão em um fórum em que o autor precisava fazer o mesmo. A equipe decidiu que ele só pode ser encaminhado para as portas de entrada. Mas precisamos sair! Portanto, fazemos isso.
Adicione uma função de stream abaixo do construtor:
void clkThread(void)
{
while (true)
{
wait(oscillator.posedge_event());
clk.write (true);
wait(oscillator.negedge_event());
clk.write (false);
}
}
E adicione um link para ele no construtor de classe:
SC_THREAD(clkThread);
Deixe-me mostrar a área do construtor atual para dar uma visão holística do resultado atual:
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS))
{
SC_CTHREAD(drive,clk.pos());
SC_THREAD(clkThread);
m_reg[ULPI_REG_VIDL] = 0x24;
m_reg[ULPI_REG_VIDH] = 0x04;
m_reg[ULPI_REG_PIDL] = 0x04;
m_reg[ULPI_REG_PIDH] = 0x00;
m_reg[ULPI_REG_FUNC] = 0x41;
m_reg[ULPI_REG_OTG] = 0x06;
m_reg[ULPI_REG_SCRATCH] = 0x00;
}
void clkThread(void)
{
while (true)
{
wait(oscillator.posedge_event());
clk.write (true);
wait(oscillator.negedge_event());
clk.write (false);
}
}
Tudo. A primeira edição está concluída.
Fazendo um barramento de dados bidirecional
ULPI possui um barramento de dados bidirecional. E no modelo, vemos a seguinte descrição dele:
sc_out <sc_uint<8> > ulpi_data_o;
sc_in <sc_uint<8> > ulpi_data_i;
Transtorno! Primeiro faremos um espaço em branco com base no barramento de saída e, em seguida, mudaremos tudo para ele. Por onde começar? Pelo fato de que o barramento deve ser capaz de ir para o terceiro estado, e o tipo sc_uint <8> funciona apenas com dados binários. O tipo sc_lv <8> nos ajudará . Portanto, alteramos a declaração do pneu para:
sc_inout <sc_lv<8> > ulpi_data_o;
Agora vá para o arquivo ulpi_driver.cpp e procure por todas as chamadas para o barramento ulpi_data_o lá . Intuitivamente, percebi que havia apenas um lugar para consertar:
O mesmo texto.
void ulpi_driver::drive_input(void)
{
// Turnaround
ulpi_dir_o.write(false);
ulpi_nxt_o.write(false);
ulpi_data_o.write(0x00);
wait(oscillator.posedge_event());
}
Altere a linha selecionada para
ulpi_data_o.write("ZZZZZZZZ");
Tudo. Agora você pode, em vez de duas linhas:
sc_inout <sc_lv<8> > ulpi_data_o;
sc_in <sc_uint<8> > ulpi_data_i;
escreva um:
sc_inout <sc_lv<8> > ulpi_data;
e substitua todas as referências a variáveis antigas no h-nick e no cpp-shnik por referências à variável ulpi_data .
Adicionar aliases de porta
Então. Depois de uma longa pesquisa, cheguei à conclusão (possivelmente errônea) que no ambiente ModelSim é fácil pegar e ver as portas de um módulo separado no SystemC usando a GUI, sem sorte. No entanto, se este módulo for inserido no sistema de teste, eles aparecerão. Mas enquanto vasculhava a teoria, descobri como definir aliases para nomes de porta. O construtor final da classe agora se parece com isto:
SC_HAS_PROCESS(ulpi_driver);
ulpi_driver(sc_module_name name): sc_module(name),
m_tx_fifo(1024),
m_rx_fifo(1024),
oscillator ("clk66",sc_time(15,SC_NS)),
rst_i ("rst"),
ulpi_data ("data"),
ulpi_dir_o ("dir"),
ulpi_nxt_o ("nxt"),
ulpi_stp_i ("stp")
{
SC_CTHREAD(drive,clk.pos());
SC_THREAD(clkThread);
m_reg[ULPI_REG_VIDL] = 0x24;
m_reg[ULPI_REG_VIDH] = 0x04;
m_reg[ULPI_REG_PIDL] = 0x04;
m_reg[ULPI_REG_PIDH] = 0x00;
m_reg[ULPI_REG_FUNC] = 0x41;
m_reg[ULPI_REG_OTG] = 0x06;
m_reg[ULPI_REG_SCRATCH] = 0x00;
}
Fazendo um sistema de teste
Bem então. Eu não pude fazer tudo automaticamente para que dois módulos depurados (a cabeça do analisador e o modelo de barramento ULPI) saltassem para o arquivo de teste. Mas vamos fazer pelo menos um teste de cabeça e depois adicionar ULPI a ele. Usando a técnica do último artigo , fiz um sistema de teste para o arquivo ULPIhead.sv . Eu tenho um arquivo chamado sim1.v e imediatamente renomeou sim1.sv .
Em seguida, adicionei o módulo ulpi_driver com as alças . O script final myrun.do se parece com este:
vlog +../../SystemCPlay {../../MyCores/ULPIhead.sv}
sccom -g ../../SystemCPlay/ulpi_driver.cpp
sccom -link
vlog +../../SystemCPlay {../../SystemCPlay/sim1.sv}
vsim -voptargs="+acc" sim1
A última linha é torturada. Sem ele, o código Verilog não tinha portas. Ao alterar os parâmetros de otimização, eliminamos este problema. Eu vi naquele arquivo * .do que foi criado para simular nosso sistema bem no começo, quando tudo ainda estava feito na máquina. É verdade que existe uma longa fila. Acabei de encontrar a chave que resolve o problema e copiei. E então - eu não gosto de longas filas, joguei fora tudo o que era desnecessário.
Agora adicionamos o bloco ULPI ao sistema de teste e fazemos um teste simulado. Apenas para ter certeza de que todos os sinais do relógio estão funcionando e os barramentos estão configurados com os valores corretos.
Eu tenho esse teste.
Observe o texto.
`timescale 1ns / 1ns
module sim1 ;
reg ulpi_dir ;
wire source_valid ;
wire ulpi_stp ;
reg ulpi_clk ;
reg ulpi_nxt ;
reg reset_n ;
reg read ;
reg [31:0] writedata ;
wire ulpi_rst ;
reg clk ;
wire [7:0] source_data ;
reg write ;
wire [7:0] ulpi_data ;
reg source_ready ;
reg [1:0] address ;
wire [31:0] readdata ;
always
begin
clk = 1;
#5;
clk = 0;
#5;
end
ULPIhead DUT
(
.ulpi_dir (ulpi_dir ) ,
.source_valid (source_valid ) ,
.ulpi_stp (ulpi_stp ) ,
.ulpi_clk (ulpi_clk ) ,
.ulpi_nxt (ulpi_nxt ) ,
.reset_n (reset_n ) ,
.read (read ) ,
.writedata (writedata ) ,
.ulpi_rst (ulpi_rst ) ,
.clk (clk ) ,
.source_data (source_data ) ,
.write (write ) ,
.ulpi_data (ulpi_data ) ,
.source_ready (source_ready ) ,
.address (address ) ,
.readdata (readdata ) );
ulpi_driver ULPI
(
.clk (ulpi_clk),
.rst (ulpi_rst),
.data (ulpi_data),
.dir (ulpi_dir),
.nxt (ulpi_nxt),
.stp (ulpi_stp)
);
initial
begin
reset_n = 1'b0;
source_ready = 1;
writedata = 0;
address = 0;
read = 0;
write = 0;
#20
reset_n = 1'b1;
end
endmodule
Conclusão
No mínimo, dominamos a modelagem na linguagem SystemC usando o sistema ModelSim. No entanto, descobriu-se que isso requer acesso à versão licenciada de 32 bits. A versão gratuita e a versão licenciada de 64 bits não oferecem essa oportunidade. Pelo que entendi, tudo pode ser feito de forma totalmente gratuita no sistema Icarus Verilog, mas não descobri exatamente como fazer isso. Para mim, ficou mais fácil acessar o ModelSim necessário. No próximo artigo, usaremos esse conhecimento para modelar nossa cabeça.
No decorrer do trabalho, modificações bastante complexas dos modelos foram feitas. Os arquivos resultantes podem ser baixados aqui .