排序规则:

关于乱序:

Non-posted request:允许通过另一个Non-posted request,当前读请求被堵塞,允许后面的读请求跳到前面执行;也就是说先发的读请求,可能是后返回的。
先发的MRD,返回时,可能在其他后发的MRD的后面;

对于完成包CPLD,不同的传输ID 运行变换顺序执行;

多个CPLD包,含有相同传输 ID,是不允许有顺序变换,
不同的完成包允许乱序,因为有不同的传输 ID;
目的:缓存乱序的tag数据,进行数据重排列,按照先后顺序发送tag对应的CPLD包;
Tag ID 的乱序:
FPGA 向PC端读取数据时,会发送MRD 请求包给PC,PC机接收到MRD包后,根据包的地址读取数据,然后会反馈多个CPLD包给FPGA,MRD的请求长度可以比CPLD包的payload size 更长,也就是说FPGA给PC 发一个MRD请求包,PC可能会返回多个CPLD包给FPGA,而在交互过程中,PC机会提取FPGA 发送的MRD包的tag字段,并且PC在反馈给FPGA当前的MRD包对应的CPLD包时,所有的CPLD包都会使用这个Tag 字段,FPGA 侧也可以通过这个tag 来判断这几个tag 相同的CPLD包属于同一次传输;
但是FPGA 发送MRD 请求包时,可能存在连续发几个请求的情况,比如连续发了两个MRD请求,mrd_tag_m,mrd_tag_n,但是PC机的回复并非是先把所有的m 序列发送完毕后再发送所有的n序列,而是会对mrd_tag_m,mrd_tag_n进行交错穿插回复,这就造成了tag 乱序的问题。
根据排序规则,返回的CPLD 包m 和 n 存在乱序,但是ID 0,1,2,之间不会有乱序产生;

所以需要对m 和 n 返回的CPLD包 ID 进行重新排序,给到用户模块;
因为MRD请求返回的CPLD 包的乱序只会体现在m和n,不会体现在m_0,m_1,m_2,所以只需要将m匹配的存入FIFO_m,n匹配的存入FIFO_n,读出的时候按照MRD分配m,n的顺序读出即可实现乱序到顺序。
比如 先MRD 的m 后n ,则返回CPLD 的时候先输出m 0~2,再输出n 0~2;
还需要将接收到的CPLD 包属于m 的拼接到一起输出;
每发一个MRD包,就需要请求一次tag id,请求tag id就需要提供length ,last等相关信息,以便在接收到返回的CPLD数据包时,对比tag id,包长度length是否接收完,以及最后一包的数据指示;
MRD模块接收到tag id后,就能够向外输出 request;
记录 tag id 是否有使用,是否空闲:tag_id(0~31),每当有一个请求,会把请求信息写入tagRam(或者寄存器组),假如在初始状态,写指针指向0,表示tag 0 是空闲,当来了一个请求时,会将该tag请求的last,length,H2C/SGH2C,CH_NUM进行存储,即tag 0 的信息,此时指针就会指向1,表明0被占用,1为空闲状态;
请求可以是h2c或者sgh2c或者h2c和sgh2c同时到来,同时到来则利用仲裁机制;
写满:写指针追上读指针;
指针管理,判断tag0~31 何时写满,当tag 写指针与读指针相等,写追上读,即指针指向同一位置;当FIFO写满或者读空时,判断tag 的授权与释放:
根据解出来的CPLD包,CPLD包里面有tag 信息,根据tag id 存放在对应的FIFO 中;
判断读指针指向的比如是tag0,那么tag0 信息中的length与FIFO0 中取出的数据长度相等时,判断出此tag0数据已经读取完了,读指针加1,tag0属于空闲了,此时写指针是指向0,而读指针指向1,再有请求时,就可以授权tag0;
根据valid 信号进行tag 申请,申请成功后返回CPLD的数据,CPLD数据写入内部FIFO,写完后,FIFO不空,开始产生读起始信号,之后把内部FIFO读出来的数据放到外部FIFO,当外部FIFO不空的时候,将数据读出,最后通过sel 选择,将数据输出给block H2C 还是 SGH2C;
整体设计:
依据H2C的 valid 信号进行tag 申请,当两种H2C同时进行申请时,则需要设计仲裁机制,给valid分配tag;首先申请tag 是MRD包,所以分配tag 时,需要组建tag 相关信息,目的是在接收到CPLD包后,便于分析解包;组建tag 相关信息包括MRD包的包长度和最后一拍数据的指示信号(length,last),以及两种H2C 的编号用于识别对应的H2C tag申请(h2c_sel),通道号(channel_num);之后,根据分配的tag id将选择与tag id对应的FIFO模块,一共会例化32个FIFO,因为tag id(0~31),每个FIFO模块有TAG_NUM,0~31,选择tag id 与TAG_NUM对应的模块,将tag 信息送入该FIFO模块,同时送入该FIFO模块的还有返回来的CPLD包,会判断CPLD包携带的tag id是否与当前FIFO模块的TAG_NUM参数匹配,匹配成功才会将CPLD包的数据写入FIFO缓存,根据length字段,last字段信息,将CPLD包完整的读出,读出后会给到用户逻辑;
设计包含两个FIFO,第一个FIFO用来接收返回的PCLD包,并根据PCLD包携带的tag id 放到与TAG_NUM参数对应的FIFO里等待读出,实现乱序写入顺序读出;另一个FIFO则是将CPLD的数据缓存并读出给用户侧;
仲裁机制,只针对两种valid 同时到来,进行'与运算',得到仲裁分配的结果;根据sel=10或者01分别代表sgh2c或者h2c两种tag 申请的情况;

