Spyglass概述

目录

设置设计阶段

项目文件(Project File)概述

Spyglass CDC流程

运行Spyglass

其他

获取帮助的方式

调试方式

演示

Spyglass是Synopsys公司推出的一款用于设计早期用于保证RTL代码质量,排查CDC问题的RTL代码sign off工具,用于排查问题,尽早解决问题。也可以做低功耗,DFT等相关分析。下图为Spyglass的原理流程示意图,其中首先是设计准备阶段,需要准备源文件,.sgdc文件,lib文件,使用.prj文件对该设计进行管理,同时会给出相应的报告,下一个阶段您选择并运行目标(Gaol)目标是规则(Rules)的集合。规则就是一条条约束,用于检查的最基本单位。最后是分析结果阶段,此阶段使您能够分析目标运行的结果。目前使用更多的在CDC与Lint部分

设置设计阶段

添加文件选项

文件

描述

脚本使用方式

举例

源列表文件

包括扩展名为 .spp 或 .f 的文件。这些文件包含设计文件、设计选项或两者的组合。

read_file -type sourcelist <file>

read_file -type sourcelist sources.f

HDL 库

包含预编译的 VHDL 或 Verilog 文件。

set_option lib  <logical_name>  <physical_path>

set_option lib  MyLib   /a/b/mylib_path

SGDC文件

SGDC文件约束,这些设计约束用于提供在 RTL 中不明显的附加设计信息。

read_file -type sgdc  <filename>

read_file -type sgdc  constraints.sgdc

技术文件库

包括使用这些库单元的结构化网表所需的 Synopsys .lib、.sglib、.plib 和 .gateslib 文件。

read_file -type  <gateslib | sglib | plib> <lib name>

read_file -type sglib  library.sglib

waive文件

屏蔽一些不会在窗口显示的警告信息或者error,就是屏蔽一些规则检查

read_file -type waiver <prj_name.awl>

read_file -type waiver waiver.awl

上述给了使用脚本读入文件的方式,也可以使用鼠标点击的方式在GUI界面进行添加相关的文件。不在此赘述。

项目文件(Project File)概述

读入相关文件,包括设计文件,约束文件,库文件等,同时设置运行选项(set_option),SpyGlass选项用于控制SpyGlas运行,并对所有目标具有全局性(单独总结),其次设置参数(set_parameter)在目标中使用SpyGlass参数来指导目标本身的行为。有些选项可以使用set_goal_option在目标中使用,需要注意这些设置是否可以用于该选项。参数有common parameter也有一些目标特有的,因此需要在使用注意参数的使用场景。

如下是一个prj的例子。

#Set the Top of the Design Tree

set_option top training

#Read in individual RTL Files (Verilog/VHDL/Netlists):

read_file -type hdl RTL/training.v

#Read in a file list from an location defined in an environmental file

###read_file -type sourcefile $env(SOURCE)/filelist.f

#Read in a Design Constraints File that contains clocks, resets, set_case_anlysis, etc..

read_file -type sgdc SGDC/training.sgdc

#Define a waiver file to put EXCEPTIONS INTO and a default waiver file.

read_file -type waiver training.swl

set_option default_waiver_file training.swl

#Set a global option that all Verilog will be System Verilog Compliant.

set_option enableSV yes

#Where do we find our rule sets:#Where do we find our rule sets:

current_methodology $env(SPYGLASS_HOME)/GuideWare2.0/block/rtl_handoff

#Define goal specific parameters and options

current_goal cdc/cdc_verify

set parameter ac sync debug yes #Turns on extra debuggin during cdc verify goal

set_parameter enable_handshake no #Turn off Handshake Sync Scheme

set_goal_option XXXX # Only for this goal

#Define a scenario that uses different SGDC file “–goals cdc_verify@mode1”

surrent_goal cdc/cdc_verify –scenario mode1

read_file –type sgdc mode1.sgdc

