以下内容仅代表个人观点,欢迎指正。
继上篇官方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数据异步传输的方式
上图中
ARx | Memory(DDR3) to AXI-Buffer,AXI Read Channel(TMS/TDI) | ||||||||||
CRx | AXI-Buffer copy to Vector-Buffer(TMS/TDI) | ||||||||||
JSx | JTAG Start | ||||||||||
JFx | JTAG Finished | ||||||||||
CWx | Vector-Buffer copy to AXI-Buffer(TDO) | ||||||||||
AWx | AXI-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长度足够大,使得。
受限于篇幅,此处省略了具体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核目录下
找到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内核模块和裸机)方面开始展开。。。
未完待续...
(不定时更新,随时会鸽,源码已上传至码云)