cocotb平台用VCS仿Xilinx FPGA

cocotb平台用VCS仿Xilinx FPGA

文章目录

概要

本文介绍了基于cocotb框架的AXI Stream接口验证方法。主要内容包括:1)开发AXIS VIP库,实现字节级数据发送(axis_tx_byte)、随机接收(axis_rx)和总线监控(axis_monitor_byte)功能;2)以Xilinx AXIS FIFO为例,展示VIP库的调用方法,包括测试平台搭建、数据生成和自动验证机制。该方案支持LSB配置,能模拟真实硬件背压情况,适用于AXIS接口模块的功能验证。代码提供完整的仿真环境,包含时钟复位控制、参考模型和计分板等组件,详细解析完整代码和Makefile文件。

建立cocotb仿真VIP库

例如新增一个axis.py文件,实现一个AXI Stream(AXIS)接口的驱动和监控功能,主要用于硬件验证(如使用cocotb框架)。核心功能包括数据发送、接收和监控,支持字节级操作。

import logging import math import copy import random from cocotb.triggers import RisingEdge from random_number import random_number classaxis:def__init__(self,signal,lsb=1): self.log = logging.getLogger("cocotb.tb") self.log.setLevel(logging.DEBUG) self.signal= signal self.lsb=lsb self.axis_monitor_data_byte=[] self.axis_monitor_data_fixed=[]asyncdefaxis_tx_byte(self,tx_data_byte): self.signal.tvalid.value =0 self.signal.tkeep.value =0 self.signal.tlast.value =0 tx_data=copy.deepcopy(tx_data_byte) tkeep_width=len(self.signal.tdata)//8 data_width =len(self.signal.tdata) temp = math.ceil(len(tx_data)/tkeep_width) tkeep=0if temp*tkeep_width>len(tx_data):for i inrange(temp*tkeep_width-len(tx_data)): tx_data.append(0) tkeep=tkeep+1if(self.lsb==0): tkeep=((2**tkeep_width-1)<<tkeep)&(2**tkeep_width-1)else: tkeep=((2**tkeep_width-1)>>tkeep)&(2**tkeep_width-1) data=[]for i inrange(temp): data_temp=0for m inrange(tkeep_width):if self.lsb==0: data_temp = data_temp +(tx_data[i*tkeep_width+m]<<(data_width-8-m*8))else: data_temp = data_temp +(tx_data[i*tkeep_width+m]<<(m*8)) data.append(data_temp)# self.log.info(list(map(hex,data)))for i inrange(len(data)): self.signal.tvalid.value =1 self.signal.tdata.value = data[i]if i ==len(data)-1: self.signal.tlast.value =1 self.signal.tkeep.value = tkeep else: self.signal.tlast.value =0 self.signal.tkeep.value =2**tkeep_width-1await RisingEdge(self.signal.aclk)while self.signal.tready.value ==0:await RisingEdge(self.signal.aclk) self.signal.tvalid.value =0asyncdefaxis_rx(self,tready_interval_max=8):whileTrue: tready_interval=random.randint(0, tready_interval_max) self.signal.tready.value =1await RisingEdge(self.signal.aclk) self.signal.tready.value =0for i inrange(tready_interval):await RisingEdge(self.signal.aclk)asyncdefaxis_monitor_byte(self): data=[] tkeep_width=len(self.signal.tdata)//8 data_width =len(self.signal.tdata)whileTrue:await RisingEdge(self.signal.aclk)if(self.signal.tready.value ==1)and(self.signal.tvalid.value ==1): temp=self.signal.tdata.value for m inrange(tkeep_width):if self.lsb==0:if(self.signal.tkeep.value&(1<<(tkeep_width-1-m))): data.append(((temp>>(data_width-8-m*8))&0xff))else:if(self.signal.tkeep.value&(1<<m)): data.append((temp>>(m*8))&0xff)if self.signal.tlast.value ==1: self.axis_monitor_data_byte.append(data) data=[]

AXIS接口发送(axis_tx_byte)
初始化信号状态为无效(tvalid=0),清空tkeep和tlast。将输入字节数组复制到临时变量tx_data中。

