Exemplo de Quartus® II Tcl: relatórios de sincronização de caminhos arbitrários

author-image

Por

Os comandos list_path e report_timing Tcl são muito potentes, mas têm algumas limitações. Os pontos finais do caminho devem ser clocks, pinos ou registros. Além disso, esses comandos não relatam todos os caminhos combinacionais entre os pontos finais. Este exemplo de script avançado suporta o tempo de relatórios em caminhos arbitrários em seu projeto (incluindo pontos finais combinados) e relata todos os caminhos combinados entre os pontos finais. O script usa um algoritmo de pesquisa iterativa para encontrar caminhos. O algoritmo para em pinos e registra-se para evitar tempos de uso excessivos.

Você pode especificar nomes de nó, curinga ou nomes de grupo de tempo para a origem e o destino. As exclusões de grupo de tempo não são suportadas por este script; um aviso é exibido se você especificar um grupo de tempo que contém exclusões para os pontos finais e as exclusões são ignoradas.

Você pode direcionar a saída do script para um arquivo Comma Separated Value(.csv). O nome do arquivo padrão é p2p_timing.csv. Além disso, você pode direcionar a saída do script para um painel no relatório de temporizado do seu projeto. O nome padrão do painel é Timing ponto a ponto.

quartus_tan -t p2p_timing.tcl -project <project name> -, do nome <node|wildcard|timegroup name> -a <node| do <wildcard|timegroup> [-write_file] [nome do arquivo <output>] [-write_panel] [nome do painel <reporte>]

Se você quiser direcionar a saída para um arquivo diferente do nome do arquivo padrão, você deve especificar o nome do arquivo -write_file e -file <output> opções. Se você quiser direcionar a saída para um painel de relatório diferente do nome do painel de relatório padrão, você deve especificar o nome do painel -write_panel e -panel <reporte> opções.

Copie os seguintes comandos Tcl para um arquivo e o nome p2p_timing.tcl.

pacote exige cmdline 
load_package advanced_timing 
load_package relatório 

quartus global 
variável ::argv0 $::quartus(args) 

opções de configuração { \ 
   { "from.arg" "" "Nome do nó de origem" } \ 
   { "to.arg" "" "Nome do nó de destino" } \ 
   { "project.arg" "" "Nome do projeto" } \ 
   { "file.arg" "p2p_timing.csv" "Nome do arquivo de saída csv" } \ 
   { "write_file" "" "Gravar a saída em um arquivo" } \ 
   { "panel.arg" "Timing ponto a ponto" "Nome do painel de relatório" } \ 
   { "write_panel" "" "Gravar a saída em um painel de relatório" } \ 
} 

o conjunto de matriz optas [:cmdline::getoptions::argv0 $options "Opção ruim"] 
############################################################## 
# 
# Devolve uma lista de nomes de nó e IDs de nó correspondentes 
# para nomes de projetos que correspondam ao argumento de padrão. 
# Qualquer padrão que não corresponda a nomes no design tem um 
# lista vazia devolvida 
Exemplo: passe em "redefinir" e obter { redefinir 3 } de volta 
# 
############################################################## 
proc get_node_ids { padrão } { 
   conjunto de matriz name_to_node [lista] 

se { [string igual a "" $pattern] } { 
      retornar [lista] 
   }

# O padrão é na verdade o nome de um grupo de tempo? 
   # Se for, então o script recebe recursivamente os membros da 
   # o grupo de tempo. 
   membros definidos [get_all_global_assignments -name TIMEGROUP_MEMBER -section_id $pattern] 

# Se houver membros da coleção, 
   # o padrão é um grupo de tempo 
   se { 0 < [get_collection_size $members]} { 
      # Alerte se houver exclusões, porque o script 
      # ignora exclusões 
      se {0 < [get_collection_size [get_all_global_assignments -name TIMEGROUP_EXCLUSION -section_id $pattern]] } { 
         post_message tipo de aviso "Ignorar exclusões no timegroup $pattern" 
      } 

# Consulte cada item no grupo de tempo. 
      foreach_in_collection tarefa $members { 
         # Cada item da coleção é uma lista como esta: 
         # {$pattern} {TIMEGROUP_MEMBER} {nó/padrão real} 
         conjunto de sub_collection_names [get_node_ids [lindex $assignment 2]] 

foreach node_name [nomes de matriz sub_collection_names] { 
            definir name_to_node($node_name) $sub_collection_names ($node_name) 
         } 
      } 
   } outra { 
      Não é um grupo de tempo 
      # Iterar entre todos os nós de sincronização no design, 
      # verificando se o nome corresponde ao padrão especificado 
      foreach_in_collection node_id [get_timing_nodes -type all] { 
         definir node_name [get_timing_node_info -info name $node_id] 
         se { [string match [escape_brackets $pattern] $node_name] } { 
            definir name_to_node($node_name) $node_id 
         } 
      } 
   } 
   retorno [matriz obter name_to_node]
} 

