TXT 文件格式 - 2023.2 简体中文

AI 引擎工具和流程用户指南 (UG1076)

Document ID
UG1076
Release Date
2023-12-04
Version
2023.2 简体中文

输入文件

输入/输出串流的默认位宽为 32 位。位宽用于指定仿真输入文件上每行的样本数。输入文件的每一行上样本的解读取决于期望的数据类型和 PLIO 数据宽度。下表根据数据类型及其对应的 PLIO 接口规范,显示了输入数据文件中的样本解读方式。

表 1. 数据类型和 PLIO 位宽
数据类型 PLIO 32 位 PLIO 64 位 PLIO 128 位
adf::input_plio in = adf::input_plio::create("DataIn1", adf::plio_32_bits, "input.txt"); adf::input_plio in = adf::input_plio::create("DataIn1", adf::plio_64_bits, "input.txt"); adf::input_plio in = adf::input_plio::create("DataIn1", adf::plio_128_bits, "input.txt");
int8 每行 4 个值。例如:

6 8 3 2

每行 8 个值。例如:

6 8 3 2 6 8 3 2

每行 16 个值。例如:

6 8 3 2 6 8 3 2 6 8 3 2 6 8 3 2

int16 每行 2 个值。例如:

24 18

每行 4 个值。例如:

24 18 24 18

每行 8 个值。例如:

24 18 24 18 24 18 24 18

int32 每行 1 个值

2386

每行 2 个值。例如:

2386 2386

每行 4 个值。例如:

2386 2386 2386 2386

int64 不适用 45678 每行 2 个值。例如:

45678 95578

cint16 每行 1 个 cint 值:实数,虚数。例如:

1980 485

每行 2 个 cint 值。例如:

1980 45 180 85

每行 4 个 cint 值。例如:

1980 485 180 85 980 48 190 45

cint32 不适用 每行 1 个 cint 值:实数,虚数。例如:

1980 485

每行 2 个 cint 值。例如:

1980 45 180 85

float 每行 1 个浮点值。例如:

893.5689

每行 2 个浮点值。例如:

893.5689 3459.3452

每行 4 个浮点值。例如:

893.5689 39.32 459.352 349.345

cfloat 不适用 每行 1 个浮点 cfloat 值:实数,虚数。例如:

893.5689 24156.456

每行 2 个浮点 cfloat 值:实数,虚数。例如:

893.5689 24156.456 93.689 256.46

PLIO 和包串流接口要求

当 TXT 文件用于提供表示 PLIO 端口和包串流接口的数据时,应遵循以下 TXT 文件要求。

  1. tkeep 始终有效。不支持将 tkeep 信号设置为 False。
  2. 根据接口宽度,一行中的多个数据样本可以合并发送。第一个数据样本在接口的最低位中发送。例如,如果数据类型为 int16 且 AI 引擎到 PL 接口位宽为 64 位,那么 0 1 2 3 行作为 0x0003000200010000 发送到 AI 引擎。
    0 1 2 3
  3. TXT 文件中的 tlast 表示以下行的 tlast 等于 1。如果 tlast 为 1,那么发送到 AI 引擎的数据样本数可等于或小于 AI 引擎到 PL 接口的宽度。例如,如果数据类型为 int16 且 AI 引擎到 PL 接口位宽为 64 位,那么最后一行的 4 5 作为 0x00050004tlast 一并发送到 AI 引擎:
    0 1 2 3
    tlast
    4 5
  4. 对于包串流接口,tlast 等于 1 表示包结束。包的报头应以无符号十进制格式来指定。例如,如果 AI 引擎到 PL 接口位宽为 64 位,那么以下行会发送包报头 0x8fff0000(无符号十进制下为 2415853568):
    tlast
    2415853568
    如果包串流接口的数据类型为 64 位,且 AI 引擎到 PL 接口位宽为 64 位,那么以下行会向 AI 引擎发送包报头 0x8fff0000、包数据 0x0,然后发送包数据 0xfffeffff
    2415853568 0
    tlast
    -1 -2

输出文件

在每个输出 PLIO 端口上,仿真器可使用与输入 PLIO 数据文件相同类型的声明来自动创建包含串流内容的文件。

adf::output_plio out1 = adf::output_plio::create("DataOut1",adf::plio_32_bits,"output1.txt");
adf::output_plio out2 = adf::output_plio::create("DataOut2",adf::plio_64_bits,"output2.txt");
adf::output_plio out3 = adf::output_plio::create("DataOut3",adf::plio_128_bits,"output3.txt");
对于输出创建的文件,适用的格式与输入相同。根据数据类型和 PLIO 带宽,将在每一行上显示一定量的数据。仿真器会为每个输出行添加时间戳,以便您估算仿真期间的数据吞吐量。可用时间戳单位如下:
  • 皮秒 (ps)
  • 纳秒 (ns)
  • 微秒 (us)
  • 毫秒 (ms)
  • 秒 (s)

如果串流的来源是在每一帧末尾生成的 TLAST 标记,那么此 TLAST 也会写入输出文件。以下提供了此类输出文件示例。

