NV_NVDLA_cvif.cpp
WriteRequest_sdp2cvif
voidNV_NVDLA_cvif::WriteRequest_sdp2cvif(){uint64_tbase_addr;uint64_tfirst_base_addr;uint64_tlast_base_addr;uint64_tcur_address;uint32_tsize_in_byte;uint32_ttotal_axi_size;uint64_tpayload_addr;uint32_tpayload_size;uint8_t*axi_byte_enable_ptr;uint32_tbyte_iter;uint32_tatom_iter;uint32_tatom_num;boolis_base_64byte_align;boolis_rear_64byte_align;boolis_read=false;uint8_t*axi_atom_ptr;nvdla_dbb_extension*nvdla_dbb_ext=NULL;client_cvif_wr_req_t*sdp_wr_req;dla_b_transport_payload*bt_payload;这段代码是一个 C++ 函数,它位于名为 “NV_NVDLA_cvif” 的类中,函数名为 “WriteRequest_sdp2cvif”。这个函数的主要功能是执行一些内存写入请求的处理操作。以下是代码中定义的变量和操作的简要总结:
定义了多个变量,包括用于地址计算的
base_addr、first_base_addr和last_base_addr,以及用于循环迭代的cur_address,用于数据大小的size_in_byte和total_axi_size,以及其他用于字节操作的变量。定义了布尔类型的变量,如
is_base_64byte_align和is_rear_64byte_align,用于检查地址是否按照64字节对齐。定义了一个布尔类型的变量
is_read,用于指示是否执行读取操作。定义了一个指针变量
axi_atom_ptr,它是一个指向字节的指针,用于操作存储器中的数据。定义了一个指针
nvdla_dbb_ext,它指向 “nvdla_dbb_extension” 类型的对象,用于处理一些数据扩展的操作。定义了两个指针变量
sdp_wr_req和bt_payload,它们分别指向 “client_cvif_wr_req_t” 和 “dla_b_transport_payload” 类型的对象,用于处理写入请求和数据传输的操作。
这段代码的主要目的是处理内存写入请求,可能涉及地址计算、数据大小检查、数据传输等操作。
while(true){// Read one write commandsdp_wr_req=sdp2cvif_wr_cmd_fifo_->read();payload_addr=sdp_wr_req->addr;// It's aligend to 32B, not 64Bpayload_size=sdp_wr_req->size;cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, got one write command from sdp2cvif_wr_cmd_fifo_\x0A"));cslDebug((50," payload_addr: 0x%lx\x0A",payload_addr));cslDebug((50," payload_size: 0x%x\x0A",payload_size));is_base_64byte_align=payload_addr%AXI_TRANSACTION_ATOM_SIZE==0;first_base_addr=is_base_64byte_align?payload_addr:payload_addr-DMA_TRANSACTION_ATOM_SIZE;// Align to 64Bis_rear_64byte_align=(payload_addr+payload_size)%AXI_TRANSACTION_ATOM_SIZE==0;// According to DBB_PV standard, data_length shall be equal or greater than DBB_PV m_size * m_length no matter the transactions is aglined or nottotal_axi_size=payload_size+(is_base_64byte_align?0:DMA_TRANSACTION_ATOM_SIZE)+(is_rear_64byte_align?0:DMA_TRANSACTION_ATOM_SIZE);last_base_addr=first_base_addr+total_axi_size-AXI_TRANSACTION_ATOM_SIZE;这段代码是一个循环,它用于处理内存写入请求。以下是代码的简要解释:
while(true)表示一个无限循环,即这段代码将一直运行,直到遇到显式的退出条件才会停止。sdp_wr_req = sdp2cvif_wr_cmd_fifo_->read();从名为sdp2cvif_wr_cmd_fifo_的FIFO队列中读取一个写入命令,并将其存储在sdp_wr_req变量中。payload_addr存储了从写入命令中获取的地址,该地址可能不是64字节对齐的。payload_size存储了从写入命令中获取的数据大小。is_base_64byte_align和is_rear_64byte_align用于检查地址是否按照64字节对齐。is_base_64byte_align检查payload_addr是否是64字节对齐,is_rear_64byte_align检查payload_addr + payload_size是否是64字节对齐。first_base_addr和last_base_addr用于计算一系列地址,以确保数据访问在64字节的边界上对齐。first_base_addr被设置为64字节对齐的地址,而last_base_addr被设置为在first_base_addr的基础上加上总的AXI事务大小(total_axi_size)减去一个AXI事务的大小(AXI_TRANSACTION_ATOM_SIZE)。
总体而言,这段代码的目的是处理来自sdp2cvif_wr_cmd_fifo_队列的内存写入命令,它计算了与地址对齐和数据大小相关的一些参数,以便后续的内存访问和数据传输操作能够按照正确的边界和大小进行。这段代码在数据传输和内存管理的上下文中使用。
// if ( total_axi_size <= AXI_TRANSACTION_ATOM_SIZE ) {// // The first and last transaction is actually the same// last_base_addr = first_base_addr;// } else {// last_base_addr = (first_base_addr + total_axi_size) - (first_base_addr + total_axi_size)%AXI_TRANSACTION_ATOM_SIZE;// }// if (total_axi_size + first_base_addr%CVIF_MAX_MEM_TRANSACTION_SIZE <= CVIF_MAX_MEM_TRANSACTION_SIZE) {// // Base and last are in the same AXI transaction// } else {// // Base and last are in different AXI transaction// last_base_addr =// }// } else if ((first_base_addr + total_axi_size)%CVIF_MAX_MEM_TRANSACTION_SIZE != 0) {// if (total_axi_size >= (first_base_addr + total_axi_size)%CVIF_MAX_MEM_TRANSACTION_SIZE) {// last_base_addr = first_base_addr + total_axi_size - (first_base_addr + total_axi_size)%CVIF_MAX_MEM_TRANSACTION_SIZE;// } else {// last_base_addr = first_base_addr;// }// } else {// if (total_axi_size >= CVIF_MAX_MEM_TRANSACTION_SIZE) {// last_base_addr = first_base_addr + total_axi_size - CVIF_MAX_MEM_TRANSACTION_SIZE;// } else {// last_base_addr = first_base_addr;// }// }cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif:\x0A"));cslDebug((50," first_base_addr: 0x%lx\x0A",first_base_addr));cslDebug((50," last_base_addr: 0x%lx\x0A",last_base_addr));cslDebug((50," total_axi_size: 0x%x\x0A",total_axi_size));这段注释掉的代码部分似乎是在计算last_base_addr的具体值,以确保内存访问和数据传输按照正确的AXI事务边界进行的。由于代码被注释掉了,所以它实际上并没有执行。下面是对该段代码的解释:
这段代码在处理last_base_addr的计算时,考虑了不同的情况,以确保内存访问的正确性。last_base_addr是用于指示最后一个AXI事务的起始地址的。
- 首先,代码检查了
total_axi_size是否小于等于AXI_TRANSACTION_ATOM_SIZE,如果是,则将last_base_addr设置为first_base_addr,因为只有一个AXI事务。 - 然后,代码检查了是否
first_base_addr和last_base_addr在相同的AXI事务中。如果在同一AXI事务中,它可能需要进行一些特殊处理,但是由于代码被注释掉了,所以无法确定具体操作。 - 最后,代码通过计算余数来检查
first_base_addr + total_axi_size是否跨越了CVIF_MAX_MEM_TRANSACTION_SIZE的边界,并相应地计算last_base_addr。
总之,这段代码的目的是确保内存访问按照AXI事务边界对齐和分割,以适应底层硬件或协议的要求。
// cur_address = payload_addr;cur_address=is_base_64byte_align?payload_addr:first_base_addr;// Align to 64B//Split dma request to axi requests// while(cur_address < payload_addr + payload_size) {}while(cur_address<=last_base_addr){base_addr=cur_address;size_in_byte=AXI_TRANSACTION_ATOM_SIZE;// Check whether next ATOM belongs to current AXI transaction// while (((cur_address + DMA_TRANSACTION_ATOM_SIZE) < (payload_addr + payload_size)) && ((cur_address + DMA_TRANSACTION_ATOM_SIZE) % CVIF_MAX_MEM_TRANSACTION_SIZE != 0)) {// size_in_byte += DMA_TRANSACTION_ATOM_SIZE;// cur_address += DMA_TRANSACTION_ATOM_SIZE;// }while(((cur_address+AXI_TRANSACTION_ATOM_SIZE)<(first_base_addr+total_axi_size))&&((cur_address+AXI_TRANSACTION_ATOM_SIZE)%CVIF_MAX_MEM_TRANSACTION_SIZE!=0)){size_in_byte+=AXI_TRANSACTION_ATOM_SIZE;cur_address+=AXI_TRANSACTION_ATOM_SIZE;}// start address of next axi transactioncur_address+=AXI_TRANSACTION_ATOM_SIZE;atom_num=size_in_byte/DMA_TRANSACTION_ATOM_SIZE;bt_payload=newdla_b_transport_payload(size_in_byte,dla_b_transport_payload::DLA_B_TRANSPORT_PAYLOAD_TYPE_MC);axi_byte_enable_ptr=bt_payload->gp.get_byte_enable_ptr();cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, base_addr=0x%lx size_in_byte=0x%x atom_num=0x%x\x0A",base_addr,size_in_byte,atom_num));for(byte_iter=0;byte_iter<size_in_byte;byte_iter++){if((base_addr==first_base_addr)&&(false==is_base_64byte_align)&&(byte_iter<DMA_TRANSACTION_ATOM_SIZE)){// Diable 1st DMA atom of the unaligned first_base_addraxi_byte_enable_ptr[byte_iter]=TLM_BYTE_DISABLED;// All bytes should be enabled}elseif(((base_addr+size_in_byte)==(last_base_addr+AXI_TRANSACTION_ATOM_SIZE))&&(false==is_rear_64byte_align)&&(byte_iter>=size_in_byte-DMA_TRANSACTION_ATOM_SIZE)){// Diable 2nd DMA atom of the unaligned last_base_addraxi_byte_enable_ptr[byte_iter]=TLM_BYTE_DISABLED;// All bytes should be enabled}else{axi_byte_enable_ptr[byte_iter]=TLM_BYTE_ENABLED;// All bytes should be enabled}}cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, TLM_BYTE_ENABLE is done\x0A"));这段代码的主要目的是将一个大的内存写入请求分割成多个小的 AXI 事务请求,以便按照 AXI 事务边界进行传输,并为每个小事务创建相应的 AXI 事务数据。
以下是代码的主要步骤和功能:
cur_address初始化为payload_addr或first_base_addr,具体取决于是否需要按照64字节对齐。这是为了确保起始地址在AXI事务边界上对齐。while(cur_address <= last_base_addr)循环迭代,这个循环的目的是将大的内存写入请求分割成多个小的 AXI 事务请求。每次迭代,base_addr被设置为cur_address,size_in_byte被初始化为AXI_TRANSACTION_ATOM_SIZE,表示一个AXI事务的大小。在内层循环中,代码检查是否下一个 ATOM 是否仍然属于当前 AXI 事务,以确保不会跨越AXI事务边界。如果是,
size_in_byte会增加一个 AXI 事务的大小,cur_address也会相应地增加。在每次循环迭代中,会创建一个
dla_b_transport_payload对象bt_payload,用于存储小的AXI事务数据。这个对象的大小等于size_in_byte。对于每个字节,代码会根据地址对齐情况设置
axi_byte_enable_ptr,以确定哪些字节需要被禁用(TLM_BYTE_DISABLED)或启用(TLM_BYTE_ENABLED)。这是为了确保数据按照正确的方式传输,特别是在不对齐的边界上。最后,代码输出一些调试信息,包括
base_addr、size_in_byte和atom_num等信息。
总之,这段代码的功能是将大的内存写入请求分割成多个小的 AXI 事务请求,并为每个小事务创建相应的 AXI 事务数据,同时考虑了地址对齐和字节启用/禁用的情况,以确保数据传输的正确性。这通常用于处理底层硬件或协议要求的内存访问和数据传输。
for(atom_iter=0;atom_iter<atom_num;atom_iter++){if((base_addr==first_base_addr)&&(false==is_base_64byte_align)&&(0==atom_iter)){// Disable 1st DMA atom of the unaligned first_base_addr// Use unaligned address as required by DBB_PVmemset(&bt_payload->data[atom_iter*DMA_TRANSACTION_ATOM_SIZE],0,DMA_TRANSACTION_ATOM_SIZE);}elseif(((base_addr+size_in_byte)==(last_base_addr+AXI_TRANSACTION_ATOM_SIZE))&&(false==is_rear_64byte_align)&&((atom_iter+1)==atom_num)){// Disable 2nd DMA atom of the unaligned last_base_addrmemset(&bt_payload->data[atom_iter*DMA_TRANSACTION_ATOM_SIZE],0,DMA_TRANSACTION_ATOM_SIZE);}else{cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, before read an atom from sdp2cvif_wr_data_fifo_, base_addr = 0x%lx, atom_iter=0x%x\x0A",base_addr,atom_iter));axi_atom_ptr=sdp2cvif_wr_data_fifo_->read();for(inti=0;i<DMA_TRANSACTION_ATOM_SIZE;i++){cslDebug((50,"%02x ",axi_atom_ptr[i]));}cslDebug((50,"\x0A"));cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, after read an atom from sdp2cvif_wr_data_fifo_\x0A"));memcpy(&bt_payload->data[atom_iter*DMA_TRANSACTION_ATOM_SIZE],axi_atom_ptr,DMA_TRANSACTION_ATOM_SIZE);delete[]axi_atom_ptr;}}这段代码处理小的 AXI 事务请求的数据部分。它包括以下关键步骤:
for循环迭代atom_iter,该迭代器控制了在一个小的 AXI 事务中处理的 DMA 原子数据的数量。在每次循环迭代中,代码检查以下情况:
- 如果
base_addr不是64字节对齐,且atom_iter为0(表示在当前 AXI 事务中的第一个 DMA 原子数据),则会执行以下操作:- 将
bt_payload->data中与当前 DMA 原子数据对应的内存区域用0填充(memset)。这是为了在不对齐的情况下对第一个 DMA 原子数据进行禁用。
- 将
- 如果
base_addr + size_in_byte不是64字节对齐,且atom_iter为当前 AXI 事务中的最后一个 DMA 原子数据,则会执行以下操作:- 同样,将
bt_payload->data中与当前 DMA 原子数据对应的内存区域用0填充,以禁用最后一个不对齐的 DMA 原子数据。
- 同样,将
- 否则,代码将从
sdp2cvif_wr_data_fifo_队列中读取一个 AXI 原子数据,然后将其拷贝(memcpy)到bt_payload->data中,以准备发送到 AXI 总线。读取和拷贝操作将小的 AXI 事务数据放入bt_payload->data中。
- 如果
无论哪种情况,如果成功读取和处理了一个 DMA 原子数据,代码都会使用
delete[]来释放分配的内存,以防止内存泄漏。
总之,这段代码的主要功能是处理小的 AXI 事务请求的数据部分,它根据地址对齐情况,禁用了不对齐的 DMA 原子数据,并将合适的 DMA 原子数据从sdp2cvif_wr_data_fifo_队列中读取和拷贝到bt_payload->data中,以准备发送到 AXI 总线。这有助于确保数据传输按照 AXI 事务边界正确进行。
if((base_addr==first_base_addr)&&(false==is_base_64byte_align)){base_addr+=DMA_TRANSACTION_ATOM_SIZE;}cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, base_address=0x%lx size in byte=0x%x\x0A",base_addr,size_in_byte));// Prepare write payloadbt_payload->configure_gp(base_addr,size_in_byte,is_read);bt_payload->gp.get_extension(nvdla_dbb_ext);cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, sending write command to sdp_wr_req_fifo_.\x0A"));cslDebug((50," addr: 0x%016lx\x0A",base_addr));cslDebug((50," size: %d\x0A",size_in_byte));nvdla_dbb_ext->set_id(SDP_AXI_ID);nvdla_dbb_ext->set_size(64);nvdla_dbb_ext->set_length(size_in_byte/AXI_TRANSACTION_ATOM_SIZE);// if (base_addr%AXI_TRANSACTION_ATOM_SIZE != 0) //Set length(in unit of 64B) to be same as RTL// nvdla_dbb_ext->set_length(((size_in_byte - DMA_TRANSACTION_ATOM_SIZE) + DMA_TRANSACTION_ATOM_SIZE)/AXI_TRANSACTION_ATOM_SIZE);// else // base_addr is aligned to 64Bytes// nvdla_dbb_ext->set_length((size_in_byte + DMA_TRANSACTION_ATOM_SIZE)/AXI_TRANSACTION_ATOM_SIZE-1);// write payload to arbiter fifosdp_wr_req_fifo_->write(bt_payload);这段代码的主要功能是准备和发送一个写入请求。以下是代码的主要步骤和功能:
首先,代码检查是否
base_addr不是64字节对齐且不是当前 AXI 事务中的第一个 DMA 原子数据。如果是,它会将base_addr增加DMA_TRANSACTION_ATOM_SIZE,以确保地址对齐。代码输出一些调试信息,包括
base_addr和size_in_byte等信息。接下来,代码准备写入请求的 payload。它使用
bt_payload->configure_gp方法来配置 payload 的一些参数,包括base_addr(写入的起始地址)、size_in_byte(写入的数据大小)和is_read(指示是否执行读取操作)。通过
bt_payload->gp.get_extension(nvdla_dbb_ext)获取一个扩展对象nvdla_dbb_ext,该对象用于设置一些 AXI 相关的参数。代码输出一些额外的调试信息,包括地址(
addr)、大小(size)、ID(SDP_AXI_ID)和长度(length)等。最后,代码将准备好的 payload 写入名为
sdp_wr_req_fifo_的 FIFO 队列,以便将写入请求发送到相应的处理单元或总线。
总之,这段代码的功能是准备和发送一个内存写入请求,它首先检查地址对齐情况,然后配置 payload 的参数,设置 AXI 相关的扩展信息,并将写入请求写入 FIFO 队列以进行后续处理。这有助于确保写入请求按照正确的格式和参数发送到相应的目标。
// When the last split req is sent to ext, write true to sdp_wr_required_ack_fifo_ when ack is required.if(cur_address>=(payload_addr+payload_size)){if(sdp_wr_req->require_ack!=0){cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, require ack.\x0A"));sdp_wr_required_ack_fifo_->write(true);}else{cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, does not require ack.\x0A"));sdp_wr_required_ack_fifo_->write(false);}}else{cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, does not require ack.\x0A"));sdp_wr_required_ack_fifo_->write(false);}}deletesdp_wr_req;cslDebug((50,"NV_NVDLA_cvif::WriteRequest_sdp2cvif, write command processing done\x0A"));}}这段代码的主要功能是处理内存写入请求的最后部分,并确定是否需要等待确认(acknowledgment)。
代码首先检查
cur_address是否大于或等于payload_addr + payload_size,这表示所有小的 AXI 事务请求都已经发送完毕。如果是这种情况,代码继续执行下面的步骤,否则跳过这一部分。在内部条件检查中,代码判断了
sdp_wr_req->require_ack是否为非零值,这表示写入请求需要等待确认。如果需要等待确认,代码将true写入名为sdp_wr_required_ack_fifo_的 FIFO 队列中,以便后续处理单元知道需要发送确认。如果写入请求不需要等待确认,代码将
false写入sdp_wr_required_ack_fifo_。无论是否需要等待确认,最后都会释放
sdp_wr_req指针指向的内存对象,以避免内存泄漏。最后,代码输出一条调试信息,表示写入命令的处理已完成。
总结来说,这段代码负责确定写入请求是否需要等待确认,并相应地将确认信息写入 FIFO 队列。如果写入请求需要确认,处理单元将等待确认,否则它将继续执行后续操作。这有助于管理内存写入请求的确认机制。