这里标识了整个tag 申请的过程,当有valid到来则开始tag申请,完成一次握手,视为结束;

同步时刻,要对tag 信号进行封装,主要是MRD的length字段和last字段;

完成一次tag 的申请,对应写指针累加1,因为tag 只有32位,所以要计数tag已经申请了多少,如果申请完后,没有空闲的tag id后续就不能再进行申请;

关于ready信号的信号,则是在valid信号的到来后,根据sel信号,拉高两种申请所对应的ready信号,完成握手处理;


根据申请的 tag_id 和自定义的TAG_NUM会进入到单个tag id 的处理模块中,
比如tag id 申请的是 1,2,3,而返回的CPLD tag id顺序是 3,2,1,那么CPLD 数据会写入到TAG_NUM等于3,2,1的模块内,此时到来的顺序不是顺序的,是乱序的,因为例化了0~31个该模块,所以返回的CPLD包不管tag id顺序如何,只会放到与TAG_NUM对应的single_tag_ctr模块里面去;
该模块会实现对CPLD 数据的接收与读出;
写FIFO 使能是依据CPLD 携带的tag id与TAG_NUM相等且数据有效进行写动作;

因为CPLD 包是4DW,128位; 计算 4 x 4 x 8 = 128位;
对于Keep信号位宽为16位,如果让其以DW为单位进行计算一共有多少个DW,那么只需要每4位取第一位即可;1DW=4Byte;

根据得到的DW数量,与CPLD的包长度length比较,如果长度相等,则认为一个带有与TAG_NUM相同的tag id 的CPLD包接收完成;

在写FIFO 侧存在写指针wptr,用于计数tag id 被占用的情况,在读FIFO侧,存在读指针rptr,用于计数tag id 被释放的情况;

读单个带有tag id 的CPLD包过程:

伴随读过程产生,会产生读数据有效的信号,该信号用于读FIFO的使能信号;
因xilinx FIFO almost_empty 与 empty 之间只间隔一个数据
当 r_tfifo_aempty==1 时 r_tfifo_data_vld 会由1跳变为0,此时数据无效;
当 满足 r_tfifo_aempty==1 && r_tfifo_empty==0 && r_tfifo_data_vld==0,
r_tfifo_data_vld 会跳变为1,此时数据有效,
直到 r_tfifo_empty 有效,xilinx FIFO的话,下一拍empty就会拉高,
r_tfifo_data_vld 就会一直为0;


计算读出的CPLD包的DW数据量,目的用于后续keep 信号的判断,以及最后一包传输完成后拉高读FIFO完成的标志;


单个tag 数据读完;

此时完成的是CPLD 数据的乱序写入顺序读出;
将单个CPLD数据依据tag id 顺序的输出到h2c_tag_order_sort模块;
该模块使用FIFO存储CPLD数据,写使能就为single_tag_ctrl的FIFO 读使能信号;
而对于该模块的FIFO 读使能产生:

同时会产生数据有效标识,给到用户侧;

将DW转换为字节:

带有tag id 的CPLD数据包读取结束:




