diff options
-rw-r--r-- | native/etnaviv/viv.c | 20 | ||||
-rw-r--r-- | native/etnaviv/viv.h | 12 | ||||
-rw-r--r-- | native/utils/viv_gpu_top.c | 254 |
3 files changed, 253 insertions, 33 deletions
diff --git a/native/etnaviv/viv.c b/native/etnaviv/viv.c index 9b89987..76afb8c 100644 --- a/native/etnaviv/viv.c +++ b/native/etnaviv/viv.c @@ -542,3 +542,23 @@ int viv_unmap_user_memory(struct viv_conn *conn, void *memory, size_t size, viv_ return viv_invoke(conn, &id); } +int viv_read_register(struct viv_conn *conn, uint32_t address, uint32_t *data) +{ + gcsHAL_INTERFACE id; + int rv; + id.command = gcvHAL_READ_REGISTER; + id.u.ReadRegisterData.address = address; + rv = viv_invoke(conn, &id); + *data = id.u.ReadRegisterData.data; + return rv; +} + +int viv_write_register(struct viv_conn *conn, uint32_t address, uint32_t data) +{ + gcsHAL_INTERFACE id; + id.command = gcvHAL_WRITE_REGISTER; + id.u.WriteRegisterData.address = address; + id.u.WriteRegisterData.data = data; + return viv_invoke(conn, &id); +} + diff --git a/native/etnaviv/viv.h b/native/etnaviv/viv.h index 74c7b37..4d0ebd4 100644 --- a/native/etnaviv/viv.h +++ b/native/etnaviv/viv.h @@ -277,6 +277,18 @@ void viv_show_chip_info(struct viv_conn *conn); */ int viv_reset(struct viv_conn *conn); +/** Read register from GPU. + * @note Needs kernel module compiled with user space register access + * (gcdREGISTER_ACCESS_FROM_USER=1) + */ +int viv_read_register(struct viv_conn *conn, uint32_t address, uint32_t *data); + +/** Write register to GPU. + * @note Needs kernel module compiled with user space register access + * (gcdREGISTER_ACCESS_FROM_USER=1) + */ +int viv_write_register(struct viv_conn *conn, uint32_t address, uint32_t data); + /** Convenience macro to probe features from state.xml.h: * VIV_FEATURE(chipFeatures, FAST_CLEAR) * VIV_FEATURE(chipMinorFeatures1, AUTO_DISABLE) diff --git a/native/utils/viv_gpu_top.c b/native/utils/viv_gpu_top.c index f62df7f..7dbb3e1 100644 --- a/native/utils/viv_gpu_top.c +++ b/native/utils/viv_gpu_top.c @@ -26,6 +26,8 @@ #include <etnaviv/viv.h> #include <etnaviv/viv_profile.h> +#include <etnaviv/state_hi.xml.h> +#include <etnaviv/state.xml.h> static const char *bars[] = { " ", @@ -47,15 +49,18 @@ static const char color_num_max[] = "\x1b[1;37m"; static const char color_num_zero[] = "\x1b[1;30m"; static const char color_num[] = "\x1b[1;33m"; static const char color_reset[] = "\x1b[0m"; +static const char color_percentage_1[] = "\x1b[38;5;154;48;5;236m"; +static const char color_percentage_2[] = "\x1b[38;5;112;48;5;236m"; +static const char color_title[] = "\x1b[38;5;249m"; -#define STATS_LEN (20) -#define PERCENTAGE_BAR_END (79 - STATS_LEN) - -static void print_percentage_bar(float percent, int cur_line_len) +static void print_percentage_bar(float percent, int bar_width) { - int bar_avail_len = (PERCENTAGE_BAR_END - cur_line_len - 1) * 8; + int bar_avail_len = bar_width * 8; int bar_len = bar_avail_len * (percent + .5) / 100.0; + int cur_line_len = 0; int i; + if(bar_len > bar_avail_len) + bar_len = bar_avail_len; for (i = bar_len; i >= 8; i -= 8) { printf("%s", bars[8]); @@ -68,7 +73,8 @@ static void print_percentage_bar(float percent, int cur_line_len) /* NB: We can't use a field width with utf8 so we manually * guarantee a field with of 45 chars for any bar. */ - printf("%*s", PERCENTAGE_BAR_END - cur_line_len, ""); + + printf("%*s", bar_width - cur_line_len, ""); } /* Get time in microseconds */ @@ -132,23 +138,13 @@ struct counter_rec uint64_t events_per_s; }; -/* Sort counters descending */ -static int counter_rec_compar(const void *a, const void *b) -{ - uint64_t ca = ((const struct counter_rec*)a)->events_per_s; - uint64_t cb = ((const struct counter_rec*)b)->events_per_s; - if(ca < cb) - return 1; - else if(cb > ca) - return -1; - else return 0; -} - enum display_mode { MODE_ALL = 0, MODE_MAX = 1, - MODE_SORTED = 2 + MODE_SORTED = 2, + MODE_OCCUPANCY = 3, + MODE_DMA = 4 }; /* derived counters (derived information computed from existing counters) */ @@ -160,6 +156,71 @@ static struct viv_profile_counter_info derived_counter_info[] = { [0] = {"TOTAL_INST_COUNTER", "Total inst counter"}, }; +static struct { + const char *name; + uint32_t bit; + bool inv; /* show inverted value */ +} idle_module_names[] = { + {"FE", VIVS_HI_IDLE_STATE_FE, true}, + {"DE", VIVS_HI_IDLE_STATE_DE, true}, + {"PE", VIVS_HI_IDLE_STATE_PE, true}, + {"SH", VIVS_HI_IDLE_STATE_SH, true}, + {"PA", VIVS_HI_IDLE_STATE_PA, true}, + {"SE", VIVS_HI_IDLE_STATE_SE, true}, + {"RA", VIVS_HI_IDLE_STATE_RA, true}, + {"TX", VIVS_HI_IDLE_STATE_TX, true}, + {"VG", VIVS_HI_IDLE_STATE_VG, true}, + {"IM", VIVS_HI_IDLE_STATE_IM, true}, + {"FP", VIVS_HI_IDLE_STATE_FP, true}, + {"TS", VIVS_HI_IDLE_STATE_TS, true}, + {"AXI_LP", VIVS_HI_IDLE_STATE_AXI_LP, false} +}; +#define NUM_IDLE_MODULES (sizeof(idle_module_names)/sizeof(idle_module_names[0])) + +static const char* cmd_state_names[]={ +"IDLE", "DEC", "ADR0", "LOAD0", "ADR1", "LOAD1", "3DADR", "3DCMD", +"3DCNTL", "3DIDXCNTL", "INITREQDMA", "DRAWIDX", "DRAW", "2DRECT0", "2DRECT1", "2DDATA0", +"2DDATA1", "WAITFIFO", "WAIT", "LINK", "END", "STALL", "UNKNOWN" +}; +#define NUM_CMD_STATE_NAMES (sizeof(cmd_state_names)/sizeof(cmd_state_names[0])) + +static const char* cmd_dma_state_names[]={ + "IDLE", "START", "REQ", "END" +}; +#define NUM_CMD_DMA_STATE_NAMES (sizeof(cmd_dma_state_names)/sizeof(cmd_dma_state_names[0])) + +static const char* cmd_fetch_state_names[]={ + "IDLE", "RAMVALID", "VALID", "UNKNOWN" +}; +#define NUM_CMD_FETCH_STATE_NAMES (sizeof(cmd_fetch_state_names)/sizeof(cmd_fetch_state_names[0])) + +static const char* req_dma_state_names[]={ + "IDLE", "START", "REQ", "END" +}; +#define NUM_REQ_DMA_STATE_NAMES (sizeof(req_dma_state_names)/sizeof(req_dma_state_names[0])) + +static const char* cal_state_names[]={ + "IDLE", "LDADR", "IDXCALC", "UNKNOWN" +}; +#define NUM_CAL_STATE_NAMES (sizeof(cal_state_names)/sizeof(cal_state_names[0])) + +static const char* ve_req_state_names[]={ + "IDLE", "CKCACHE", "MISS", "UNKNOWN" +}; +#define NUM_VE_REQ_STATE_NAMES (sizeof(ve_req_state_names)/sizeof(ve_req_state_names[0])) + +/* Sort counters descending */ +static int counter_rec_compar(const void *a, const void *b) +{ + uint64_t ca = ((const struct counter_rec*)a)->events_per_s; + uint64_t cb = ((const struct counter_rec*)b)->events_per_s; + if(ca < cb) + return 1; + else if(cb > ca) + return -1; + else return 0; +} + static struct viv_profile_counter_info *get_counter_info(uint32_t idx) { if(idx < derived_counters_base) @@ -168,6 +229,26 @@ static struct viv_profile_counter_info *get_counter_info(uint32_t idx) return &derived_counter_info[idx - derived_counters_base]; } +static void print_percentage_row(int l, double percent, const char *name, int name_width, bool color, int bar_width) +{ + printf("%-*s ", name_width, name); + if(color) + { + if(percent > 99.0) + printf("%s", color_num_max); + else if(percent < 1.0) + printf("%s", color_num_zero); + else + printf("%s", color_num); + } + printf("%5.1f%% ", percent); + if(color) + printf("%s%s", color_reset, (l%2) ? color_percentage_1 : color_percentage_2); + print_percentage_bar(percent, bar_width); + if(color) + printf("%s", color_reset); +} + int main(int argc, char **argv) { struct viv_conn *conn = 0; @@ -197,6 +278,8 @@ int main(int argc, char **argv) case 'm': mode = MODE_MAX; break; case 'a': mode = MODE_ALL; break; case 's': mode = MODE_SORTED; break; + case 'o': mode = MODE_OCCUPANCY; break; + case 'd': mode = MODE_DMA; break; default: printf("Unknown mode %s\n", optarg); } @@ -230,28 +313,59 @@ int main(int argc, char **argv) events_per_s[c] = 0; } + uint32_t idle_states[NUM_IDLE_MODULES] = {}; + uint32_t cmd_state[32] = {}; + uint32_t cmd_dma_state[4] = {}; + uint32_t cmd_fetch_state[4] = {}; + uint32_t req_dma_state[4] = {}; + uint32_t cal_state[4] = {}; + uint32_t ve_req_state[4] = {}; for(int sample=0; sample<samples_per_second; ++sample) { - if(viv_read_profile_counters_3d(conn, counter_data) != 0) + if(mode == MODE_OCCUPANCY) { - fprintf(stderr, "Error querying counters (probably unsupported with this kernel, or not built into libetnaviv)\n"); - exit(1); - } - for(int c=0; c<num_profile_counters; ++c) + uint32_t data = 0; + viv_read_register(conn, VIVS_HI_IDLE_STATE, &data); + for(int mid=0; mid<NUM_IDLE_MODULES; ++mid) + { + if(data & idle_module_names[mid].bit) + idle_states[mid]++; + } + } else if(mode == MODE_DMA) { - if(!reset_after_read[c]) + uint32_t data = 0; + viv_read_register(conn, VIVS_FE_DMA_DEBUG_STATE, &data); + int cmd_state_idx = data & 0x1F; + if(cmd_state_idx >= (NUM_CMD_STATE_NAMES-1)) /* Mark unknowns as UNKNOWN */ + cmd_state_idx = NUM_CMD_STATE_NAMES-1; + cmd_state[cmd_state_idx]++; + cmd_dma_state[(data>>8) & 3]++; + cmd_fetch_state[(data>>10) & 3]++; + req_dma_state[(data>>12) & 3]++; + cal_state[(data>>14) & 3]++; + ve_req_state[(data>>16) & 3]++; + } else { + if(viv_read_profile_counters_3d(conn, counter_data) != 0) { - if(counter_data_last[c] > counter_data[c]) + fprintf(stderr, "Error querying counters (probably unsupported with this kernel, or not built into libetnaviv)\n"); + exit(1); + } + for(int c=0; c<num_profile_counters; ++c) + { + if(!reset_after_read[c]) { + if(counter_data_last[c] > counter_data[c]) + { + events_per_s[c] += counter_data[c]; + } else { + events_per_s[c] += (uint32_t)(counter_data[c] - counter_data_last[c]); + } + } else events_per_s[c] += counter_data[c]; - } else { - events_per_s[c] += (uint32_t)(counter_data[c] - counter_data_last[c]); - } - } else - events_per_s[c] += counter_data[c]; + } + for(int c=0; c<num_profile_counters; ++c) + counter_data_last[c] = counter_data[c]; } - for(int c=0; c<num_profile_counters; ++c) - counter_data_last[c] = counter_data[c]; usleep(interval); } @@ -349,6 +463,80 @@ int main(int argc, char **argv) } printf("\n"); } + } else if(mode == MODE_OCCUPANCY) + { + int lines = NUM_IDLE_MODULES; + if(lines > max_lines) + lines = max_lines; + + if(color) + printf("%s", color_title); + printf("Module occupancy\n"); + if(color) + printf("%s", color_reset); + for(int l=0; l<lines; ++l) + { + double percent = 100.0 * (double)idle_states[l] / (double)samples_per_second; + if(idle_module_names[l].inv) + percent = 100.0 - percent; + print_percentage_row(l, percent, idle_module_names[l].name, 6, color, 40); + printf("\n"); + } + } else if(mode == MODE_DMA) + { + const struct dma_table { + int column; + int base_y; + const char *title; + int data_size; + const char **data_names; + uint32_t *data; + } dma_tables[] = { + {0, 1, "Command state", NUM_CMD_STATE_NAMES, cmd_state_names, cmd_state}, + {1, 1, "Command DMA state", NUM_CMD_DMA_STATE_NAMES, cmd_dma_state_names, cmd_dma_state}, + {1, 7, "Command fetch state", NUM_CMD_FETCH_STATE_NAMES, cmd_fetch_state_names, cmd_fetch_state}, + {1, 13, "DMA request state", NUM_REQ_DMA_STATE_NAMES, req_dma_state_names, req_dma_state}, + {1, 19, "Cal state", NUM_CAL_STATE_NAMES, cal_state_names, cal_state}, + {1, 25, "VE req state", NUM_VE_REQ_STATE_NAMES, ve_req_state_names, ve_req_state} + }; +#define NUM_DMA_TABLES (sizeof(dma_tables) / sizeof(dma_tables[0])) + for(int l=0; l<max_lines; ++l) + { + for(int column=0; column<2; ++column) + { + bool match = false; + for(int t=0; t<NUM_DMA_TABLES; ++t) /* find table that this column,row slot is part of, if any */ + { + const struct dma_table *table = &dma_tables[t]; + if(table->column == column) + { + if(l == table->base_y-1) + { + if(color) + printf("%s", color_title); + printf("%-59s", table->title); + if(color) + printf("%s", color_reset); + match = true; + break; + } else if(l >= table->base_y && l < (table->base_y + table->data_size)) + { + int y = l - table->base_y; + double percent = 100.0 * (double)table->data[y] / (double)samples_per_second; + print_percentage_row(y, percent, table->data_names[y], 10, color, 40); + match = true; + break; + } + } + } + if(!match) /* empty slot */ + { + printf("%-59s", ""); + } + printf(" "); + } + printf("\n"); + } } } begin_time = end_time; |