计算tkeep宽度(基于tdata信号位宽)和数据总宽度。根据输入数据长度计算需要多少个时钟周期传输完整数据。

处理数据对齐问题:若数据长度不是tkeep_width的整数倍,填充0并计算tkeep掩码。根据LSB(最低有效位)配置方向生成不同的掩码模式。

将字节数组转换为AXIS数据格式:每个时钟周期填充tkeep_width字节数据,按LSB配置决定字节序。在最后一个周期置tlast信号,并根据数据对齐情况设置tkeep。

AXIS接口接收(axis_rx)
模拟从设备就绪信号(tready)行为:随机间隔后置tready为1,保持一个周期后随机延迟0到tready_interval_max个周期。这种随机性模拟真实硬件中的背压情况。

AXIS数据监控(axis_monitor_byte)
持续监控AXIS总线:当tvalid和tready同时有效时,根据tkeep信号提取有效字节数据。根据LSB配置决定字节提取顺序,使用tlast信号判断数据包边界。完整数据包存入axis_monitor_data_byte列表。

调用VIP库仿Xilinx IP

以AXIS_FIFO为例

1. VIVIDO生成IP,完成设计。
在这里插入图片描述
2. 写python仿真代码

代码功能分析
该代码是一个基于Cocotb框架的AXIS(AXI Stream)接口测试环境,用于验证AXIS FIFO模块的功能。主要包含测试平台搭建、数据生成、参考模型、监视器和计分板等组件。

测试平台结构
TB类
是测试平台的核心:

  • 初始化时钟、复位、AXIS从接口和主接口
  • 提供复位控制、数据生成、参考模型等功能
  • 包含监视器和计分板用于自动验证
classTB:def__init__(self, dut): self.dut = dut self.log = logging.getLogger("cocotb.tb") self.log.setLevel(logging.DEBUG) cocotb.start_soon(Clock(dut.s_axis_aclk,10, units="ns").start()) self.axis_slave_if = axis(signal_axis_slave(dut)) self.axis_master_if = axis(signal_axis_master(dut)) self.data_drv=[] self.data_mon=[]

关键测试流程
复位序列
通过控制复位信号确保DUT处于已知状态:

asyncdefreset(self): self.dut.s_axis_tvalid.value =0 self.dut.s_axis_tdata .value =0 self.dut.s_axis_tkeep .value =0 self.dut.s_axis_tlast .value =0 self.dut.m_axis_tready.value =0 self.dut.s_axis_aresetn.setimmediatevalue(1)await RisingEdge(self.dut.s_axis_aclk)await RisingEdge(self.dut.s_axis_aclk) self.dut.s_axis_aresetn.value =0await Timer(1, units="us")await RisingEdge(self.dut.s_axis_aclk)await RisingEdge(self.dut.s_axis_aclk) self.dut.s_axis_aresetn.value =1

数据生成与验证
数据生成
使用随机数创建测试激励:

defseq_model(self,length): data =[]for i inrange(length): data.append(random.randint(0,2**8-1))return data 

计分板比较驱动数据和监视数据:

asyncdefscoreboard(self): i=0whileTrue:await RisingEdge(self.dut.s_axis_aclk)if(len(self.data_drv)>0)and(len(self.data_mon)>0):if(self.data_drv[0]!=self.data_mon[0]):print("data_drv[%d]="%(i),list(map(hex,self.data_drv[0])))print("data_mon[%d]="%(i),list(map(hex,self.data_mon[0])))assert self.data_drv[0]==self.data_mon[0] self.log.info("The test %d is success,length is %d !!!",i,len(self.data_mon[0])) self.data_drv.pop(0) self.data_mon.pop(0) i=i+1

测试用例执行
run_test
函数组织完整的测试流程:

  1. 初始化测试环境
  2. 执行复位
  3. 启动监视和验证协程
  4. 发送测试数据
  5. 等待验证完成
