NVDLA系列之C-model:cdp<62>

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操作序列的组成部分。以下是它们的简要描述:

  1. CdpWdmaReadphileSequence() 函数被跳过(#pragma CTC SKIP#pragma CTC ENDSKIP 之间的代码段),因此在代码中没有实际的操作。从函数名来看,它可能是用于执行Cdp模块的WDMA读取操作的序列,但由于被跳过了,所以没有实际的功能。

  2. 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() 函数的主要功能和流程:

  1. 从寄存器中读取配置变量,这些变量包括输入数据的尺寸、目的地址、线条跨度、面跨度以及目的数据元素的组数。

  2. 计算总共要传输的原子数目,这是由输入数据的尺寸和数据元素组数决定的。

  3. 初始化迭代器和计数器,包括 surf_iter(用于迭代处理面)、line_iter(用于迭代处理线条)、width_iter(用于迭代处理宽度)、sent_atom_num(已发送的原子数目)等。

  4. 进入主循环,该循环处理每个原子的数据传输。

  5. hls_atom_num_fifo_ 读取要处理的原子数目,并计算每个原子的轮次数(每个原子有4轮数据)。

  6. 计算当前原子数据的目的地址 payload_addr

  7. 准备 DMA 写入请求的命令部分,包括地址和大小。

  8. 发送 DMA 写入请求的命令。

  9. hls_out_fifo_ 读取每个原子的数据,并将数据存储到 post_calc_buffer 中。

  10. 准备 DMA 写入请求的数据部分。

  11. 发送 DMA 写入请求的数据。

  12. 如果是最后一个写入请求,设置 is_required_ack 为 true,以便在发送完最后一个请求后等待应答。

  13. 迭代更新迭代器和计数器,继续处理下一个原子。

  14. 当所有原子都处理完后,通知 cdp_done_ 事件,表示当前层的处理已完成。

  15. 打印日志,指示当前层的处理已完成。

这个函数的主要目的是将 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 缓冲区)的可用或空闲空间超过指定数量的工具函数。这些函数的目的是确保在进行数据传输或处理之前,有足够的资源可用或空闲,以避免数据丢失或阻塞。以下是这些函数的功能:

  1. WaitUntilRdmaFifoFreeSizeGreaterThan(uint32_t num):等待 RDMA FIFO 中的空闲空间超过指定数量 num。这个函数使用 rdma_fifo_num_free() 函数来检查空闲空间的数量,如果不足则等待 rdma_fifo_data_read_event() 事件。

  2. WaitUntilRdmaFifoAvailableSizeGreaterThan(uint32_t num):等待 RDMA FIFO 中的可用空间超过指定数量 num。这个函数使用 rdma_fifo_num_available() 函数来检查可用空间的数量,如果不足则等待 rdma_fifo_data_written_event() 事件。

  3. WaitUntilWdmaBufferFreeSizeGreaterThan(uint32_t num):此函数目前处于被注释状态,似乎不在代码中使用。它类似于第一个函数,但是针对的是 WDMA 缓冲区,同样使用 wdma_buffer_ 对象。

  4. 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 中。以下是函数的主要步骤:

  1. 首先,函数会获取DMA读响应的payload中的有效掩码(mask)和数据指针(payload_data_ptr)。掩码用于指示哪些32字节数据是有效的,哪些是无效的。

  2. 接下来,函数会检查掩码的值,如果掩码的最低位为1(即mask & 0x1不为0),则说明低32字节数据有效,将这部分数据复制到名为 rdma_atom_cube_ptr 的缓冲区中。

  3. 如果掩码的次低位为1(即mask & 0x2不为0),则说明高32字节数据有效,将这部分数据复制到 rdma_atom_cube_ptr 缓冲区的下一部分。

  4. rdma_atom_cube_ptr 缓冲区中的数据写入名为 rdma_fifo_ 的FIFO中,以便后续处理。每次写入一个32字节的数据块。

  5. 计算事务计数(txn_r),并根据数据类型(cdp_input_data_type_)检查响应数据中的每个元素是否为FP16的无穷大(inf)或NaN值,并在需要时更新计数器(inf_input_numnan_input_num)。

  6. 最后,函数会打印调试信息,显示每个32字节数据块的十六进制值。

这个函数的主要作用是将DMA读响应中的数据提取出来,并根据掩码标志判断哪些数据是有效的,然后将有效数据写入到FIFO中,以便后续的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值