############################################################## 
# 
# Este procedimento encontra caminhos combinados entre uma fonte 
Nó #e uma lista de nós de destino. Ele retorna uma lista de 
# caminhos entre os nós. Cada caminho é feito de um tríplice 
Nº de ID de nó, atraso de interconexão e atraso de célula a partir do 
Nº nó anterior. 
# O procedimento para de percorrer a netlist em um registro 
# ou pino, para que ele não encontre caminhos que se cruzam em registros. 
#
############################################################## 
proc find_combinational_paths_between {queue dest_nodes} { 
   conjunto num_iterations 0 
   definir caminhos [lista] 
   
enquanto {0 < [llength $queue]} { 
      # Informe sobre o andamento do loop a cada mil 
      # iterações incr num_iterations 
      se { 1000 == $num_iterations } { 
         conjunto num_iterations 0 
         post_message "Verificando [llength $queue] caminhos." 
      } 
      
# Pop o primeiro caminho da fila. 
      # A primeira vez que o procedimento é chamado, a fila 
      # é apenas um número, o nó de origem. 
      definir caminho [lindex $queue 0] 
      definir fila [lrange $queue 1 extremidade] 
      
# Obtenha o último nó no caminho, em seguida, no loop de foreach 
      # obter fanout a partir deste nó 
      definir last_triplet_in_path [lindex $path final] 
      definir last_node_in_path [lindex $last_triplet_in_path 0] 
 
# Extraia apenas as IDs de nó no caminho atual. 
      # Isso é usado mais tarde para garantir que os loops não sejam percorridos. 
      definir nodes_in_path [collapse_triplets_to_node_list $path] 

# Obtenha todos os fanouts do último nó neste caminho e faça 
      # novos caminhos com eles para empurrar na fila. 
      foreach n [get_timing_node_fanout $last_node_in_path] { 
         foreach { node_id ic_delay cell_delay } $n { 
            Quebrar 
         }
 
se { -1 != [lsearch $dest_nós $node_id] } { 
            # Se este nó no caminho estiver na lista de 
            # nós de destino, há um caminho. 
            # Adicione-o à lista de caminhos entre os nós 
            definir new_path $path lappend 
            new_path $n 
            caminhos lappend $new_path 
         } 

se { -1 == [lsearch $nodes_in_path $node_id] } { 
            # Se este nó no caminho não estiver no caminho 
            # já, isso não é um loop. Pressione-o no 
            # fila se for um nó combinado ou de clock. 
            # O caminho não é pressionado se este nó for um 
            # registrar ou fixar. 
            # Empurrando um novo caminho na fila como este, 
            # mesmo que este nó no caminho possa corresponder 
            # um nó final, garante o maior tempo possível 
            # caminhos são encontrados. 
            definir node_type [get_timing_node_info -info type $node_id] 
            comutador - exato - $node_type { 
               pente - 
               clk { 
                  definir next_path $path 
                  lappend next_path $n 
                  fila lappend $next_path 
               } 
               padrão { 
               } 
            } 
         } 
      }
   }
   devolução $paths 
} 

############################################################## 
# 
# Adiciona dois números de atraso e devolve o resultado. 
# Números de atraso estão no formulário "unidades de valor" onde as unidades 
# pode ser nanossegundos (ns) ou picoseconds (ps), e o valor pode 
# ser x{1,3} se as unidades são picoseconds, ou x+.y{1,3} se o 
# unidades são nanossegundos. Este procedimento normaliza os atrasos para 
# nanossegundos e adiciona os valores. 
Exemplo: add_delays "1.234 ns" "56 ps" # 
############################################################## 
proc add_delays { a b } { 
   se { ![ regexp {^([\d\.] +)\s+([np]s)$} $a corresponder a_value a_unit] } { 
      post_message tipo de erro "Não foi possível determinar partes do tempo: $a" 
   } 

se { ![ regexp {^([\d\.] +)\s+([np]s)$} $b corresponder b_value b_unit] } { 
      post_message tipo de erro "Não foi possível determinar partes do tempo: $b" 
   } 
  
# Converta tudo em nanossegundos, se necessário 
   se { [string equal -nocase ps $a_unit] } { 
      definir a_value_ps [formato "%.3f" $a_valor] 
      definir a_value [formato "%.3f" [expr { $a_value_ps / 1000 }]] 
   } 

se { [string equal -nocase ps $b_unit] } { 
      definir b_value_ps [formato "%.3f" $b_valor] 
      definir b_value [formato "%.3f" [expr { $b_value_ps / 1000 }]] 
   } 

# Agora as unidades são iguais, e nanossegundos. 
   # Basta adicionar os números juntos. 
   definir sum_value [formato "%.3f" [expr { $a_value + $b_value }]] 
  
devolver "$sum_valor ns" 
} 