asyncdefrun_test(dut,length,tready_interval_max): tb=TB(dut)await tb.reset() cocotb.start_soon(tb.axis_slave_if.axis_rx(tready_interval_max=tready_interval_max))await Timer(1, units="us")await RisingEdge(dut.s_axis_aclk) cocotb.start_soon(tb.axis_slave_if.axis_monitor_byte()) cocotb.start_soon(tb.monitor()) cocotb.start_soon(tb.scoreboard())for i inrange(len(length)): temp = tb.seq_model(length[i])await tb.axis_master_if.axis_tx_byte(temp) tb.data_drv.append(tb.ref_model(temp))

测试工厂配置
通过TestFactory创建参数化测试:

factory = TestFactory(run_test) factory.add_option("length",[[2000,2001,2002,2003]]) factory.add_option("tready_interval_max",[0,1])

该配置会生成多个测试用例,覆盖不同数据长度和tready间隔的组合。
完整代码如下:

import random import cocotb import logging from cocotb.clock import Clock from cocotb.triggers import RisingEdge from cocotb.triggers import Timer from cocotb.regression import TestFactory from axis import axis classTB:#初始化搭建测试端口,时钟,复位,axis_slave_if,axis_master_ifdef__init__(self, dut): self.dut = dut self.log = logging.getLogger("cocotb.tb") self.log.setLevel(logging.DEBUG) cocotb.start_soon(Clock(dut.s_axis_aclk,10, units="ns").start()) self.axis_slave_if = axis(signal_axis_slave(dut)) self.axis_master_if = axis(signal_axis_master(dut)) self.data_drv=[] self.data_mon=[]#复位axis_fifo_0asyncdefreset(self): self.dut.s_axis_tvalid.value =0 self.dut.s_axis_tdata .value =0 self.dut.s_axis_tkeep .value =0 self.dut.s_axis_tlast .value =0 self.dut.m_axis_tready.value =0 self.dut.s_axis_aresetn.setimmediatevalue(1)await RisingEdge(self.dut.s_axis_aclk)await RisingEdge(self.dut.s_axis_aclk) self.dut.s_axis_aresetn.value =0await Timer(1, units="us")await RisingEdge(self.dut.s_axis_aclk)await RisingEdge(self.dut.s_axis_aclk) self.dut.s_axis_aresetn.value =1await RisingEdge(self.dut.s_axis_aclk)await RisingEdge(self.dut.s_axis_aclk)#生成随机数据defseq_model(self,length): data =[]for i inrange(length): data.append(random.randint(0,2**8-1))return data #参考模型,直接将输入数据赋值给输出数据defref_model(self,din): dout = din return dout #监视器,用axis_vip中的axis_monitor_data_byte将输出数据存储到data_mon中asyncdefmonitor(self):whileTrue:await RisingEdge(self.dut.s_axis_aclk)iflen(self.axis_slave_if.axis_monitor_data_byte)>0: self.data_mon.append(self.axis_slave_if.axis_monitor_data_byte[0]) self.axis_slave_if.axis_monitor_data_byte.pop(0)## 计分板,比较data_drv和data_mon是否相等asyncdefscoreboard(self): i=0whileTrue:await RisingEdge(self.dut.s_axis_aclk)if(len(self.data_drv)>0)and(len(self.data_mon)>0):if(self.data_drv[0]!=self.data_mon[0]):print("data_drv[%d]="%(i),list(map(hex,self.data_drv[0])))print("data_mon[%d]="%(i),list(map(hex,self.data_mon[0])))assert self.data_drv[0]==self.data_mon[0] self.log.info("The test %d is success,length is %d !!!",i,len(self.data_mon[0])) self.data_drv.pop(0) self.data_mon.pop(0) i=i+1#端口信号映射,将axis_fifo_0的端口映射到axis_vip的端口classsignal_axis_master:def__init__(self,dut): self.aclk = dut.s_axis_aclk self.tvalid = dut.s_axis_tvalid self.tready = dut.s_axis_tready self.tdata = dut.s_axis_tdata self.tkeep = dut.s_axis_tkeep self.tlast = dut.s_axis_tlast #端口信号映射,将axis_fifo_0的端口映射到axis_vip的端口classsignal_axis_slave:def__init__(self,dut): self.aclk = dut.s_axis_aclk self.tvalid = dut.m_axis_tvalid self.tready = dut.m_axis_tready self.tdata = dut.m_axis_tdata self.tkeep = dut.m_axis_tkeep self.tlast = dut.m_axis_tlast asyncdefrun_test(dut,length,tready_interval_max): tb=TB(dut)#搭建测试环境 tb.log.info("Running test!")#开始测试await tb.reset()#复位 cocotb.start_soon(tb.axis_slave_if.axis_rx(tready_interval_max=tready_interval_max))#启动axis_slave_if的接收进程await Timer(1, units="us")await RisingEdge(dut.s_axis_aclk) cocotb.start_soon(tb.axis_slave_if.axis_monitor_byte())#启动axis_slave_if的监视进程 cocotb.start_soon(tb.monitor())#启动monitor进程 cocotb.start_soon(tb.scoreboard())#启动scoreboard进程#分别用两种方式向axis_master_if发送数据并存储到data_drv中for i inrange(len(length)): temp = tb.seq_model(length[i])await tb.axis_master_if.axis_tx_byte(temp) tb.data_drv.append(tb.ref_model(temp))for i inrange(len(length)): temp = tb.seq_model(length[i])await tb.axis_master_if.axis_tx_tvalid_interval_byte(temp,1) tb.data_drv.append(tb.ref_model(temp))await Timer(1, units="us")await RisingEdge(dut.s_axis_aclk)if cocotb.SIM_NAME: factory = TestFactory(run_test) factory.add_option("length",[[2000,2001,2002,2003]])#定义测试长度,用于构建不同长度的测试数据 factory.add_option("tready_interval_max",[0,1])#定义tready_interval_max,用于控制接收tready的间隔时间 factory.generate_tests()
3、编写Makefile

