基于ZYNQ的Xilinx Virtual Cable(XVC) Server 开发心得(三)

以下内容仅代表个人观点,欢迎指正。

继上篇官方IP核分析与测试之后,IP核优化基本有两个方向:

一是减小ZYNQ数据搬运时间,即DDR3至外设、或外设到DDR3的时间;

二是减小ZYNQ数据搬运时间与JTAG信号产生时间占比,即增大一次性可传输的JTAG信号比特数。

接下来对我在开发过程中曾使用过的优化方案逐一介绍并加以分析。

  • 使用AXI-DMA IP核。更改TMS/TDI/TDO数据搬运路线从原本的AXI-Lite总线——外设,为外设——AXI-Stream(Master/Slave)——AXI4 Master(AXI-DMA)。相比于官方AXI转JTAG IP核,该方案JTAG数据搬运由DMA IP核(硬件)完成,解放CPU的同时,提高了JTAG数据在内存和外设间的传输速度。但AXI-DMA IP核外设端接口为AXI-Stream,三路AXI-Stream总线的引入增加了系统复杂度;AXI-DMA IP核传输需要配置,增加了ZYNQ额外的时间消耗;
  • 将官方IP核接口修改为AXI4,并增大硬件JTAG数据缓冲区。此时JTAG数据可由ZYNQ片上DMA搬运,相比于上一个方案降低了系统复杂度。但ZYNQ的片上DMA不允许非对齐(数据总线32位宽对齐)的数据传输,需要提前分配总线对齐的内存缓冲区,并需要维护DMA Cache一致性(上方案也需要)。

上述方案均是以减小ZYNQ数据搬运时间为目标的,即优化方向一。但转JTAG信号的过程仍为:TMS/TDI数据搬运,JTAG传输,TDO数据搬运,(循环)。假使此处引入异步传输的机制,在JTAG传输过程中搬运TDO数据并提前搬运TMS/TDI数据,即可大大减小ZYNQ数据搬运时间与JTAG信号产生时间占比。接下来以单缓冲为例,描述JTAG数据异步传输的方式

上图中

ARxMemory(DDR3) to AXI-Buffer,AXI Read Channel(TMS/TDI)
CRxAXI-Buffer copy to Vector-Buffer(TMS/TDI)
JSxJTAG Start
JFxJTAG Finished
CWxVector-Buffer copy to AXI-Buffer(TDO)
AWxAXI-Buffer to Memory(DDR3), AXI Write Channel(TDO)

AXI-Buffer的读/写通道具有阻塞性,其中AR1为预加载,AR2必须等待AR1传输完毕;而Vector-Buffer拷贝无需等待,并且CRx与JSx,JFx与CWx可同时进行。至此,JTAG数据长传输中,每一次JTAG短传输(JSx→JFx)之间省去了TMS/TDI读取和TDO存储的时间,JTAG传输效率可逼近100%。

但上述方法存在一些问题,假使ARx所需时间超过JSx→JFx,如出现总线竞争,JTAG传输便会出错。因此,需保证JTAG短传输的JTAG Vector长度足够大,使得t_{ARx}<<(t_{JFx}-t_{JSx})

受限于篇幅,此处省略了具体IP核设计过程和所用到的方法。直接上传了本人的AXI转JTAG IP核最终优化版本

PS:该IP核需,对主AXI4和从AXI-Lite总线时序有清晰完整的认知。

PPS:IP核工程恕不能上传分享,请见谅。

构建的IP核用户界面如下图所示,配置项主要为主AXI总线数据位宽,JTAG最大传输长度VECTOR_LEN,JTAG接口类型和JTAG I/O三态控制极性。其中s_axi_lite接口为从AXI-Lite接口,负责IP核内部寄存器读写;m_axi接口为主AXI4接口,负责访问主AXI内存空间,实现DMA传输;m_jtag或m_jtag_io接口为主JTAG接口,负责JTAG通信。

上段说明信息可通过IP图形界面的Documentation->Datasheet打开,或者可以在IP核目录下

drivers/axi_to_jtag_lm/doc

找到PDF文件,所有IP核使用相关均可在该手册中查询到。

接下来继续对IP核性能进行分析,建立testbench文件(可于IP核目录hdl/sim下找到),源码如下:

//TMS/TDI/TDO各分配2KB内存地址空间
`define MEM_SIZE (1024*4)

`define TEST_VECTOR_LEN 8200

`define TMS_MEM_PTR_POS 0
`define TDI_MEM_PTR_POS 2048
`define TDO_MEM_PTR_POS 4096

module axi_to_jtag_lm_testbench
	   #
	   (
		   parameter integer C_M_AXI_LITE_ADDR_WIDTH = 32,
		   parameter integer C_M_AXI_LITE_DATA_WIDTH = 32,

		   parameter integer C_LOCAL_MEM_BUFFER_SIZE = `MEM_SIZE,
		   parameter integer C_LOCAL_AXI_MAX_BURST_LENGTH = 256,
		   parameter integer C_LOCAL_AXI_ADDR_WIDTH = 32,
		   parameter integer C_LOCAL_AXI_DATA_WIDTH = 64,

		   parameter integer C_M_JTAG_CLK_FREQUENCY = 100e6,

		   //9 registers, nid 6 bit address
		   parameter integer C_S_AXI_ADDR_WIDTH = 5,
		   parameter integer C_S_AXI_DATA_WIDTH = 32,

		   parameter integer C_VECTOR_LEN = 128,
		   parameter integer C_M_AXI_MAX_BURST_LENGTH = 256,
		   parameter integer C_M_AXI_ADDR_WIDTH = 32,
		   parameter integer C_M_AXI_DATA_WIDTH = 64
	   )
	   ();