这里对其进行简单解释哈~首先是设置设计Top的名称,就是的module名称。接下来读入相关文件,其中sgdc可以使用DC吐出来的SDC生成,然后在其基础上进行修改,不修改直接使用是不被允许的,官方文件中明确指出需要进一步修改。生成的文件对于大型设计SGDC的书写可以提供很多便利。进而是全局的设置选项,根据自己的需要进行相关的设置。接下来是设置当前的方法,当前的目标,然后目标对应的参数与目标设置。可以使用GUI界面进行检查。同时对于需要重新建立一个项目文件的情况,可以自己使用GUI界面新建一个prj,然后进行相关基础设置后保存,再次基础上进行进一步的修改。

重点是介绍SGDC的书写,将在SGDC的书写(单独总结)篇进行详述

另外一个需要提及的是,current_methodology,方法分为blockhandoff以及sochandoff,在使用的时候根据需要进行选择。其中SoC-level的目标集更多一些~如下图所示。其中M是必选,O为可选,但是整体来看其实主要是在lay_out层次的增加,一般我们只是用rtl检查的话差距不大。

Spyglass CDC流程

如下图所示,该图介绍了CDC流程的目标及其对应的规则。每一个目标有做对应的事情,需要执行完上一个目标后同时达到该目标后,才可以进行到下一个阶段,可以看到,每一个目标对应的规则是不同的,因此可能会导致一些问题遗漏,同时可能导致下一个目标的error的由于上一个目标没有完成的遗留问题。

接下来简单介绍每一个步骤需要完成的事情

  • cdc_setup 该步骤需要做的指定精确完整的SpyGlass CDC验证所需的约束、参数和其他设计组件。
    • 输出
      • autoclocks.sgdc
      • generated clocks.sgdc
      • autoresets.sgdc
      • generated_resets.sgdc
  • cdc_setup_check 检查完整正确的设置
    • 此步骤的目的是执行约束健全性检查,并检测任何冲突/缺失的约束。对于正确的CDC分析,在约束文件中定义时钟并正确重置非常重要
    • 先决条件
      • 前一个目标cdc/cdc_setup中的零错误和零警告
    • 输出
      • Clock_info03a‐报告时钟不受限制的故障
      • Clock_info03b报告数据引脚绑定到常数的触发器
      • Clock_info05‐报告多路复用器上收敛的时钟信号
  • clock_reset_integrity 修正时钟和复位的问题
  • cdc_verify 发现时钟域交叉中的结构问题,修正设计中的异步问题
    • 输出为可能违反规则的设计问题
  • cdc_verify_struct  使用已验证的模块验证SoC

执行起来的时候是一步一步来的~务必确保上一个阶段是没有问题的再进行下一步的检查

运行Spyglass

在运行Spyglass的时候,假设你已经有一个prj文件。

运行时使用如下命令 spyglass –project training.prj –batch –goals cdc/cdc_setup | tee cdc.log &

在分析调试时去掉–batch选项即可,即spyglass –project training.prj –goals cdc/cdc_setup &

使用时根据不同的目标更换对应选项即可

在你没有prj文件的时候,可以使用gui界面快捷的设置相关参数,保存一个文件用于后续的使用。输入spyglass,不加任何选项,读入文件,将不需要检查的文件设置为stop,设置一个top选项,选择一个目标goal,打开incremental Mode,运行,保存prj文件即可,就可以看到一个软件生成的文件了

其他

获取帮助的方式

gui界面的help界面,安装目录下的的doc文件夹下的官方指导文件,论坛博文

调试方式

一般打开电路图进行debug,打开incremental Mode可以方便观察那些改动引起或是消除了那些error

演示

见如下链接(哔哩哔哩视频的形式),主要是异步FIFOlintCDC检查。未完成

本文参考:1,SpyGlass_CDCMethodology_GuideWare2.0_UserGuide.pdf 2,SpyGlass_CDC_Training_Slides_510_20Aug2013.pdf