以下是对该Makefile的分析和关键内容说明:

Makefile变量定义

  • TOPLEVEL_LANG 指定设计语言为Verilog
  • SIM 指定仿真工具为Synopsys VCS
  • WAVES 控制波形生成,默认启用
  • SEED 使用当前时间作为随机种子
  • XILINX_VIVADO 指向Vivado工具安装路径
  • PYTHONPATH 添加cocotb VIP库路径

编译选项

  • COMP_OPTS 包含SV支持(-sverilog)、64位模式(-full64)和时间精度设置
  • COMPILE_ARGS 指定编译器版本(g+±4.8/gcc-4.8)和调试选项
  • COV_OPT 定义覆盖率收集类型(line/cond/tgl/fsm)

源文件配置

  • VERILOG_SRC 包含:
    • Xilinx IP核生成的axis_data_fifo_0.v
    • 波形dump模块iverilog_dump.v
    • Xilinx全局仿真模块glbl.v
  • VHDL_SRC 当前为空

关键目标说明

  • clean_all 清理所有生成文件
  • comp 创建构建目录并执行Verilog编译
  • iverilog_dump.v 动态生成波形dump模块
  • run 完整流程:清理->设置->编译->仿真
  • cov 生成覆盖率报告并启动DVE查看器

特殊处理

  • synopsys_sim.setup 动态生成仿真配置文件
  • 通过$(shell cocotb-config --makefiles)引入cocotb的Makefile规则
  • 顶层模块设置为axis_data_fifo_0
  • 测试平台模块为axis_data_fifo_0_tb