integer i;

initial
begin
	AXI_LITE_WENABLE <= 0;
	AXI_LITE_RENABLE <= 0;
	AXI_LITE_REG_WADDR <= 0;
	AXI_LITE_REG_RADDR <= 0;
	AXI_LITE_REG_WDW <= 0;
	AXI_LITE_REG_RDW <= 0;
	AXI_LITE_REG_WVAL <= 0;

	AXI_WENABLE <= 0;
	AXI_RENABLE <= 0;
	AXI_MEM_WADDR_BASE <= 0;
	AXI_MEM_RADDR_BASE <= 0;
	AXI_MEM_WLEN <= 0;
	AXI_MEM_RLEN <= 0;
	AXI_MEM_WVAL <= 0;

	#300;

	//TMS内存空间填充0x01, 0x02, 0x03, .....
	for (i = 0;i < (`TEST_VECTOR_LEN + 7) / 8;i = i + 1)
	begin
		AXI_MEM_WVAL[(8 * i) + : 8] <= i + 'h01;
	end

	AXI_MEM_WADDR_BASE = `TMS_MEM_PTR_POS;
	AXI_MEM_WLEN = (`TEST_VECTOR_LEN + 7) / 8;
	AXI_WENABLE <= 1;
	@(~AXI_WBUSY);
	@(AXI_WBUSY);
	AXI_WENABLE = 0;

	#20;

	//TDI内存空间填充0x11, 0x12, 0x13, .....
	for (i = 0;i < (`TEST_VECTOR_LEN + 7) / 8;i = i + 1)
	begin
		AXI_MEM_WVAL[(8 * i) + : 8] <= i + 'h11;
	end

	AXI_MEM_WADDR_BASE = `TDI_MEM_PTR_POS;
	AXI_MEM_WLEN = (`TEST_VECTOR_LEN + 7) / 8;
	AXI_WENABLE <= 1;
	@(~AXI_WBUSY);
	@(AXI_WBUSY);
	AXI_WENABLE = 0;

	#20;
	//写入频率控制字,设定TCK频率为10MHz, FREQ_WORD=10e6/100e6*2^32
	AXI_LITE_REG_WADDR <= 'h08;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= 'd429496730;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;

	#20;
	//写入JTAG Vector长度
	AXI_LITE_REG_WADDR <= 'h04;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= `TEST_VECTOR_LEN;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;

	#20;
	//写入TMS缓冲区内存地址
	AXI_LITE_REG_WADDR <= 'h0C;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= `TMS_MEM_PTR_POS;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;

	#20;
	//写入TDI缓冲区内存地址
	AXI_LITE_REG_WADDR <= 'h10;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= `TDI_MEM_PTR_POS;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;
	
	#20;
	//写入TDO缓冲区内存地址
	AXI_LITE_REG_WADDR <= 'h14;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= `TDO_MEM_PTR_POS;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;

	#20;
	//使能中断(bit1)
	//启动JTAG传输(bit0)
	AXI_LITE_REG_WADDR <= 'h0;
	AXI_LITE_REG_WDW <= 2;
	AXI_LITE_REG_WVAL <= 'h3;
	AXI_LITE_WENABLE <= 1;
	@(~AXI_LITE_WBUSY);
	@(AXI_LITE_WBUSY);
	AXI_LITE_WENABLE <= 0;
end

endmodule

运行仿真,仿真整体结果如下:

传输JTAG比特数为8200个,TCK频率为10MHz,总共用时824.03us,TCK效率为99.51%。首先观察AXI-Lite总线写入部分时序:

写入的寄存器为:地址0x08,FREQ_WORD,TCK 频率控制字寄存器;地址0x04,LENGTH,JTAG 传输位长寄存器;地址0x0C,0x10,0x14,分别为TMS、TDI、TDO数据缓冲区内存地址寄存器。接下来观察AR1,AR2阶段波形:

AR1阶段,从TMS基地址0x00取出了64bit*2=128bit=16个字节数据,并从TDI基地址0x800取出了16个字节数据;AR2阶段读指针发生偏移16个字节,分别从0x10和0x810中提前取出了TMS/TDI下次传输的数据;在AR2阶段开始稍后几个总线时钟周期(m_axi_aclk),JS1启动。JS1启动后这一次JTAG短传输波形为:

在JF1阶段结束稍后AW1,AR3次序启动,并同时启动JS2;AW1阶段向TDO地址(0x100)写入了16个字节数据;AR3阶段预取了下一次(JS3)的TMS/TDI数据。接下来直接观察最后一次JTAG短传输波形:

可以看到于JS[N-1]到JF[N-1]阶段进行了最后一次预取(TMS/TDI),在JS[N]-JF[N]并未发生预取;在最后一次JTAG短传输内TDO数据全部写入完毕,包括AW[N-1]与AW[N]。

至此JTAG异步传输的实现、仿真分析全部完毕。

理论上JTAG比特传输效率已逼近100%,同时释放了ZYNQ的CPU性能,在JTAG信号生成的同时,ZYNQ网络数据收发不受到影响。可以预想到,XVC服务器性能会相较之前产生巨大提升,而这又为多XVC服务器,即多目标FPGA的实现打下了基础。

下篇将从XVC服务器结构设计和JTAG设备驱动设计(含LINUX内核模块和裸机)方面开始展开。。。

未完待续...

(不定时更新,随时会鸽,源码已上传至码云)

  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值