以下是一个使用IgH EtherCAT驱动器控制雷赛伺服的示例程序: ```c #include <stdio.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sched.h> #include <sys/mman.h> #include <time.h> #include "ecrt.h" #define FREQUENCY 1000 #define PERIOD_NS (1000000000 / FREQUENCY) // EtherCAT Master static ec_master_t *master = NULL; // EtherCAT Slave static ec_slave_config_t *slave = NULL; // EtherCAT Domain static ec_domain_t *domain = NULL; // EtherCAT Master Clock static ec_master_clock_t *clock = NULL; // Sync Manager static ec_sync_info_t sync_info = { .dir = EC_DIR_OUTPUT, .n = 0, .type = EC_SYNC_CYCLOP, .size = 0, .offset = 0, .config_index = 0 }; static ec_sync_info_t *syncs[] = { &sync_info, NULL }; // Process Data struct { uint16_t status_word; int32_t target_position; int32_t target_velocity; int16_t target_torque; } __attribute__((packed)) pd; // Process Data Mapping static ec_pdo_entry_info_t pdo_entries[] = { { 0x6041, 0x00, 16 }, // Control Word { 0x6040, 0x00, 16 }, // Status Word { 0x607A, 0x00, 32 }, // Target Position { 0x60FF, 0x00, 32 }, // Target Velocity { 0x6071, 0x00, 16 }, // Target Torque }; static ec_pdo_info_t pdo_info = { .n_entries = 5, .entry = pdo_entries }; static ec_sync_pdo_entry_t sync_pdos[] = { { 0x03A0, 0x01, &pdo_info, 0 }, { 0x03A0, 0x02, &pdo_info, 0 }, { 0x03A0, 0x03, &pdo_info, 0 }, { 0x03A0, 0x04, &pdo_info, 0 }, { 0x03A0, 0x05, &pdo_info, 0 }, { 0x03A0, 0x06, &pdo_info, 0 }, { 0x03A0, 0x07, &pdo_info, 0 }, { 0x03A0, 0x08, &pdo_info, 0 }, }; static ec_sync_info_t sync_pdos_info[] = { { .n = 0, .type = EC_SYNC_PDO, .size = 0, .offset = 0, .config_index = 0xff }, { .n = 8, .type = EC_SYNC_PDO, .size = sizeof(pd), .offset = 0, .config_index = 0 }, { .n = 0, .type = EC_SYNC_END, .size = 0, .offset = 0, .config_index = 0 } }; // Thread static pthread_t thread; static int thread_run = 1; // Application void *application(void *arg) { struct timespec ts; uint32_t cycle_ns = PERIOD_NS; int32_t position; uint16_t status; int wkc; int error; int32_t pos; int32_t vel; int16_t tor; // Set thread priority struct sched_param param = { .sched_priority = 99 }; pthread_setschedparam(pthread_self(), SCHED_FIFO, &param); // Run EtherCAT Master ecrt_master_activate(master); ecrt_master_application_time(master, cycle_ns, 0); ecrt_master_sync_reference_clock(master); ecrt_master_sync_slave_clocks(master); while (thread_run) { // Wait for next cycle clock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_nsec += cycle_ns; if (ts.tv_nsec >= 1000000000) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } pthread_mutex_lock(&ecrt_master_mutex); ecrt_master_application_time(master, cycle_ns, 0); ecrt_master_sync_reference_clock(master); wkc = ecrt_master_sync_slave_clocks(master); pthread_mutex_unlock(&ecrt_master_mutex); // Read current position pthread_mutex_lock(&ecrt_domain_mutex); position = EC_READ_S32(domain->io_map + 0); pthread_mutex_unlock(&ecrt_domain_mutex); // Write target position, velocity and torque pd.target_position = position; pd.target_velocity = 1000; pd.target_torque = 0; // Send process data pthread_mutex_lock(&ecrt_domain_mutex); ecrt_domain_queue(domain); pthread_mutex_unlock(&ecrt_domain_mutex); // Wait for next cycle clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL); // Receive process data pthread_mutex_lock(&ecrt_domain_mutex); ecrt_domain_process(domain); status = EC_READ_U16(domain->io_map + 2); pos = EC_READ_S32(domain->io_map + 4); vel = EC_READ_S32(domain->io_map + 8); tor = EC_READ_S16(domain->io_map + 12); pthread_mutex_unlock(&ecrt_domain_mutex); printf("Status: 0x%04X, Position: %d, Velocity: %d, Torque: %d\n", status, pos, vel, tor); // Check for errors error = ecrt_master_error(master); if (error) { printf("Error: %s (%d)\n", ecrt_master_strerror(error), error); break; } // Check for watchdog if (wkc < sync_pdos_info[0].n * ec_slave_config_reg_pdo_entry_count(slave, 0)) { printf("Watchdog!\n"); thread_run = 0; } } // Stop EtherCAT Master pthread_mutex_lock(&ecrt_master_mutex); ecrt_master_deactivate(master); pthread_mutex_unlock(&ecrt_master_mutex); return NULL; } // Main int main(int argc, char **argv) { int result; // Initialize EtherCAT Master if (ecrt_master_open(&master)) { printf("Failed to open EtherCAT Master!\n"); return -1; } if (ecrt_master_open_config(master, NULL)) { printf("Failed to open EtherCAT Master configuration!\n"); ecrt_master_close(master); return -1; } if (ecrt_master_application_ready(master)) { printf("Failed to make EtherCAT Master application ready!\n"); ecrt_master_close(master); return -1; } // Get EtherCAT Slave configuration slave = ecrt_master_slave_config(master, 0x03A0, 0x0000); if (!slave) { printf("Failed to get EtherCAT Slave configuration!\n"); ecrt_master_close(master); return -1; } // Initialize EtherCAT Domain domain = ecrt_master_create_domain(master, &sync_info, syncs); if (!domain) { printf("Failed to create EtherCAT Domain!\n"); ecrt_master_close(master); return -1; } // Initialize EtherCAT Master Clock clock = ecrt_master_clock(master); if (!clock) { printf("Failed to initialize EtherCAT Master Clock!\n"); ecrt_master_close(master); return -1; } // Register process data mapping if (ecrt_slave_config_pdos(slave, EC_END, sync_pdos_info)) { printf("Failed to register process data mapping!\n"); ecrt_master_close(master); return -1; } // Configure EtherCAT Slave if (ecrt_slave_config_dc(slave, PERIOD_NS, 0, PERIOD_NS / 2, 0, 0)) { printf("Failed to configure EtherCAT Slave!\n"); ecrt_master_close(master); return -1; } // Activate EtherCAT Slave if (ecrt_slave_activate(slave)) { printf("Failed to activate EtherCAT Slave!\n"); ecrt_master_close(master); return -1; } // Initialize process data pd.status_word = 0; pd.target_position = 0; pd.target_velocity = 0; pd.target_torque = 0; // Start thread result = pthread_create(&thread, NULL, &application, NULL); if (result) { printf("Failed to start thread!\n"); ecrt_slave_deactivate(slave); ecrt_master_close(master); return -1; } // Wait for thread pthread_join(thread, NULL); // Deactivate EtherCAT Slave ecrt_slave_deactivate(slave); // Deinitialize EtherCAT Master ecrt_master_close(master); return 0; } ``` 在该示例程序中,使用了以下步骤来控制雷赛伺服: 1. 初始化EtherCAT Master。 2. 获取EtherCAT Slave配置。 3. 初始化EtherCAT Domain和Sync Manager。 4. 注册Process Data Mapping。 5. 配置EtherCAT Slave。 6. 激活EtherCAT Slave。 7. 启动线程,该线程周期性地更新Process Data,并将其发送到EtherCAT Slave。 8. 等待线程结束。 9. 停止EtherCAT Slave。 10. 关闭EtherCAT Master。 在线程中,使用了以下步骤来更新Process Data并将其发送到EtherCAT Slave: 1. 读取当前位置。 2. 设置目标位置、目标速度和目标力矩。 3. 将Process Data发送到EtherCAT Slave。 4. 等待下一个周期。 5. 接收Process Data。 6. 检查错误和看门狗。 请注意,该示例程序仅供参考,您需要根据实际情况对其进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值