############################################################## 
# 
# Formata e imprime os nomes dos nós no caminho com o  
# atrasos entre os nós. 
# 
############################################################## 
proc print_path_delays { caminho {iteração primeiro}} { 
   definir source_triplet [lindex $path 0] 
   definir source_node [lindex $source_triplet 0] 
   definir source_node_name [get_timing_node_info -info name $source_node] 
   definir source_node_loc [get_timing_node_info -info location $source_node] 
   
# Imprima os atrasos primeiro 
   se { [string igual a "primeiro" $iteration] } { 
      accumulate_data [lista "IC(0,000 ns)" "CELL(0,000 ns)"] 
   } outra { 
      definir ic_delay [lindex $source_triplet 1] 
      definir cell_delay [lindex $source_triplet 2] 
      accumulate_data [lista "IC($ic_delay)" "CELL($cell_delay)"] 
   } 
   accumulate_data [list $source_node_loc $source_node_name] 
   print_accumulated_data 

# Recurse no resto do caminho 
   se { 1 < [llength $path] } { 
      print_path_delays [lrange $path 1 extremidade] outra 
   } 
} 

############################################################## 
# 
# Resume os atrasos de IC e de células no caminho especificado e 
# retorna uma lista com atraso total de interconexão e célula total 
# atraso. 
# 
############################################################## 
proc end_to_end_delay { caminho } { 
   conjunto ic_total "0,000 ns" 
   conjunto cell_total "0,000 ns" 
   
# Isso passa por nós 1 para terminar no caminho porque o 
   O primeiro nó no caminho é a fonte e cada nó no 
   # path contém os atrasos do nó anterior a ele. O 
   A fonte # não tem nó anterior a ele, portanto, não tem atrasos. 
   foreach n [lrange $path 1 extremidade] { 
      foreach { node_id ic_delay cell_delay } $n { 
         Quebrar 
      } 
      definir ic_total [add_delays $ic_total $ic_delay] 
      definir cell_total [add_delays $cell_total $cell_delay] 
   } 

devolução [lista $ic_total $cell_total] 
} 

##############################################################
# 
# Garante que a fonte especificada e os destinos existam no 
# design, encontra os caminhos combinacionais entre eles e 
# imprime os caminhos. 
# 
############################################################## 
proc find_paths_and_display { dest fonte } { 
   fontes de conjunto de matrizes [get_node_ids $source] 
   dests array set [get_node_ids $dest] 

conjunto nodes_exist 1 

# Certifique-se de que os nós nomeados existam 
   se { 0 == [llength [matriz obter fontes]] } { 
      definir nodes_exist 0 
      post_message tipo de erro "Nós correspondentes $source foram encontrados em seu design." 
   } 
   se { 0 == [llength [array get dests]] } { { 
      conjunto nodes_exist 0 
      post_message tipo de erro "Nós correspondentes $dest foram encontrados em seu design." 
   } 

# Se eles o fizerem, encontre caminhos.   se { $nodes_exist } { 
      # Obtenha a lista de ids de nó de destino 
      definir dest_node_ids [lista] 
      foreach d [dests nomes de matriz] { 
         lappend dest_node_ids $dests($d) 
      } 

# Ande por todos os nós 
      foreach s [fontes de nomes de matriz] { 
         definir caminhos [find_combinational_paths_between $sources($s) $dest_node_ids] 
         se { 0 == [llength $paths] } {  
            post_message "Nenhum caminho combinacional existe de $s a $dest" 
         } outra { 
            foreach path $paths { 
               # Imprima o caminho 
               print_path_delays $path 

# Soma os atrasos de interconexão e células e 
               # imprima-os sob o caminho. 
               foreach {total_ic_delay total_cell_delay } [end_to_end_delay $path] { 
                  Quebrar 
               } 
               accumulate_data [lista $total_ic_delay $total_cell_delay] 
               accumulate_data [lista [add_delays $total_ic_delay $total_cell_delay]] 

Há duas chamadas para print_accumulated_data 
               # aqui, um para imprimir as somas da interconexão 
               # e atrasos de células, e um para gerar um espaço em branco 
               # linha na saída. 
               print_accumulated_data print_accumulated_data 
            } 
         } 
      }
   } 
} 

############################################################## 
# 
# Um caminho é feito de trigêmeos de informações - id de nó, 
# atraso de interconexão e atraso da célula. Este procedimento extrai 
# a id de nó de cada trigêmeo em ordem e retorna uma lista 
Nº dos ids de nó 
# 
############################################################## 
proc collapse_triplets_to_node_list { l } { 
   definir to_return [lista] 
   foreach triplet $l { 
      lappend to_return [lindex $triplet 0] 
   } 
   devolução $to_retorno 
} 