该Makefile实现了完整的VCS仿真流程,包含编译、仿真、波形收集和覆盖率分析功能,针对Xilinx IP核的验证需求进行了专门配置。

 TOPLEVEL_LANG ?= verilog SIM ?= vcs PWD=$(shell pwd) WAVES ?= 1 SEED = `date "+%H%M%S"` TEST_SEED = $(SEED) RUN_OPTS = COMP_OPTS = -sverilog -full64 +v2k -override_timescale=1ps/1ps #+define+VCS export PYTHONPATH := $(PWD)/../../../../../cocotb_vip:$(PYTHONPATH) XILINX_VIVADO = /home/jacen/tools/xilinx/Vivado/2022.2 SIM_ARGS += $(RUN_OPTS) SIM_CMD_PREFIX += RANDOM_SEED=$(TEST_SEED) COMPILE_ARGS += -cpp g++-4.8 -cc gcc-4.8 -LDFLAGS -Wl,--no-as-needed -l debug.log COMPILE_ARGS += -top iverilog_dump -top glbl -lca # 覆盖率选项 COV_OPT = line #+cond+tgl+fsm SIM_ARGS += -cm $(COV_OPT) COMPILE_ARGS += -cm $(COV_OPT) COV_DIR = sim_build/coverage include $(shell cocotb-config --makefiles)/Makefile.sim TOPLEVEL := axis_data_fifo_0 MODULE := axis_data_fifo_0_tb COMP_INCDIR = VHDL_SRC = VERILOG_SRC += $(PWD)/../../vivado_prj/vivado_prj.gen/sources_1/ip/axis_data_fifo_0/sim/axis_data_fifo_0.v\ $(PWD)/iverilog_dump.v \ $(XILINX_VIVADO)/data/verilog/src/glbl.v #WORDS = 4 #COMPILE_ARGS += -pvalue+WORDS=$(WORDS) clean_all: rm -rf DVEfiles *.vpd *.fst __pycache__ results.xml sim_build \ ucli.key stack.info.* dve_report.* *.vcd log/* iverilog_dump.v *.dump *.log synopsys_sim.setup comp: mkdir sim_build mkdir sim_build/64 # vhdlan $(VHDL_SRC) -l comp_vhd.log vlogan $(COMP_OPTS) $(COMP_INCDIR) $(VERILOG_SRC) -l comp_vlog.log # vhdlan -full64 -f oran_low_phy_uvm_vhd_file_list -l comp_vhd.log mv synopsys_sim.setup sim_build/ synopsys_sim.setup: echo 'DEFAULT : $(PWD)/sim_build' >> $@ echo 'OTHERS=/home/jacen/tools/xilinx/vcs_lib/synopsys_sim.setup' >> $@ iverilog_dump.v: echo 'module iverilog_dump();' >> $@ echo 'initial begin' >> $@ echo ' $$vcdplusfile("wave.vpd");' >> $@ echo ' $$vcdpluson();' >> $@ echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@ echo 'end' >> $@ echo 'endmodule' >> $@ run: clean_all synopsys_sim.setup iverilog_dump.v comp sim @echo "done..." # 添加覆盖率报告生成 cov:run urg -dir sim_build/simv.vdb -report $(COV_DIR) dve -covdir sim_build/simv.vdb& 
4、运行仿真,看波形

进入子系统仿真目录
运行make run

在这里插入图片描述


打开VCS软件,导入波形,选择信号加入波形图
dve&

在这里插入图片描述


在这里插入图片描述


Makefile中需要注意:
1、synopsys_sim.setup 指示Xilinx IP VCS仿真库的位置
2、需要指定编译器版本(g+±4.8/gcc-4.8)

Read more

智能车竞赛实战:如何用地瓜机器人打造智慧医疗解决方案(附完整代码)

智能车竞赛实战:基于地瓜机器人的智慧医疗系统开发指南 在当今技术驱动的医疗创新浪潮中,智能车竞赛为大学生创客提供了绝佳的实践平台。地瓜机器人作为一款开源硬件平台,其灵活的可扩展性和丰富的传感器生态,使其成为开发智慧医疗解决方案的理想选择。本文将深入探讨如何从零开始构建一套完整的智慧医疗系统,涵盖硬件选型、算法设计到实战优化的全流程。 1. 硬件架构设计与环境搭建 构建智慧医疗系统的第一步是搭建可靠的硬件基础。地瓜机器人平台的核心优势在于其模块化设计,允许开发者根据具体需求灵活配置传感器和执行机构。 1.1 核心硬件选型建议 对于医疗应用场景,我们需要特别关注数据的准确性和系统的稳定性。以下是经过实战验证的硬件配置方案: * 主控单元:推荐使用地瓜机器人V3.2开发板,其搭载的STM32H743芯片提供充足的算力资源 * 环境传感器: * 温湿度:SHT31高精度数字传感器(±1.5%RH精度) * 空气质量:SGP30 VOC传感器 * 医疗监测模块: * 红外测温:MLX90614非接触式传感器 * 心率血氧:MAX30102光电传感器

