2026/5/21 2:41:38
网站建设
项目流程
南沙网站建设价格,有没有哪个网站免费做简历的,制作好的网站,基于无网站网络营销的问题问得好#xff01;Cpl#xff08;Completion#xff09;TLP的寻址逻辑是PCIe协议中非常核心且巧妙的设计。让我详细解释。1. Cpl TLP的基本概念
1.1 什么是Cpl TLP#xff1f;
Cpl#xff08;Completion#xff09; 是PCIe设备对非posted请求的响应#xff1a;
非posted…问得好CplCompletionTLP的寻址逻辑是PCIe协议中非常核心且巧妙的设计。让我详细解释。1. Cpl TLP的基本概念1.1 什么是Cpl TLPCplCompletion是PCIe设备对非posted请求的响应非posted请求需要对方回复的请求 ┌─────────┐ │ MRd │ ← 内存读必须回复数据 │ IORd │ ← I/O读必须回复数据 │ CfgRd │ ← 配置读必须回复数据 │ Atomic │ ← 原子操作必须回复结果 └─────────┘ posted请求不需要回复的请求 ┌─────────┐ │ MWr │ ← 内存写不回复 │ IOWr │ ← I/O写不回复 │ CfgWr │ ← 配置写不回复 │ Msg │ ← 消息不回复 └─────────┘1.2 Cpl TLP的类型// Completion类型Cpl-不带数据的完成用于写完成 CplD-带数据的完成用于读完成 CplLk-带锁定的完成原子操作 CplDLk-带数据和锁定的完成2. Cpl TLP的寻址逻辑2.1 关键问题如何找到原请求者当EP收到一个读请求MRd后需要回复CplD。但PCIe是包交换网络EP如何知道把回复发给谁答案TLP头中的Requester ID和Tag2.2 TLP头格式对比// 请求TLP头例如MRd typedef struct { bit [ 7:0] fmt_type; // Fmt001b, Type00000b (MRd) bit [ 9:0] length; // 数据长度 bit [15:0] requester_id;// 请求者ID (Bus:Device:Function) bit [ 7:0] tag; // 事务标签0-255 bit [ 3:0] last_be; // 最后字节使能 bit [ 3:0] first_be; // 首字节使能 bit [63:0] address; // 目标地址 } mrd_header_t; // 完成TLP头CplD typedef struct { bit [ 7:0] fmt_type; // Fmt010b, Type01010b (CplD) bit [ 9:0] length; // 数据长度 bit [15:0] completer_id;// 完成者ID bit [ 2:0] status; // 完成状态 bit bcm; // 字节计数修改 bit [11:0] byte_count; // 剩余字节数 bit [15:0] requester_id;// 原请求者ID从请求TLP复制 bit [ 7:0] tag; // 原请求的Tag从请求TLP复制 bit [ 6:0] lower_addr; // 低地址地址[6:2] } cpld_header_t;3. Cpl寻址的核心机制3.1 三步寻址逻辑RC (Requester)SwitchEP (Completer)1. RC发起读请求MRd TLPRequesterID00:00.0, Tag0x12Address0x1000转发MRd TLP2. EP准备数据并回复读取地址0x1000的数据CplD TLPCompleterID01:00.0RequesterID00:00.0, Tag0x123. Switch根据RequesterID路由查找路由表: 00:00.0 → Port 0转发CplD TLP到RCRC (Requester)SwitchEP (Completer)3.2 详细寻址过程// 场景RC读EP的内存// RC: Bus 0, Device 0, Function 0 (00:00.0)// EP: Bus 1, Device 0, Function 0 (01:00.0)// 步骤1RC发送MRdmrd_tlp{.fmt_type3b001_00000,// MRd, 3DW头无数据.requester_id16h0000,// 00:00.0.tag8h12,// 事务标签.address64h0000_1000,// 目标地址.length10h1// 读1个DW};// 步骤2EP收到MRd准备回复// EP记住RequesterID00:00.0, Tag0x12// 步骤3EP发送CplDcpld_tlp{.fmt_type3b010_01010,// CplD, 3DW头有数据.completer_id16h0100,// 01:00.0 (EP自己).requester_id16h0000,// 复制自MRd的RequesterID.tag8h12,// 复制自MRd的Tag.lower_addr7h04,// 地址[6:2] 0x1000[6:2] 0x04.data{32hDEAD_BEEF}// 读取的数据};// 步骤4Switch根据RequesterID路由// 查找路由表Bus 0 → Port 0 (连接RC)// 转发CplD到Port 0// 步骤5RC收到CplD// 匹配Tag0x12的未完成请求// 将数据0xDEAD_BEEF返回给CPU4. 关键字段详解4.1 Requester ID (16位)Requester ID格式 Bits 15:8 Bus Number (0-255) Bits 7:3 Device Number (0-31) Bits 2:0 Function Number (0-7) 示例 0x0000 Bus 0, Device 0, Function 0 (RC通常) 0x0100 Bus 1, Device 0, Function 0 (EP) 0x0218 Bus 2, Device 4, Function 0 (Device 4, Func 0)作用在Cpl TLP中Requester ID告诉网络把回复发给谁。4.2 Tag (8位)// Tag是Requester分配的本地事务ID// 用于区分同一Requester的多个未完成请求// RC可能有多个未完成的读请求tag_table[0]{address0x1000,length4,requesterCPU0}tag_table[1]{address0x2000,length8,requesterCPU1}tag_table[2]{address0x3000,length1,requesterCPU0}// 当CplD返回时用Tag查找对应的请求uint8_ttagcpld_header.tag;original_requesttag_table[tag];Tag池管理// RC维护Tag池structtag_entry{uint64_taddress;uint16_tlength;uint8_trequester_cpu;uint8_tstatus;// PENDING, COMPLETED};tag_entry tag_pool[256];// 256个Tag// 分配Taguint8_tallocate_tag(uint64_taddr,uint16_tlen){for(inti0;i256;i){if(tag_pool[i].statusFREE){tag_pool[i].addressaddr;tag_pool[i].lengthlen;tag_pool[i].statusPENDING;returni;// 返回Tag值}}return0xFF;// Tag池满}// 收到CplD时voidhandle_cpld(uint8_ttag,uint32_t*data){original_reqtag_pool[tag];// 将data返回给original_req.requester_cputag_pool[tag].statusFREE;// 释放Tag}4.3 Lower Address (7位)Lower Address 原始请求地址的[6:2] 为什么是[6:2] - 地址对齐PCIe访问总是DW对齐的 - [1:0]总是00DW对齐 - [6:2]可以表示32个DW128字节内的偏移 示例 原始地址 0x1234_5678 [6:2] 0x78[6:2] 0x1E (二进制 11110) 作用 1. 帮助Requester将数据放到正确的位置 2. 对于跨DW边界的访问配合Byte Count使用4.4 Byte Count (12位)// Byte Count表示还有多少字节需要传输// 对于拆分的事务多个CplD这个值递减// 示例RC读16字节4个DW// 地址0x1000, length4// 第一个CplDbyte_count16-已传输的字节数16-016// 如果EP一次只能发8字节// 第一个CplD: byte_count16, length2 (8字节)// 第二个CplD: byte_count8, length2 (8字节)// Requester用byte_count知道是否收完所有数据5. 路由机制5.1 基于ID的路由Cpl TLP使用ID路由不是地址路由// Switch的路由逻辑 module switch_routing( input tlp_header_t header, output port_num_t out_port ); // 判断TLP类型 case (header.fmt_type) // 地址路由的TLPMRd, MWr等 3b000_00000, 3b001_00000, // MRd 3b010_00000, 3b011_00000: // MWr // 根据地址路由 out_port address_routing(header.address); // ID路由的TLPCpl, Cfg等 3b000_01010, 3b010_01010: // Cpl, CplD // 根据Requester ID路由 out_port id_routing(header.requester_id); // 配置TLP也使用ID路由 3b000_00100, 3b010_00100: // CfgRd, CfgWr out_port id_routing({header.bus_num, 8h0}); endcase endmodule // ID路由表 module id_routing( input [15:0] requester_id, output [2:0] out_port ); // 简单实现根据Bus Number路由 case (requester_id[15:8]) // Bus Number 8h00: out_port 3b001; // Port 1 (连接RC) 8h01: out_port 3b010; // Port 2 (连接Bus 1) 8h02: out_port 3b100; // Port 3 (连接Bus 2) default: out_port 3b000; // 丢弃 endcase endmodule5.2 Switch的路由表示例Switch连接 Port 0: 上游 → RC (Bus 0) Port 1: 下游 → EP1 (Bus 1) Port 2: 下游 → EP2 (Bus 2) Switch路由表 Bus 0 → Port 0 (RC) Bus 1 → Port 1 (EP1) Bus 2 → Port 2 (EP2) 工作流程 1. RC(00:00.0)读EP2(02:00.0) MRd: 地址路由 → Port 2 2. EP2回复CplD给RC CplD: RequesterID00:00.0 → Bus 0 → Port 06. 复杂场景6.1 多级Switch拓扑拓扑 RC (00:00.0) │ Switch A (01:00.0) ├─ Port 1: Switch B (02:00.0) │ ├─ EP1 (03:00.0) │ └─ EP2 (04:00.0) └─ Port 2: EP3 (05:00.0) 场景RC读EP1(03:00.0)地址0x1000 步骤 1. RC发送MRd: RequesterID00:00.0, Tag0x01 Switch A: 地址路由到Port 1 Switch B: 地址路由到EP1 2. EP1回复CplD: CompleterID03:00.0 RequesterID00:00.0 (复制) Tag0x01 (复制) 3. Switch B收到CplD: 查找RequesterID00:00.0 → Bus 0 不知道Bus 0在哪但知道来自Port 0 转发到Port 0 (Switch A) 4. Switch A收到CplD: 查找RequesterID00:00.0 → Bus 0 Bus 0在Port 0 (RC) 转发到Port 0 5. RC收到CplD匹配Tag0x016.2 跨多个Switch的ID路由// Switch需要知道每个Bus在哪个端口// 通过配置空间的路由表实现// Switch的配置寄存器#defineSWITCH_PRIMARY_BUS0x18// 上游Bus#defineSWITCH_SECONDARY_BUS0x19// 下游起始Bus#defineSWITCH_SUBORDINATE_BUS0x1A// 下游最大Bus// Switch A配置// Primary Bus 0// Secondary Bus 1// Subordinate Bus 5 (包含所有下游Bus)// Switch B配置// Primary Bus 1 (来自Switch A)// Secondary Bus 2// Subordinate Bus 4 (只管理Bus 2-4)// ID路由规则// 如果RequesterID的Bus在[Secondary, Subordinate]范围内// → 下游端口// 否则// → 上游端口7. 错误处理7.1 Cpl状态字段// Cpl头中的Status字段3位 typedef enum { SC_SUCCESS 3b000, // 成功 SC_UNSUPPORTED 3b001, // 不支持请求 SC_CONFIG_RETRY 3b010, // 配置重试 SC_COMPLETER_ABORT 3b100, // 完成者中止 SC_UNEXPECTED 3b101 // 意外完成 } completion_status_t;7.2 错误Cpl示例// 场景EP无法完成读请求// EP收到MRd但地址无效// EP发送错误Cplerror_cpl{.fmt_type3b000_01010,// Cpl (无数据).completer_id16h0100,.requester_idmrd_header.requester_id,.tagmrd_header.tag,.statusSC_COMPLETER_ABORT,.byte_count12h0};// RC收到后// 1. 释放对应的Tag// 2. 报告错误可能产生中断// 3. 不会重试除非软件驱动重试7.3 超时处理// RC维护未完成请求的超时计时器structpending_request{uint8_ttag;uint64_tstart_time;uint8_tretry_count;};// 超时检查voidcheck_timeouts(void){for(inti0;i256;i){if(tag_pool[i].statusPENDING){if(current_time-tag_pool[i].start_timeTIMEOUT){// 超时处理handle_timeout(i);tag_pool[i].statusTIMEOUT;// 可选重试有限次数if(tag_pool[i].retry_countMAX_RETRY){retry_request(i);}}}}}8. 性能优化8.1 Tag重用和流水线// 为了高性能RC会流水线多个请求// 使用多个Tag实现并行// 示例RC连续读4个地址send_mrd(addr1,tag0x01);send_mrd(addr2,tag0x02);// 不等回复直接发send_mrd(addr3,tag0x03);send_mrd(addr4,tag0x04);// EP可以乱序回复// CplD for tag0x03 (先完成)// CplD for tag0x01// CplD for tag0x04// CplD for tag0x02// RC根据Tag正确匹配8.2 大传输拆分// PCIe支持最大4KB的TLP// 但实际实现可能限制更小// RC读256字节64个DW// EP可能拆分为多个CplD// CplD 1: length16 (64字节), byte_count256// CplD 2: length16 (64字节), byte_count192// CplD 3: length16 (64字节), byte_count128// CplD 4: length16 (64字节), byte_count64// CplD 5: length16 (64字节), byte_count0 ← 完成// RC用byte_count知道何时收完// lower_addr帮助定位第一个DW的位置9. 实际代码示例9.1 RC侧的Cpl处理// RC的TLP接收处理voidrc_handle_cpld(tlp_header_cpld_t*header,uint32_t*data){// 1. 提取关键字段uint8_ttagheader-tag;uint16_trequester_idheader-requester_id;uint8_tstatusheader-status;uint16_tbyte_countheader-byte_count;uint8_tlower_addrheader-lower_addr;// 2. 查找对应的未完成请求structpending_request*reqfind_request_by_tag(tag);if(!req){// 没有匹配的请求可能是错误log_error(Unexpected CplD, tag0x%02X,tag);return;}// 3. 检查状态if(status!SC_SUCCESS){log_error(CplD error, status0x%X, tag0x%02X,status,tag);complete_request(req,ERROR);return;}// 4. 处理数据// lower_addr[6:2]是DW偏移uint32_tdw_offsetlower_addr0x1F;// 将数据复制到请求的缓冲区uint32_t*destreq-bufferdw_offset;for(inti0;iheader-length;i){dest[i]data[i];}// 5. 更新剩余字节数req-remaining_bytes-header-length*4;// 6. 如果完成释放Tagif(req-remaining_bytes0){complete_request(req,SUCCESS);free_tag(tag);}}9.2 EP侧的Cpl生成// EP处理读请求并生成CplDvoidep_handle_mrd(tlp_header_mrd_t*header){// 1. 提取请求信息uint64_taddressheader-address;uint16_tlengthheader-length;uint16_trequester_idheader-requester_id;uint8_ttagheader-tag;uint8_tfirst_beheader-first_be;uint8_tlast_beheader-last_be;// 2. 读取数据uint32_tdata[length];if(!read_local_memory(address,data,length)){// 读失败发送错误Cplsend_error_cpl(requester_id,tag,SC_COMPLETER_ABORT);return;}// 3. 计算lower_addr// address[6:2]因为DW对齐uint8_tlower_addr(address2)0x1F;// 4. 生成CplDtlp_header_cpld_tcpld_header;cpld_header.fmt_type0x4A;// CplDcpld_header.lengthlength;cpld_header.completer_idMY_DEVICE_ID;cpld_header.requester_idrequester_id;cpld_header.tagtag;cpld_header.lower_addrlower_addr;cpld_header.byte_countlength*4;cpld_header.statusSC_SUCCESS;// 5. 发送CplDsend_tlp(cpld_header,data);}10. 总结Cpl TLP寻址逻辑的核心基于ID的路由Cpl使用Requester ID路由而不是地址路由Switch根据Bus Number查找路由表Tag匹配机制Tag是Requester分配的本地事务ID用于匹配请求和响应支持乱序完成和并行请求关键字段Requester ID告诉网络回复给谁Tag告诉Requester这是哪个请求的回复Lower Address帮助定位数据在缓冲区的位置Byte Count跟踪大传输的进度工作流程请求阶段RC → (地址路由) → EP 回复阶段EP → (ID路由) → RC设计优势解耦EP不需要知道RC的拓扑结构灵活支持复杂的多级Switch拓扑高效Tag机制支持请求流水线可靠状态字段和超时机制保证可靠性简单记忆请求TLP“我要访问这个地址”完成TLP“这是你要的数据还给[RequesterID]对应[Tag]”这种设计使得PCIe能够高效、可靠地在复杂拓扑中传输数据是PCIe协议的精妙之处。