diff --git a/tests/kfdtest/include/sdma_pkt_struct.h b/tests/kfdtest/include/sdma_pkt_struct.h index bc37b5189f..127c44f43d 100644 --- a/tests/kfdtest/include/sdma_pkt_struct.h +++ b/tests/kfdtest/include/sdma_pkt_struct.h @@ -30,6 +30,7 @@ const unsigned int SDMA_OP_WRITE = 2; const unsigned int SDMA_OP_FENCE = 5; const unsigned int SDMA_OP_TRAP = 6; +const unsigned int SDMA_OP_TIMESTAMP = 13; const unsigned int SDMA_OP_CONST_FILL = 11; @@ -316,4 +317,41 @@ typedef struct SDMA_PKT_TRAP_TAG } SDMA_PKT_TRAP, *PSDMA_PKT_TRAP; +/* +** Definitions for SDMA_PKT_TIMESTAMP packet +*/ + +typedef struct SDMA_PKT_TIMESTAMP_TAG +{ + + union + { + struct + { + unsigned int op:8; + unsigned int sub_op:8; + unsigned int reserved_0:16; + }; + unsigned int DW_0_DATA; + } HEADER_UNION; + + union + { + struct + { + unsigned int addr_31_0:32; + }; + unsigned int DW_1_DATA; + } ADDR_LO_UNION; + + union + { + struct + { + unsigned int addr_63_32:32; + }; + unsigned int DW_2_DATA; + } ADDR_HI_UNION; +} SDMA_PKT_TIMESTAMP, *PSDMA_PKT_TIMESTAMP; + #endif // __SDMA_PKT_STRUCT_H__ diff --git a/tests/kfdtest/src/KFDTestUtil.hpp b/tests/kfdtest/src/KFDTestUtil.hpp index 014b31d13f..fb4937e7dc 100644 --- a/tests/kfdtest/src/KFDTestUtil.hpp +++ b/tests/kfdtest/src/KFDTestUtil.hpp @@ -32,6 +32,8 @@ class BaseQueue; #define ARRAY_SIZE(_x) (sizeof(_x)/sizeof(_x[0])) +#define ALIGN_UP(x, align) (((uint64_t)(x) + (align) - 1) & ~(uint64_t)((align)-1)) +#define CounterToNanoSec(x) ((x) * 1000 / (is_dgpu() ? 27 : 100)) uint64_t RoundToPowerOf2(uint64_t val); diff --git a/tests/kfdtest/src/KFDTestUtilQueue.cpp b/tests/kfdtest/src/KFDTestUtilQueue.cpp new file mode 100644 index 0000000000..b6604287cf --- /dev/null +++ b/tests/kfdtest/src/KFDTestUtilQueue.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2018 Advanced Micro Devices, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include "SDMAQueue.hpp" +#include "PM4Queue.hpp" +#include "SDMAPacket.hpp" +#include "PM4Packet.hpp" +#include "KFDTestUtil.hpp" +#include "KFDTestUtilQueue.hpp" + +#define MB_PER_SEC(size, time) ((((size) * 1ULL) >> 20) * 1000ULL * 1000ULL * 1000ULL / (time)) + +class AsyncMPSQ; +class AsyncMPMQ; + +typedef std::shared_ptr sharedAsyncMPSQ; +typedef std::list AsyncMPSQList; + +typedef std::shared_ptr sharedPacket; +typedef std::list PacketList; + +/* AsyncMPSQ is short for Async multiple packet single queue. + * It is allowed to place a list of packets to run on one queue of the specified GPU node. + */ +class AsyncMPSQ { + public: + AsyncMPSQ() : m_queue(NULL), m_buf(NULL), m_event(NULL) { /*do nothing*/} + + virtual ~AsyncMPSQ(void) { Destroy(); } + + /* It is the main function to deal with the packet and queue.*/ + void PlacePacketOnNode(PacketList &packetList, int node, TSPattern tsp); + + /* Run the packets placed on nodes and return immediately.*/ + void Submit(void) { ASSERT_NE((HSAuint64)m_queue, NULL); m_queue->SubmitPacket(); } + + /* Return only when all packets are consumed. + * If there is any packet issues some IO operations, wait these IO to complete too. + */ + void Wait(void) { ASSERT_NE((HSAuint64)m_queue, NULL); m_queue->Wait4PacketConsumption(m_event); } + + /* Report the time used between packet [begin, end) in Global Counter on success. + * Return 0 on failure. + */ + HSAuint64 Report(int indexOfPacketBegin = 0, int indexOfPacketEnd = 0); + /* Report the timestamp around the packet. + * Return the time used on success. + * Return 0 on failure. + */ + HSAuint64 Report(int indexOfPacket, HSAuint64 &tsBegin, HSAuint64 &tsEnd); + + private: + BaseQueue *m_queue; + HSA_QUEUE_TYPE m_queueType; + HsaEvent *m_event; + /* m_ts points to m_buf's memory.*/ + HsaMemoryBuffer *m_buf; + TimeStamp *m_ts; + unsigned m_ts_count; + TSPattern m_ts_pattern; + + void AllocTimeStampBuf(int packetCount); + void Destroy(); + + /* It determines which queue will be created.*/ + void InitQueueType(PACKETTYPE packetType) { + if (packetType == PACKETTYPE_SDMA) + m_queueType = HSA_QUEUE_SDMA; + else if (packetType == PACKETTYPE_PM4) + m_queueType = HSA_QUEUE_COMPUTE; + else + WARN() << "Unsupported queue type!" << std::endl; + } + + unsigned int TimePacketSize(void) { + if (m_queueType == HSA_QUEUE_SDMA) + return SDMATimePacket(0).SizeInBytes(); + else if (m_queueType == HSA_QUEUE_COMPUTE) + return PM4ReleaseMemoryPacket(0, 0, 0, 0, 0).SizeInBytes(); + return 0; + } + + void CreateNewQueue(int node, unsigned int queueSize) { + if (m_queueType == HSA_QUEUE_SDMA) + m_queue = new SDMAQueue(); + else if (m_queueType == HSA_QUEUE_COMPUTE) + m_queue = new PM4Queue(); + else { + m_queue = NULL; + WARN() << "Unsupported queue type!" << std::endl; + } + + if (m_queue) + ASSERT_SUCCESS(m_queue->Create(node, queueSize)); + } + + void PlaceTimestampPacket(void *addr) { + if (m_queueType == HSA_QUEUE_SDMA) + PlacePacket(SDMATimePacket(addr)); + else if (m_queueType == HSA_QUEUE_COMPUTE) + PlacePacket( + PM4ReleaseMemoryPacket(true, (HSAuint64)addr, 0, true, true)); + else + WARN() << "Unsupported queue type!" << std::endl; + } + + void PlacePacket(const BasePacket &packet) { + m_queue->PlacePacket(packet); + } +}; + +void AsyncMPSQ::Destroy(void) { + /* Delete queue first.*/ + if (m_queue) { + delete m_queue; + } + + if (m_buf) + delete m_buf; + + if (m_event) + hsaKmtDestroyEvent(m_event); +} + +void AsyncMPSQ::AllocTimeStampBuf(int packetCount) { + if (m_ts_pattern == NOTS) { + m_buf = NULL; + m_ts = NULL; + m_ts_count = 0; + return; + } + + if (m_ts_pattern == ALLTS) + /* One extra timestamp packet.*/ + m_ts_count = packetCount + 1; + else + m_ts_count = 2; + + /* One more timestamp space to fit with alignment.*/ + HSAuint64 size = ALIGN_UP(sizeof(TimeStamp) * (m_ts_count + 1), PAGE_SIZE); + + m_buf = new HsaMemoryBuffer(size, 0, true, false); + + TimeStamp *array = m_buf->As(); + + /* SDMATimePacket need 32bytes aligned boundary dst address*/ + m_ts = reinterpret_castALIGN_UP(array, sizeof(TimeStamp)); +} + +void AsyncMPSQ::PlacePacketOnNode(PacketList &packets, int node, TSPattern tsp = ALLTS) { + int nPacket = packets.size(); + + if (nPacket == 0) { + WARN() << "Empty packetList!" << std::endl; + return; + } + + /*1: All resources should be freed.*/ + Destroy(); + + /*2: Must initialize queueType first.*/ + InitQueueType(packets.front()->PacketType()); + /*3: Initialize timestamp buf second with the pattern.*/ + m_ts_pattern = tsp; + AllocTimeStampBuf(nPacket); + /*4: Create a event for Wait().*/ + CreateQueueTypeEvent(false, false, node, &m_event); + + int i = -1; + int packetSize = 0; + /* Calculate the space to put all timestamp packet.*/ + int timePacketSize = TimePacketSize() * m_ts_count; + /* Another one page space to put fence, trap, etc*/ + int extraPacketSize = PAGE_SIZE + timePacketSize; + + /* To calculate the total packet size we will need to create the queue. + * As the packet in the vector might be different with each other, + * we have no other way to calculate the queuesize. + */ + for (auto &packet : packets) + packetSize += packet->SizeInBytes(); + + /* queueSize need be power of 2.*/ + const int queueSize = RoundToPowerOf2(packetSize + extraPacketSize); + + /*5: Create a new queue on node for the packets.*/ + CreateNewQueue(node, queueSize); + + if (tsp != NOTS) { + i++; + PlaceTimestampPacket(m_ts + i); + } + + for (auto &packet : packets) { + PlacePacket(*packet); + if (tsp == ALLTS) { + i++; + PlaceTimestampPacket(m_ts + i); + } + } + + if (tsp == HEAD_TAIL) { + i++; + PlaceTimestampPacket(m_ts + i); + } + + ASSERT_EQ(i + 1, m_ts_count); +} + +HSAuint64 AsyncMPSQ::Report(int indexOfPacket, HSAuint64 &begin, HSAuint64 &end) { + /* Should not get any timestamp if NOTS is specified.*/ + int error = 0; + EXPECT_NE(m_ts_pattern, NOTS) + << " Error " << ++error << ": No timestamp would be reported!" << std::endl; + + if (m_ts_pattern == HEAD_TAIL) + indexOfPacket = 0; + + EXPECT_NE((HSAuint64)m_ts, NULL) + << " Error " << ++error << ": No timestamp buf!" << std::endl; + /* m_ts_count is equal to packets count + 1, see PlacePacketOnNode(). + * So the max index of a packet is m_ts_count - 2. + * make it unsigned to defend any minus values. + */ + EXPECT_GE(m_ts_count - 2, (unsigned)indexOfPacket) + << " Error " << ++error << ": Index overflow!" << std::endl; + + if (error) + return 0; + + begin = m_ts[indexOfPacket].timestamp; + end = m_ts[indexOfPacket + 1].timestamp; + return end - begin; +} + +HSAuint64 AsyncMPSQ::Report(int indexOfPacketBegin, int indexOfPacketEnd) { + HSAuint64 ts[4]; + int error = 0; + + if (indexOfPacketEnd == 0) + indexOfPacketEnd = m_ts_count - 1; + + EXPECT_GT((unsigned)indexOfPacketEnd, (unsigned)indexOfPacketBegin) + << " Error " << ++error << ": Index inverted!" << std::endl; + + if (error) + return 0; + /* Get the timestamps around the two packets.*/ + if (!Report(indexOfPacketBegin, ts[0], ts[1])) + return 0; + /* [begin, end)*/ + if (!Report(indexOfPacketEnd - 1, ts[2], ts[3])) + return 0; + + EXPECT_GT(ts[3], ts[0]) + << " Waring: Might be wrong timestamp values!" << std::endl; + + return ts[3] - ts[0]; +} + +/* AsyncMPMQ is short for Async multiple packet multiple queue. + * AsyncMPMQ manages a list of AsyncMPSQ. + * So the packet can be running on multiple GPU nodes at same time. + */ + +class AsyncMPMQ { + public: + AsyncMPMQ(void) { /* do nothing*/} + + virtual ~AsyncMPMQ(void) { /*do nothing*/} + + sharedAsyncMPSQ PlacePacketOnNode(PacketList &packetList, int node, TSPattern tsp = ALLTS) { + /* Create a sharedAsyncMPSQ object and push it into the AsyncMPSQList. + * As we might submit packet to same GPU nodes several times, AsyncMPSQ * + * is returned to stand for the AsyncMPSQ it is created with + */ + sharedAsyncMPSQ mpsq_ptr(new AsyncMPSQ); + mpsq_ptr->PlacePacketOnNode(packetList, node, tsp); + m_mpsqList.push_back(mpsq_ptr); + return mpsq_ptr; + } + + void Submit(void) { + for (auto &mpsq : m_mpsqList) + mpsq->Submit(); + } + + void Wait(void) { + for (auto &mpsq : m_mpsqList) + mpsq->Wait(); + } + + private: + AsyncMPSQList m_mpsqList; +}; + + +/* + * SDMA queue helper functions. + */ + +bool sort_SDMACopyParams(SDMACopyParams &a1, SDMACopyParams &a2) { + if (a1.node != a2.node) + return a1.node < a2.node; + return a1.group < a2.group; +} + +/* + * Copy from src to dst with corresponding sDMA. + * It will try to merge copy on same node into one queue unless + * caller forbid it by setting mashup to 0 and SDMACopyParams::group to different values. + * On condition of mashup is 1, it will re-sort array into mergeable state. + * All mergeable copy will be placed together. + * On condition os mashup is 0, it keeps array in original order. + * It will merge nearby copy if they have same group and node anyway. + */ +void sdma_multicopy(std::vector &array, int mashup, TSPattern tsp) { + int i, packet_index = 0, queue_index = 0; + PacketList packetList; + AsyncMPMQ obj; + std::vector handle; + + /* Sort it and then reduce the amount of queues if caller permits. + * We might change the order of array only here. + */ + if (mashup) + std::sort(array.begin(), array.end(), sort_SDMACopyParams); + + for (i = 0; i < array.size(); i++) { + sharedPacket packet(new + SDMACopyDataPacket(array[i].dst, array[i].src, array[i].size)); + packetList.push_back(packet); + + /* We put the real queue_id in local handle[] to reduce some assignment.*/ + array[i].queue_id = queue_index; + /* Every queue has its packets with the index starts from 0.*/ + array[i].packet_id = packet_index++; + + /* If next copy is on same node and group, try to merge it into same queue.*/ + if (i + 1 < array.size() && array[i].node == array[i + 1].node + && array[i].group == array[i + 1].group) + continue; + + /* Now we have prepare one packetList, place packet into the queue on GPU node.*/ + queue_index++; + handle.push_back(obj.PlacePacketOnNode(packetList, array[i].node, tsp)); + + /* Prepare a new(empty) packetList.*/ + packetList.clear(); + + /* Prepare a new(zero) packet index for the packets in the new queue.*/ + packet_index = 0; + } + + obj.Submit(); + obj.Wait(); + + if (tsp == NOTS) + return; + + /* Get the time used by packet.*/ + for (i = 0; i < array.size(); i++) + array[i].timeConsumption = (handle[array[i].queue_id])->Report( + array[i].packet_id, array[i].timeBegin, array[i].timeEnd); +} + +static +void sdma_multicopy_report(std::vector &array, HSAuint64 countPerGroup, std::stringstream *msg, + HSAuint64 &timeConsumptionMin, HSAuint64 &timeConsumptionMax, + HSAuint64 &totalSizeMin, HSAuint64 &totalSizeMax) { + HSAuint64 begin, end; + /* There can be different count of copies in different groups in the future. + * But assume they are same now. + */ + HSAuint64 group = array.size() / countPerGroup; + HSAuint64 interval = -1; + timeConsumptionMin = -1; + timeConsumptionMax = 0; + totalSizeMin = totalSizeMax = 0; + + /* Try to find out + * 1) The max/min timeConsumption of one copy in all copies. + * 2) The minimal average of timeConsumption of one packet in all copies. + * And one char # or - stands for one interval, aka minimal average. + * Say, one copy use 10ns with 10 copy packets. the other copy use 20ns + * with 10 copy packets. So the interval is 1ns, the timeConsumption is 20ns. + * So the ouput msg will be like + * ########## //copy1 10ns + * #---##----####### //copy2 20ns + */ + for (int i = 0; i < group; i++) { + HSAuint64 begin, end, base = i * countPerGroup; + + begin = array[base].timeBegin; + end = array[base + countPerGroup - 1].timeEnd; + + if (timeConsumptionMax < end - begin) + timeConsumptionMax = end - begin; + + if (timeConsumptionMin > end - begin) + timeConsumptionMin = end - begin; + } + + interval = timeConsumptionMin / countPerGroup; + + /* Draw the timestamp event for each copy list. + * - means still doing copy. + * # means just finish one copy. + */ + if (msg) + for (int i = 0; i < group; i++) { + HSAuint64 base = i * countPerGroup; + HSAuint64 last = array[base].timeBegin; + HSAuint64 timeConsumption; + + *msg << "[" << array[base].node << " : " << array[base].group << "] "; + + for (int j = 0; j < countPerGroup; j++) { + timeConsumption = array[base + j].timeEnd - last; + + while (timeConsumption >= interval) { + timeConsumption -= interval; + last += interval; + + if (timeConsumption >= interval) + *msg << "-"; + else + *msg << "#"; + }; + } + + *msg << std::endl; + } + + /* Try to find out + * 1) The size of all copies in all queues. + * 2) The size of the copies running within the same period in all queues. + * We assume all packets begin to run at same time. + */ + for (int i = 0; i < group; i++) { + HSAuint64 base = i * countPerGroup; + HSAuint64 time = 0; + + for (int j = 0; j < countPerGroup; j++) { + totalSizeMax += array[base + j].size; + + if (time < timeConsumptionMin) { + time += array[base + j].timeConsumption; + totalSizeMin += array[base + j].size; + } + } + } +} + +/* + * Do copy with corresponding sDMA. + */ +void +sdma_multicopy(SDMACopyParams *copyArray, int arrayCount, + HSAuint64 *minSpeed, HSAuint64 *maxSpeed, std::stringstream *msg) { + const HSAuint64 countPerGroup = minSpeed || maxSpeed ? 100 : 1; + std::vector array; + HSAuint64 totalSizeMin, totalSizeMax, timeConsumptionMin, timeConsumptionMax; + + for (int i = 0; i < arrayCount; i++) { + /* Each copy has its own queue.*/ + copyArray[i].group = i; + for (int j = 0; j < countPerGroup; j++) + array.push_back(copyArray[i]); + } + + sdma_multicopy(array, 0, ALLTS); + + sdma_multicopy_report(array, countPerGroup, msg, + timeConsumptionMin, timeConsumptionMax, + totalSizeMin, totalSizeMax); + + if (minSpeed) + *minSpeed = MB_PER_SEC(totalSizeMin, CounterToNanoSec(timeConsumptionMin)); + + if (maxSpeed) + *maxSpeed = MB_PER_SEC(totalSizeMax, CounterToNanoSec(timeConsumptionMax)); +} + +/* + * PM4 queue helper functions. + */ +// TODO diff --git a/tests/kfdtest/src/KFDTestUtilQueue.hpp b/tests/kfdtest/src/KFDTestUtilQueue.hpp new file mode 100644 index 0000000000..dac222b623 --- /dev/null +++ b/tests/kfdtest/src/KFDTestUtilQueue.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 Advanced Micro Devices, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __KFD__TEST__UTIL__QUEUE__H__ +#define __KFD__TEST__UTIL__QUEUE__H__ + +#include "hsakmt.h" +#include + +typedef struct { + HSAuint64 timestamp; + HSAuint64 timeConsumption; + HSAuint64 timeBegin; + HSAuint64 timeEnd; +} TimeStamp; + +/* We have three pattern to put timestamp packet, + * NOTS: No timestamp packet insert. + * ALLTS: Put timestamp packet around every packet. This is the default behavoir. + * It will look like |timestamp|packet|timestamp|...|packet|timestamp| + * HEAD_TAIL: Put timestmap packet at head and tail to measure the overhead of a bunch of packet. + * It will look like |timestamp|packet|...|packet|timestamp| + */ +typedef enum { + NOTS = 0, + ALLTS = 1, + HEAD_TAIL = 2, +} TSPattern; + +typedef struct { + /* input values*/ + HSAuint32 node; + void *src; + void *dst; + HSAuint64 size; + /* input value for internal use.*/ + HSAuint64 group; + /* output value*/ + HSAuint64 timeConsumption; + HSAuint64 timeBegin; + HSAuint64 timeEnd; + /* private: Output values for internal use.*/ + HSAuint64 queue_id; + HSAuint64 packet_id; +} SDMACopyParams; + +void sdma_multicopy(SDMACopyParams *array, int n, + HSAuint64 *speedSmall = 0, HSAuint64 *speedLarge = 0, std::stringstream *s = 0); +void sdma_multicopy(std::vector &array, int mashup = 1, TSPattern tsp = ALLTS); +#endif //__KFD__TEST__UTIL__QUEUE__H__ diff --git a/tests/kfdtest/src/SDMAPacket.cpp b/tests/kfdtest/src/SDMAPacket.cpp index 2dbb449d9f..81ed3ba73d 100644 --- a/tests/kfdtest/src/SDMAPacket.cpp +++ b/tests/kfdtest/src/SDMAPacket.cpp @@ -207,3 +207,24 @@ void SDMATrapPacket::InitPacket(unsigned int eventID) { packetData.HEADER_UNION.op = SDMA_OP_TRAP; packetData.INT_CONTEXT_UNION.int_context = eventID; } + +SDMATimePacket::SDMATimePacket(void *destaddr) { + InitPacket(destaddr); +} + +SDMATimePacket::~SDMATimePacket(void) { +} + +void SDMATimePacket::InitPacket(void *destaddr) { + memset(&packetData, 0, SizeInBytes()); + + packetData.HEADER_UNION.op = SDMA_OP_TIMESTAMP; + packetData.HEADER_UNION.sub_op = 1 << 1; /* Get Global GPU Timestamp*/ + + if (reinterpret_cast(destaddr) & 0x1f) + WARN() << "SDMATimePacket dst address must aligned to 32bytes boundary" << std::endl; + + SplitU64(reinterpret_cast(destaddr), + packetData.ADDR_LO_UNION.DW_1_DATA, /*dst_addr_31_0*/ + packetData.ADDR_HI_UNION.DW_2_DATA); /*dst_addr_63_32*/ +} diff --git a/tests/kfdtest/src/SDMAPacket.hpp b/tests/kfdtest/src/SDMAPacket.hpp index 074c52976e..b1afb374ce 100644 --- a/tests/kfdtest/src/SDMAPacket.hpp +++ b/tests/kfdtest/src/SDMAPacket.hpp @@ -141,4 +141,23 @@ class SDMATrapPacket : public SDMAPacket { SDMA_PKT_TRAP packetData; }; +class SDMATimePacket : public SDMAPacket { + public: + // Empty constructor, before using the packet call the init func + SDMATimePacket(void*); + + virtual ~SDMATimePacket(void); + + // @returns Pointer to the packet + virtual const void *GetPacket() const { return &packetData; } + // @brief Initialise the packet + void InitPacket(void*); + // @returns Packet size in bytes + virtual unsigned int SizeInBytes() const { return sizeof(SDMA_PKT_TIMESTAMP); } + + protected: + SDMA_PKT_TIMESTAMP packetData; +}; + + #endif // __KFD_SDMA_PACKET__H__