NV_NVDLA_cvif.cpp
WriteRequest_sdp2cvif
void NV_NVDLA_cvif::WriteRequest_sdp2cvif() {
uint64_t base_addr;
uint64_t first_base_addr;
uint64_t last_base_addr;
uint64_t cur_address;
uint32_t size_in_byte;
uint32_t total_axi_size;
uint64_t payload_addr;
uint32_t payload_size;
uint8_t* axi_byte_enable_ptr;
uint32_t byte_iter;
uint32_t atom_iter;
uint32_t atom_num;
bool is_base_64byte_align;
bool is_rear_64byte_align;
bool is_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 command
sdp_wr_req = sdp2cvif_wr_cmd_fifo_->read();
payload_addr = sdp_wr_req->addr; // It's aligend to 32B, not 64B
payload_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 64B
is_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 not
total_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 transaction
cur_address += AXI_TRANSACTION_ATOM_SIZE;
atom_num = size_in_byte / DMA_TRANSACTION_ATOM_SIZE;
bt_payload = new dla_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_addr
axi_byte_enable_ptr[byte_iter] = TLM_BYTE_DISABLED; // All bytes should be enabled
} else if (( (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_addr
axi_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_PV
memset(&bt_payload->data[atom_iter*DMA_TRANSACTION_ATOM_SIZE], 0, DMA_TRANSACTION_ATOM_SIZE);
} else if (((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_addr
memset(&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(int i=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 payload
bt_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 fifo
sdp_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);
}
}
delete sdp_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 队列。如果写入请求需要确认,处理单元将等待确认,否则它将继续执行后续操作。这有助于管理内存写入请求的确认机制。