手把手用ROS实现Ego-Planner动态避障:无人机撞树问题终结方案

手把手用ROS实现Ego-Planner动态避障:无人机撞树问题终结方案 你是否曾满怀期待地启动无人机,看着它在仿真环境中流畅起飞,却在下一秒“砰”地一声撞上突然出现的障碍物,仿真画面定格,留下一串令人沮丧的报错信息?在复杂、非结构化的真实飞行场景中,比如在枝叶交错的林间穿行,或在有行人、车辆移动的城区执行任务,传统的全局规划器往往显得力不从心。它们规划的路径可能全局最优,但面对瞬息万变的局部环境,反应速度跟不上变化,导致“撞树”成了家常便饭。今天,我们不谈空洞的理论对比,而是聚焦于一个能真正解决这个痛点的方案——Ego-Planner,并带你一步步在ROS和Gazebo搭建的仿真世界里,亲手实现一个能“眼观六路、随机应变”的无人机大脑。 本文面向的是已经具备一定ROS和无人机仿真基础,正被动态避障问题困扰的开发者、研究者或高级爱好者。我们将彻底抛开宏观的算法优劣论述,直接深入到代码配置、参数调优和实战排错层面。你将看到的不是“Ego-Planner实时性更好”这样的结论,而是“如何设置距离场梯度计算的网格分辨率”、“碰撞反作用力系数调到多少能让无人机既灵活又稳定”的具体操作。我们

VRM4U插件完整指南:在Unreal Engine 5中高效处理VRM模型

VRM4U插件完整指南:在Unreal Engine 5中高效处理VRM模型 【免费下载链接】VRM4URuntime VRM loader for UnrealEngine4 项目地址: https://gitcode.com/gh_mirrors/vr/VRM4U 还在为Unreal Engine 5中VRM模型导入的各种技术问题而烦恼吗?今天我要为你详细介绍一款能够彻底优化VRM工作流程的专业工具——VRM4U插件!这款专为UE5设计的VRM文件导入解决方案,让你能够专注于创意实现,而不是技术细节。 项目核心价值:为什么VRM4U是你的最佳选择 VRM4U插件不仅仅是一个格式转换器,它是一套完整的3D角色处理生态系统。通过智能化的技术实现,它解决了VRM模型在UE5环境中面临的多重挑战。 核心问题解决方案: * 自动化的材质系统转换 * 完整的骨骼结构映射 * 动画数据的无缝衔接 * 跨平台性能优化 快速入门:5分钟完成插件配置 获取插件资源 首先需要下载VRM4U插件,使用以下命令获取完整代码库: git clone https://gitcode

OFA-VE在AR内容生成中的应用:实时验证虚拟物体与现实图像逻辑关系

OFA-VE在AR内容生成中的应用:实时验证虚拟物体与现实图像逻辑关系 1. 引言:当虚拟遇见现实,如何确保它们“合情合理”? 想象一下,你正在开发一款增强现实(AR)应用,用户可以通过手机摄像头,在自家的客厅里“放置”一个虚拟的沙发。听起来很酷,对吧?但问题来了:如果用户家的客厅里已经摆满了家具,这个虚拟沙发应该放在哪里才显得真实、不突兀?是悬浮在半空,还是稳稳地落在地板上?它会不会和现实中的茶几“穿模”? 这就是AR内容生成中一个核心且棘手的挑战:逻辑一致性。虚拟物体不仅要“看起来”在现实场景中,更要“在逻辑上”与现实场景融为一体。传统方法往往依赖复杂的3D场景重建和物理引擎计算,过程繁琐且对硬件要求高。 今天,我们要介绍一个能优雅解决这个问题的“智能裁判”——OFA-VE。它不是一个AR开发工具,而是一个尖端的多模态推理系统。它的核心能力是进行“视觉蕴含”分析,简单来说,就是判断一段文字描述是否符合一张图片所展现的事实。 我们将深入探讨,如何利用OFA-VE的这种能力,为AR内容生成流程注入“逻辑验证”