############################################################## 
# 
# Concatena informações para uma variável global em preparação 
# para que ele seja impresso. 
# 
############################################################## 
proc accumulate_data { dados } { 
   accum global set accum [concat $accum $data] 
}
 
############################################################## 
# 
# Imprima os dados acumulados. 
# Ele é impresso para fora padrão e opcionalmente para um arquivo em 
# formato CSV, se a alça do arquivo existir, e opcionalmente para um 
Painel de relatório # se o painel de relatório existir (não há um valor de -1) 
# 
############################################################## 
proc print_accumulated_data {} { 
   accum fh global panel_id 
   coloca [junte-$accum ",""] 

# Escrevê-lo em um arquivo? 
   se { [informações existem fh] } { 
      coloca $fh [junte-$accum ",")] 
   } 

# Adicione-o ao painel de relatório? 
   se { -1 != $panel_id } { 
      # Se a linha do painel de relatório não tiver 4 itens 
      # nele, pad-lo para 4. 
      enquanto { 4 > [llength $accum] } { 
         accum lappend [lista] 
      } 
      add_row_to_table -id $panel_id $accum 
   } 
   # Limpe as informações na variável global. 
   definir accum [lista] 
}

############################################################## 
############################################################## 
# 
# Fim dos procedimentos, início do roteiro 
# 
############################################################## 
##############################################################
# 
# variáveis globais para manter os dados para impressão e o painel 
ID #para um painel de relatório opcional 

definir accum [lista] 
conjunto panel_id -1 

se { [string igual a "" $opts(project)] } { 
   # Opções de uso de impressão se o script for chamado sem 
   # argumentos 
   coloca [:cmdline::uso $options] 
} elseif { [string igual a "" $opts(project)] } { 
   post_message tipo de erro "Especifique um projeto com a opção -project". 
} elseif { ! [project_exists $opts(projeto)] } { 
   post_message tipo de erro "O projeto $opts(project) não existe neste diretório." 
} elseif { [string igual a "" $opts(de)] } { 
   post_message tipo de erro "Especifique um nome ou padrão curinga com a opção -from". 
} elseif { [string igual a "" $opts(a)] } { 
   post_message tipo de erro "Especifique um nome ou padrão curinga com a opção -to". 
} outra { 
   definir cur_revision [get_current_revision $opts(projeto)] 
   project_open $opts(project) -revisão $cur_revision 

# Tente criar a netlist de sincronização. Este comando falharia 
   # se quartus_fit ainda não foi executado, por exemplo. 
   se { [catch { create_timing_netlist } msg ] } { 
      post_message tipo de erro $msg 
   } outra { 

# Prepare-se para gravar a saída em um arquivo, se necessário 
      se { 1 == $opts(write_file) } { 
         se { [catch {open $opts(file) w} fh] } { 
            post_message tipo de erro "Não foi possível abrir $opts(write_file): $fh" fh 
         } outra { 
            post_message "Escrevendo a saída para $opts(arquivo)" 
            # Adicione algumas informações introdutórios ao arquivo de saída 
            coloca $fh "Relatório de caminhos de $opts(de) a $opts(para)" 
            coloca $fh "Gerado em [formato do clock [segundos do clock]]". 
            coloca $fh "" coloca $fh "atraso de IC,atraso de célula,localização do nó,nome do nó" 
         } 
      } 

# Prepare-se para gravar a saída em um painel de relatório, se necessário 
      se { 1 == $opts(write_panel) } { 
         # Carregue o relatório, exclua o painel se ele já existir, 
         # crie um novo painel e adicione a linha de títulos. 
         load_report 
         definir panel_id [get_report_panel_id "Analisador de sincronização|| $opts(painel)"] 
         se { -1 != $panel_id } { 
            delete_report_panel -id $panel_id 
         } 
        definir panel_id [create_report_panel tabela "Analisador de sincronização|| $opts(painel)"] 
        add_row_to_table -id $panel_id [lista "atraso de IC" "atraso de célula" "Localização do nó" "Nome do nó") 
      } 
      find_paths_and_display $opts(de) $opts(para) 

# feche o arquivo de saída, se necessário 
      se { [informações existem fh] } { fechar $fh } 

# Salve o painel de relatório, se necessário 
      se { -1 != $panel_id } { 
         save_report_database 
         unload_report 
      } 
   } 
   project_close 
}    

O conteúdo desta página é uma combinação de tradução humana e por computador do conteúdo original em inglês. Este conteúdo é fornecido para sua conveniência e apenas para informação geral, e não deve ser considerado completo ou exato. Se houver alguma contradição entre a versão em inglês desta página e a tradução, a versão em inglês prevalecerá e será a determinante. Exibir a versão em inglês desta página.