...
T 15984 ns
4552 4555 
T 15988 ns
4558 4561 
T 15992 ns
4564 4567 
T 15996 ns
4570 4573 
T 16 us
4576 4579 
T 16004 ns
4582 4585 
T 16008 ns
4588 4591 
T 16012 ns
4594 4597 
T 16016 ns
4600 4603 
T 16020 ns
4606 4609 
T 16024 ns
TLAST
4612 4615 
T 17940 ns
4618 4621 
T 17944 ns
4624 4627 
T 17948 ns
4630 4633 
T 17952 ns
...
综上,PLIO 端口的每项输出都具有如下格式:
  • 时间戳
  • TLAST 和 TKEEP
  • 样本 DATA 值

您可基于输出来为此 PLIO 端口估算设计吞吐量。时间戳仅与有效输出相关。当 PLIO 端口处于静默状态时,输出文件上没有任何指示信息。

吞吐量计算方式为输出样本数除以时间戳差值:

吞吐量 = 样本数 / (最后一个时间戳 - 第一个时间戳)

如果不考虑最后一个输出样本之后发生的所有时钟周期,那么此简单公式将过高估算吞吐量。

对于基于帧的输出,可能会过高估算此结果。在此情况下,输出格式如下:
  • 帧 0 输出(tstart_0 到 tend_0)
  • 帧间静默
  • 帧 1 输出(tstart_1 到 tend_1)
  • 帧间静默
  • ...
  • 帧 N-1 输出(tstart_N-1 到 tend_N-1)
  • 帧间静默
  • 帧 N 输出(tstart_N 到 tend_N)

在此情况下,您必须考虑每个帧输出的帧间耗用时间。如果您仅使用前 N 个帧(从 0 到 N-1),那么这是可行的。在此情况下,请将吞吐量公式的时间戳替换为:

  • FirstTimestamp = tstart_0
  • LastTimestamp = tstart_N(最后一帧的第一个输出时间戳)

以下提供了计算 PLIO 吞吐量的 Python 脚本示例,其中对仿真输出文件进行了分析并对吞吐量进行了计算。


import numpy as np
from math import *
import sys
import argparse

def GetTime_ns(Stamp):
  Time_ns = float(Stamp[1])
  if(Stamp[2] == 'ps'):
    Time_ns = Time_ns/1000.0
  elif(Stamp[2] == 'us'):
    Time_ns = Time_ns*1000.0
  elif(Stamp[2] == 'ms'):
    Time_ns = Time_ns*1000000.0
  elif(Stamp[2] == 's'):
    Time_ns = Time_ns*1000000000.0
  return(Time_ns)


def ReadFile(filename):
  # Detect the number of data per PLIO output
  fdr = open(filename,'r')
  ts = fdr.readline()
  d = fdr.readline()
  dw = d.split()
  fdr.close()


  coltime = 0
  coldata = 1
  numdata = len(dw)
  coltlast = numdata + 1


  # Initializes the output array
  # Format: timestamp (in ns) val1 val2 ... valN TLAST (0 or 1)
  a = np.zeros((0,numdata+2))
  fdr = open(filename,'r')
  line = ' '
  lnum = 0;

  while line !="" :
    line = fdr.readline()
    if line=='':
      continue
    res = line.split()

    if(res[0] != 'T'): # It should be a timestamp
      continue

    l = np.zeros((1,numdata+2))
    # Extract the time stamp
    l[0][0] = GetTime_ns(res)


    line = fdr.readline()
    res = line.split()
    # extract the TLAST
    if(res[0]=='TLAST'):
      tlast = 1
      line = fdr.readline()
      res = line.split()
    else:
      tlast = 0

    l[0,coltlast] = tlast
    # Extract all values
    for i in range(numdata):
      l[0,i+1] = float(res[i])

      # Appends to the whole array
      a = np.append( a , l,axis=0)

  fdr.close()
  return(a)

def Throughput(Filename,IsComplex):
  V = ReadFile(Filename)
  print("\n==============================")
  print(Filename)
  print("\n")

  NRows = V.shape[0]
  NCols = V.shape[1]
  NFullFrames = int(np.sum(V[:,NCols-1]))
  print("Number of Full Frames: " + str(NFullFrames))


  # Basic Throughput computation
  if IsComplex:
    Ratio = 0.5
  else:
    Ratio = 1
  RawThroughputMsps = float(NRows*(NCols-2))/(V[NRows-1,0]-V[0,0])*Ratio*1000.0
  print("Raw Throughput: %.2f" % RawThroughputMsps)

  # If the output is frame based, compute a more precise throughput
  tlast = np.where(V[:,NCols-1] == 1.0)
  if(len(tlast[0])<=1):
    TotalThroughput = RawThroughput
  else:
    tlast = tlast[0]
    EndRow = tlast[len(tlast)-2]+1
    # EndRow is the number of Rows I take into account for the number of datasource
    # The timestamp I am interested in is the timestamp of the next transaction
    TotalThroughputMsps = float(EndRow*(NCols-2))/(V[EndRow,0]-V[0,0])*Ratio*1000.0
    print(" Throughput: %.2f" % TotalThroughputMsps)

  print("\n")

# Entry point of this file
if __name__ == "__main__":
  parser = argparse.ArgumentParser(prog=sys.argv[0], description='Compute the throughput corresponding to some output of AIE Simulations')
  parser.add_argument('--iscomplex', action='store_true', help='Indicates Complex data in the file')
  parser.add_argument('filename',nargs='+')
  Args = sys.argv
  Args.pop(0)
  args = parser.parse_args(Args)

for f in args.filename:
  Throughput(f,args.iscomplex)