NV_NVDLA_cdp.cpp
CdpWdmaWriteSequenceThread
void NV_NVDLA_cdp::CdpWdmaWriteSequenceThread () {
// Varialbes
// Wait kick off event
while (true) {
wait(cdp_kickoff_);
CdpWdmaOrdinarySequence();
}
}
这段代码是一个线程函数,用于执行Cdp模块中的数据写入(WDMA)序列。下面是代码的主要结构和功能:
-
该函数运行在一个无限循环中(
while (true)
),等待cdp_kickoff_
事件。 -
当收到
cdp_kickoff_
事件后,函数调用CdpWdmaOrdinarySequence()
函数执行WDMA操作的普通序列。 -
这个线程函数的目的是在每次接收到触发事件后执行Cdp模块的WDMA写入操作。WDMA是用于将Cdp模块的处理结果写入外部存储器或其他目标的操作。
-
由于代码片段中没有提供
CdpWdmaOrdinarySequence()
函数的具体实现,因此无法详细描述它的功能。但一般来说,WDMA操作包括将Cdp模块的处理结果传输到外部存储器或其他目标,可能需要配置DMA传输和地址映射等操作。
总之,这段代码表示一个线程函数,用于触发Cdp模块的WDMA写入操作,并且可以根据实际需求在 CdpWdmaOrdinarySequence()
函数中执行WDMA操作的具体序列。
CdpWdmaReadphileSequence & CdpWdmaWritephileSequence
#pragma CTC SKIP
void NV_NVDLA_cdp::CdpWdmaReadphileSequence() {
CdpWdmaSequence();
}
void NV_NVDLA_cdp::CdpWdmaWritephileSequence() {
}
#pragma CTC ENDSKIP
这段代码中包含了两个函数:CdpWdmaReadphileSequence()
和 CdpWdmaWritephileSequence()
。这些函数似乎是用于Cdp模块的WDMA操作序列的组成部分。以下是它们的简要描述:
-
CdpWdmaReadphileSequence()
函数被跳过(#pragma CTC SKIP
和#pragma CTC ENDSKIP
之间的代码段),因此在代码中没有实际的操作。从函数名来看,它可能是用于执行Cdp模块的WDMA读取操作的序列,但由于被跳过了,所以没有实际的功能。 -
CdpWdmaWritephileSequence()
函数同样被跳过,因此也没有实际的操作。从函数名来看,它可能是用于执行Cdp模块的WDMA写入操作的序列,但在当前代码中没有提供具体的实现。
由于这两个函数都被跳过了,无法提供更详细的信息或功能描述。要了解它们的具体实现和用途,需要查看跳过部分之外的代码,或者查看函数的其他定义和调用。
CdpWdmaOrdinarySequence
void NV_NVDLA_cdp::CdpWdmaOrdinarySequence() {
CdpWdmaSequence();
}
CdpWdmaOrdinarySequence()
函数调用了 CdpWdmaSequence()
函数,但是在提供的代码片段中并没有包含 CdpWdmaSequence()
函数的具体实现。因此,要了解 CdpWdmaOrdinarySequence()
函数的详细功能,需要查看 CdpWdmaSequence()
函数的实际代码或者查找其他地方是否有 CdpWdmaSequence()
函数的定义和实现。
CdpWdmaOrdinarySequence()
函数似乎是用于执行Cdp模块的WDMA普通序列操作的函数,但具体的细节需要查看 CdpWdmaSequence()
函数的代码来确定。如果您有 CdpWdmaSequence()
函数的实现或其他相关信息,可以提供更多上下文,以便我能够提供更详细的帮助。
CdpWdmaSequence
void NV_NVDLA_cdp::CdpWdmaSequence() {
// Config variables, they have corresponding value in registers
uint32_t cube_width, cube_height, cube_channel;
uint64_t dst_base_addr;
uint64_t line_stride;
uint64_t surface_stride;
uint8_t element_per_group_dst;
// Control variables
// # Iterators
uint32_t width_iter;
uint32_t surf_iter;
uint32_t surf_num;
uint32_t line_iter;
uint32_t atom_iter;
uint32_t atom_num;
uint32_t total_atom_num;
uint32_t sent_atom_num;
uint32_t round_iter;
uint32_t round_num;
// # Evaluated variable
uint64_t payload_addr;
bool is_required_ack=false;
uint32_t element_data_index;
int16_t *hls_element;
uint8_t *payload_data_ptr;
CdpConfig *cfg;
cslInfo(("NV_NVDLA_cdp::CdpWdmaSequence, start.\n"));
// Copy from register value to local config variables, similar with RTL connection, begin
// # Cube setting
cfg = cdp_fifo_cfg_wdma_->read();
cube_width = cfg->cdp_rdma_width_+1;
cube_height = cfg->cdp_rdma_height_+1;
cube_channel = cfg->cdp_rdma_channel_+1;
line_stride = cdp_dst_line_stride_ << 5;
surface_stride = cdp_dst_surface_stride_ << 5;
dst_base_addr = ((uint64_t)cdp_dst_base_addr_high_ << 32) | ((uint64_t)cdp_dst_base_addr_low_ << 5);
delete cfg;
// # Precision setting
switch (cdp_input_data_type_) {
case DATA_FORMAT_IS_INT8: {
element_per_group_dst = ELEMENT_PER_GROUP_INT8;
break;
}
case DATA_FORMAT_IS_INT16: {
element_per_group_dst = ELEMENT_PER_GROUP_INT16;
break;
}
case DATA_FORMAT_IS_FP16: {
element_per_group_dst = ELEMENT_PER_GROUP_FP16;
break;
}
#pragma CTC SKIP
default: break;
#pragma CTC ENDSKIP
}
surf_num = (cube_channel+element_per_group_dst-1)/element_per_group_dst;
total_atom_num = cube_width * cube_height * surf_num;
cslDebug((30, "%s: WxHxC=%dx%dx%d, total_atoms=%d\n", __FUNCTION__, cube_width, cube_height, cube_channel, total_atom_num));
surf_iter = 0;
line_iter = 0;
width_iter = 0; // width of current stripe
sent_atom_num = 0;
while(sent_atom_num < total_atom_num) {
atom_num = hls_atom_num_fifo_->read();
round_num = atom_num * 4; // each atom has 4 rounds
payload_addr = dst_base_addr + surf_iter * surface_stride + line_iter * line_stride + width_iter*ATOM_CUBE_SIZE;
// Prepare payload
cslDebug((30, "NV_NVDLA_cdp::SendDmaWriteRequest, begin. total_atom_num=%d sent_atom_num=%d atom_num=%d payload_addr=0x%lx\n", total_atom_num, sent_atom_num, atom_num, payload_addr));
dma_wr_req_cmd_payload_->pd.dma_write_cmd.addr = payload_addr;
dma_wr_req_cmd_payload_->pd.dma_write_cmd.size = atom_num-1;
payload_data_ptr = reinterpret_cast <uint8_t *> (dma_wr_req_data_payload_->pd.dma_write_data.data);
if ((sent_atom_num + atom_num) == total_atom_num) // The last write request of the Cube
{
is_required_ack = true;
cslDebug((30, "CDP is_required_ack=true\n"));
}
// Send write command
txn_w++;
cslDebug((50, "CdpWdmaSequence SendDmaWriteRquest cmd payload_addr=0x%lx payload_atom_num=%d txn=%d required_ack=%d\n", payload_addr, atom_num, txn_w, is_required_ack));
SendDmaWriteRequest(dma_wr_req_cmd_payload_, dma_delay_, cdp_dst_ram_type_, is_required_ack);
cslDebug((50, "CdpWdmaSequence SendDmaWriteRquest cmd done\n"));
for (round_iter = 0; round_iter < round_num; round_iter++) {
hls_element = hls_out_fifo_->read(); //8B
int round_x = round_iter%atom_num;
int round_y = round_iter/atom_num;
element_data_index = round_x*ATOM_CUBE_SIZE + round_y*8;
memcpy(&post_calc_buffer[element_data_index], hls_element, 8); // Max size of payload_data_ptr is 256B
delete [] hls_element;
}
// Send write data
for (atom_iter=0; atom_iter<atom_num; atom_iter++) {
memcpy(&payload_data_ptr[(atom_iter%2)*ATOM_CUBE_SIZE], &post_calc_buffer[atom_iter*ATOM_CUBE_SIZE], ATOM_CUBE_SIZE);
if (((atom_iter%2)==1) || atom_iter==(atom_num-1)) {
// Send write data
cslDebug((50, "CdpWdmaSequence SendDmaWriteRquest data txn=%d atom_iter=%d\n", txn_w, atom_iter));
SendDmaWriteRequest(dma_wr_req_data_payload_, dma_delay_, cdp_dst_ram_type_);
cslDebug((50, "CdpWdmaSequence SendDmaWriteRquest data done\n"));
}
}
if(is_required_ack) cdp_done_.notify();
if (surf_iter == (surf_num-1)) {
surf_iter = 0;
if ((width_iter + atom_num) == cube_width) {
width_iter = 0;
line_iter++;
}
else
width_iter += atom_num;
}
else
surf_iter++;
sent_atom_num += atom_num;
}
cslInfo(("%s finished current layer\n", __FUNCTION__));
}
CdpWdmaSequence()
函数是用于执行 CDP(Convolutional Data Processor)模块的 WDMA(Write Direct Memory Access)序列操作的函数。它将配置变量和控制变量与寄存器中的相应值进行匹配,然后根据这些值执行数据传输。
以下是 CdpWdmaSequence()
函数的主要功能和流程:
-
从寄存器中读取配置变量,这些变量包括输入数据的尺寸、目的地址、线条跨度、面跨度以及目的数据元素的组数。
-
计算总共要传输的原子数目,这是由输入数据的尺寸和数据元素组数决定的。
-
初始化迭代器和计数器,包括
surf_iter
(用于迭代处理面)、line_iter
(用于迭代处理线条)、width_iter
(用于迭代处理宽度)、sent_atom_num
(已发送的原子数目)等。 -
进入主循环,该循环处理每个原子的数据传输。
-
从
hls_atom_num_fifo_
读取要处理的原子数目,并计算每个原子的轮次数(每个原子有4轮数据)。 -
计算当前原子数据的目的地址
payload_addr
。 -
准备 DMA 写入请求的命令部分,包括地址和大小。
-
发送 DMA 写入请求的命令。
-
从
hls_out_fifo_
读取每个原子的数据,并将数据存储到post_calc_buffer
中。 -
准备 DMA 写入请求的数据部分。
-
发送 DMA 写入请求的数据。
-
如果是最后一个写入请求,设置
is_required_ack
为 true,以便在发送完最后一个请求后等待应答。 -
迭代更新迭代器和计数器,继续处理下一个原子。
-
当所有原子都处理完后,通知
cdp_done_
事件,表示当前层的处理已完成。 -
打印日志,指示当前层的处理已完成。
这个函数的主要目的是将 CDP 处理后的数据传输到外部存储器或其他目的地,同时保证数据的正确性和完整性。这个过程涉及到地址计算、数据传输、DMA 请求等操作。函数的具体实现可能会根据硬件架构和寄存器配置略有不同,但总体流程应该是类似的。
WaitUntilRdmaFifoFreeSizeGreaterThan
#pragma CTC SKIP
void NV_NVDLA_cdp::WaitUntilRdmaFifoFreeSizeGreaterThan(uint32_t num) {
while (uint32_t(rdma_fifo_->num_free()) < num) {
wait( rdma_fifo_->data_read_event() );
}
}
void NV_NVDLA_cdp::WaitUntilRdmaFifoAvailableSizeGreaterThan(uint32_t num) {
while (uint32_t(rdma_fifo_->num_available()) < num) {
wait( rdma_fifo_->data_written_event() );
}
}
void NV_NVDLA_cdp::WaitUntilWdmaBufferFreeSizeGreaterThan(uint32_t num) {
// while (uint32_t(wdma_buffer_->num_free()) < num) {
// wait( wdma_buffer_->data_read_event() );
// }
}
void NV_NVDLA_cdp::WaitUntilWdmaBufferAvailableSizeGreaterThan(uint32_t num) {
// while (uint32_t(wdma_buffer_->num_available()) < num) {
// wait( wdma_buffer_->data_written_event() );
// }
}
#pragma CTC ENDSKIP
这些函数是用于等待特定资源(如 RDMA FIFO、WDMA 缓冲区)的可用或空闲空间超过指定数量的工具函数。这些函数的目的是确保在进行数据传输或处理之前,有足够的资源可用或空闲,以避免数据丢失或阻塞。以下是这些函数的功能:
-
WaitUntilRdmaFifoFreeSizeGreaterThan(uint32_t num)
:等待 RDMA FIFO 中的空闲空间超过指定数量num
。这个函数使用rdma_fifo_
的num_free()
函数来检查空闲空间的数量,如果不足则等待rdma_fifo_
的data_read_event()
事件。 -
WaitUntilRdmaFifoAvailableSizeGreaterThan(uint32_t num)
:等待 RDMA FIFO 中的可用空间超过指定数量num
。这个函数使用rdma_fifo_
的num_available()
函数来检查可用空间的数量,如果不足则等待rdma_fifo_
的data_written_event()
事件。 -
WaitUntilWdmaBufferFreeSizeGreaterThan(uint32_t num)
:此函数目前处于被注释状态,似乎不在代码中使用。它类似于第一个函数,但是针对的是 WDMA 缓冲区,同样使用wdma_buffer_
对象。 -
WaitUntilWdmaBufferAvailableSizeGreaterThan(uint32_t num)
:此函数目前处于被注释状态,似乎不在代码中使用。它类似于第二个函数,但是针对的是 WDMA 缓冲区,同样使用wdma_buffer_
对象。
这些函数可以确保在进行数据传输或处理之前,有足够的资源可供使用,以保证数据的流畅传输。根据这些函数的实现,它们会不断轮询资源的可用情况,直到满足指定的条件。在并发处理中,这种等待资源可用的机制非常有用,以避免因资源不足而导致的阻塞或数据丢失。
SendDmaReadRequest
// Send DMA read request
void NV_NVDLA_cdp::SendDmaReadRequest(nvdla_dma_rd_req_t* payload, sc_time& delay, uint8_t src_ram_type) {
cslDebug((50, "NV_NVDLA_cdp::SendDmaReadRequest, start.\n"));
if ( (NVDLA_CDP_RDMA_D_SRC_DMA_CFG_0_SRC_RAM_TYPE_MC) == src_ram_type ) {
NV_NVDLA_cdp_base::cdp2mcif_rd_req_b_transport(payload, dma_delay_);
} else {
NV_NVDLA_cdp_base::cdp2cvif_rd_req_b_transport(payload, dma_delay_);
}
cslDebug((50, "NV_NVDLA_cdp::SendDmaReadRequest, end.\n"));
}
这段代码定义了一个函数 SendDmaReadRequest
,用于发送DMA读请求。该函数接受以下参数:
-
nvdla_dma_rd_req_t* payload
:DMA读请求的数据结构指针,其中包含了请求的详细信息,如地址、大小等。 -
sc_time& delay
:表示传输的延迟时间。 -
uint8_t src_ram_type
:表示源RAM的类型,根据不同的源RAM类型,调用不同的接口进行DMA读请求。
在函数内部,它首先根据源RAM的类型(src_ram_type
)决定是调用 cdp2mcif_rd_req_b_transport
还是 cdp2cvif_rd_req_b_transport
函数来发送DMA读请求。这两个函数用于不同的DMA读请求传输。
在发送请求后,函数会打印一些调试信息,标志着请求发送的开始和结束。
这个函数的作用是将 DMA 读请求发送给相应的接口(MCIF 或 CVIF),以触发从RAM中读取数据的操作。具体的操作会根据请求的内容和类型在接口的实现中完成。
ExtractRdmaResponsePayload
void NV_NVDLA_cdp::ExtractRdmaResponsePayload(nvdla_dma_rd_rsp_t* payload){
// Extract data from payload
// Each payload is 64 byte, two mask bit tells which 32 byte groups are effective
uint8_t *payload_data_ptr;
int8_t *rdma_atom_cube_ptr;
uint8_t mask;
cslDebug((50, "NV_NVDLA_cdp::ExtractRdmaResponsePayload, get a dma read response payload\n"));
mask = payload->pd.dma_read_data.mask;
payload_data_ptr = reinterpret_cast <uint8_t *> (payload->pd.dma_read_data.data);
#pragma CTC SKIP
if (mask==0x2) { // First 32B is not effective, second 32B is effective
FAIL(("NV_NVDLA_cdp::ExtractRdmaResponsePayload, mask==2 is unexcepted"));
}
#pragma CTC ENDSKIP
// Handling lower 32 bytes
if (0 != (mask & 0x1)) {
rdma_atom_cube_ptr = new int8_t[ATOM_CUBE_SIZE];
memcpy(rdma_atom_cube_ptr, payload_data_ptr, ATOM_CUBE_SIZE);
rdma_fifo_->write(rdma_atom_cube_ptr);
txn_r++;
if(cdp_input_data_type_ == DATA_FORMAT_IS_FP16)
{
uint16_t *ptr = reinterpret_cast <uint16_t *> (rdma_atom_cube_ptr);
for(int i=0;i<16;i++)
{
uint32_t exp = (ptr[i] >> 10) & 0x1f;
uint32_t frac = ptr[i] & 0x3ff;
if(exp == 0x1f)
{
if(frac == 0)
inf_input_num++;
else
nan_input_num++;
}
}
}
cslDebug((70, "write to rdma_buffer. mask A\n"));
for(int i=0;i<32;i++)
{
cslDebug((70, "%02x ", rdma_atom_cube_ptr[i]));
}
cslDebug((70, "\n"));
}
// Handling upper 32 bytes
if (0 != (mask & 0x2)) {
rdma_atom_cube_ptr = new int8_t[ATOM_CUBE_SIZE];
memcpy(rdma_atom_cube_ptr, &payload_data_ptr[ATOM_CUBE_SIZE], ATOM_CUBE_SIZE);
rdma_fifo_->write(rdma_atom_cube_ptr);
txn_r++;
if(cdp_input_data_type_ == DATA_FORMAT_IS_FP16)
{
uint16_t *ptr = reinterpret_cast <uint16_t *> (rdma_atom_cube_ptr);
for(int i=0;i<16;i++)
{
uint32_t exp = (ptr[i] >> 10) & 0x1f;
uint32_t frac = ptr[i] & 0x3ff;
if(exp == 0x1f)
{
if(frac == 0)
inf_input_num++;
else
nan_input_num++;
}
}
}
cslDebug((70, "write to rdma_buffer. mask B\n"));
for(int i=0;i<32;i++)
{
cslDebug((70, "%02x ", rdma_atom_cube_ptr[i]));
}
cslDebug((70, "\n"));
}
cslDebug((50, "NV_NVDLA_cdp::ExtractRdmaResponsePayload, txn=%d, atom_iter=%d\n", txn_r / 8, txn_r % 8));
}
这段代码定义了一个函数 ExtractRdmaResponsePayload
,用于从DMA读响应的payload中提取数据并将其写入一个 FIFO 中。以下是函数的主要步骤:
-
首先,函数会获取DMA读响应的payload中的有效掩码(
mask
)和数据指针(payload_data_ptr
)。掩码用于指示哪些32字节数据是有效的,哪些是无效的。 -
接下来,函数会检查掩码的值,如果掩码的最低位为1(即
mask & 0x1
不为0),则说明低32字节数据有效,将这部分数据复制到名为rdma_atom_cube_ptr
的缓冲区中。 -
如果掩码的次低位为1(即
mask & 0x2
不为0),则说明高32字节数据有效,将这部分数据复制到rdma_atom_cube_ptr
缓冲区的下一部分。 -
将
rdma_atom_cube_ptr
缓冲区中的数据写入名为rdma_fifo_
的FIFO中,以便后续处理。每次写入一个32字节的数据块。 -
计算事务计数(
txn_r
),并根据数据类型(cdp_input_data_type_
)检查响应数据中的每个元素是否为FP16的无穷大(inf)或NaN值,并在需要时更新计数器(inf_input_num
和nan_input_num
)。 -
最后,函数会打印调试信息,显示每个32字节数据块的十六进制值。
这个函数的主要作用是将DMA读响应中的数据提取出来,并根据掩码标志判断哪些数据是有效的,然后将有效数据写入到FIFO中,以便后续的处理。