linuxOS_D21X/source/artinchip/test-mtop/mtop.c
2025-06-05 14:33:02 +08:00

541 lines
12 KiB
C

// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2023-2025 ArtInChip Technology Co., Ltd.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ENABLE 1
#define DISABLE 0
#define ARRAY_SIZEOF(arr) (sizeof(arr) / sizeof((arr[0])))
struct node_info {
int id;
char *name;
FILE *fp;
unsigned int count;
};
struct node_ctrl {
int id;
char *name;
FILE *fp;
};
enum mtop_ctrl {
MTOP_SWITCH,
MTOP_PERIOD,
};
struct mtop_info {
char *version;
const char **group_name;
struct node_info *node_info_aic;
int node_num;
};
const char *group_name_v10[] = {"cpu", "dma", "de", "gvd", "ahb"};
const char *group_name_v30[] = {"cpu", \
"usb", "gmac", "sdmc0", "sdmc1", "qspi", "usb_h0", "usb_h1", \
"dvp", "de", "dma0", "dma1", "dma2", \
"scan0", "scan1", "prt", \
"jpeg0", "jpeg1", "rot", "ht", "crs0", \
"crs1", "pp0", "pp1", "ehc0", "ehc1", "us", "jbig0", "jbig1", \
"ahb"
};
const char *group_name_v40[] = {"cpu", \
"dma0", "dma1", "ce", "harb0", "harb1", \
"r_cpu", "r_dma", \
"de", \
"dvp", "mipi_csi", \
"dit", "ge", "ve"
};
struct node_info node_info_aic_v10[] = {
{ 1, "cpu_rd", NULL, 0 },
{ 2, "cpu_wr", NULL, 0 },
{ 3, "dma_rd", NULL, 0 },
{ 4, "dma_wr", NULL, 0 },
{ 5, "de_rd", NULL, 0 },
{ 6, "de_wr", NULL, 0 },
{ 7, "gvd_rd", NULL, 0 },
{ 8, "gvd_wr", NULL, 0 },
{ 9, "ahb_rd", NULL, 0 },
{ 10, "ahb_wr", NULL, 0 },
};
struct node_info node_info_aic_v30[] = {
{ 1, "cpu_rd", NULL, 0 },
{ 2, "cpu_wr", NULL, 0 },
{ 3, "usbd_rd", NULL, 0 },
{ 4, "usbd_wr", NULL, 0 },
{ 5, "gmac_rd", NULL, 0 },
{ 6, "gmac_wr", NULL, 0 },
{ 7, "sdmc0_rd", NULL, 0 },
{ 8, "sdmc0_wr", NULL, 0 },
{ 9, "sdmc1_rd", NULL, 0 },
{ 10, "sdmc1_wr", NULL, 0 },
{ 11, "qspi_rd", NULL, 0 },
{ 12, "qspi_wr", NULL, 0 },
{ 13, "usbh0_rd", NULL, 0 },
{ 14, "usbh0_wr", NULL, 0 },
{ 15, "usbh1_rd", NULL, 0 },
{ 16, "usbh1_wr", NULL, 0 },
{ 17, "dvp_rd", NULL, 0 },
{ 18 , "dvp_wr", NULL, 0 },
{ 19, "de_rd", NULL, 0 },
{ 20, "de_wr", NULL, 0 },
{ 21, "dma0_rd", NULL, 0 },
{ 22, "dma0_wr", NULL, 0 },
{ 23, "dma1_rd", NULL, 0 },
{ 24, "dma1_wr", NULL, 0 },
{ 25, "dma2_rd", NULL, 0 },
{ 26, "dma2_wr", NULL, 0 },
{ 27, "scan0_rd", NULL, 0 },
{ 28, "scan0_wr", NULL, 0 },
{ 29, "scan1_rd", NULL, 0 },
{ 30, "scan1_wr", NULL, 0 },
{ 31, "prt_rd", NULL, 0 },
{ 32, "prt_wr", NULL, 0 },
{ 33, "jpeg0_rd", NULL, 0 },
{ 34, "jpeg0_wr", NULL, 0 },
{ 35, "jpeg1_rd", NULL, 0 },
{ 36, "jpeg1_wr", NULL, 0 },
{ 37, "rot_rd", NULL, 0 },
{ 38, "rot_wr", NULL, 0 },
{ 39, "ht_rd", NULL, 0 },
{ 40, "ht_wr", NULL, 0 },
{ 41, "crs0_rd", NULL, 0 },
{ 42, "crs0_wr", NULL, 0 },
{ 43, "crs1_rd", NULL, 0 },
{ 44, "crs1_wr", NULL, 0 },
{ 45, "pp0_rd", NULL, 0 },
{ 46, "pp0_wr", NULL, 0 },
{ 47, "pp1_rd", NULL, 0 },
{ 48, "pp1_wr", NULL, 0 },
{ 49, "ehc0_rd", NULL, 0 },
{ 50, "ehc0_wr", NULL, 0 },
{ 51, "ehc1_rd", NULL, 0 },
{ 52, "ehc1_wr", NULL, 0 },
{ 53, "us_rd", NULL, 0 },
{ 54, "us_wr", NULL, 0 },
{ 55, "jbig0_rd", NULL, 0 },
{ 56, "jbig0_wr", NULL, 0 },
{ 57, "jbig1_rd", NULL, 0 },
{ 58, "jbig1_wr", NULL, 0 },
{ 59, "ahb_rd", NULL, 0 },
{ 60, "ahb_wr", NULL, 0 },
};
struct node_info node_info_aic_v40[] = {
{ 1, "cpu_rd", NULL, 0 },
{ 2, "cpu_wr", NULL, 0 },
{ 3, "dma0_rd", NULL, 0 },
{ 4, "dma0_wr", NULL, 0 },
{ 5, "dma1_rd", NULL, 0 },
{ 6, "dma1_wr", NULL, 0 },
{ 7, "ce_rd", NULL, 0 },
{ 8, "ce_wr", NULL, 0 },
{ 9, "harb0_rd", NULL, 0 },
{ 10, "harb0_wr", NULL, 0 },
{ 11, "harb1_rd", NULL, 0 },
{ 12, "harb1_wr", NULL, 0 },
{ 13, "r_cpu_rd", NULL, 0 },
{ 14, "r_cpu_wr", NULL, 0 },
{ 15, "r_dma_rd", NULL, 0 },
{ 16, "r_dma_wr", NULL, 0 },
{ 17, "de_rd", NULL, 0 },
{ 18, "de_wr", NULL, 0 },
{ 19, "dvp_rd", NULL, 0 },
{ 20, "dvp_wr", NULL, 0 },
{ 21, "mipi_csi_rd", NULL, 0 },
{ 22, "mipi_csi_wr", NULL, 0 },
{ 23, "dit_rd", NULL, 0 },
{ 24, "dit_wr", NULL, 0 },
{ 25, "ge_rd", NULL, 0 },
{ 26, "ge_wr", NULL, 0 },
{ 27, "ve_rd", NULL, 0 },
{ 28, "ve_wr", NULL, 0 },
};
struct node_ctrl node_ctrl_aic[] = {
{ 0, "enable", NULL },
{ 1, "set_period", NULL },
};
static struct mtop_info mtop_data[] = {
{ .version = "0.0",
.node_num = 10,
.group_name = group_name_v10,
.node_info_aic = node_info_aic_v10,},
{ .version = "3.0",
.node_num = 60,
.group_name = group_name_v30,
.node_info_aic = node_info_aic_v30, },
{ .version = "4.0",
.node_num = 28,
.group_name = group_name_v40,
.node_info_aic = node_info_aic_v40, },
};
static struct mtop_info *mtop = NULL;
char mtop_dir[128];
int node_info_cnt;
int delay = 1;
int iter;
char output_fn[256];
FILE *output_fp = NULL;
char path_prefix[128];
unsigned int ddr_bw;
static void usage(char *program)
{
printf("\n");
printf("Usage: %s [-n iter] [-d delay] [-m] [-o FILE] [-h]\n", program);
printf(" -n NUM Number of updates before this program exiting.\n");
printf(" -d NUM Seconds to wait between update.\n");
printf(" -o FILE Output to a file.\n");
printf(" -v Display mtop version.\n");
printf(" -h Display this help screen.\n");
printf("\n");
}
static void version(void)
{
printf("\n");
printf("mtop version: %s\n", mtop->version);
printf("\n");
}
static int get_version_data()
{
int ret = -1;
FILE *file;
char buffer[16] = " ";
char path[64] = "ln -sf /sys/devices/platform/soc/*.mtop/mtop";
system(path);
chdir("/mtop");
getcwd(path, 64);
snprintf(mtop_dir, sizeof(mtop_dir), "%s/%s", path, "version");
file = fopen(mtop_dir, "r");
if (!file) {
fprintf(stderr, "Failed to open %s errno: %d[%s]\n",
mtop_dir, errno, strerror(errno));
return -1;
}
ret = fread(buffer, sizeof(char), 16, file);
if (ret <= 0) {
fprintf(stderr, "fread() return %d, errno: %d[%s]\n",
ret, errno, strerror(errno));
fclose(file);
return -1;
}
fclose(file);
for (int i = 0; i < ARRAY_SIZEOF(mtop_data); i++) {
if (strlen(mtop_data[i].version) != (strlen(buffer) - 1))
continue;
ret = strncmp(mtop_data[i].version, buffer,
strlen(mtop_data[i].version));
if (ret == 0) {
mtop = &mtop_data[i];
node_info_cnt = mtop->node_num;
break;
}
}
if (ret == -1) {
printf("Error! no version !");
return -1;
}
snprintf(mtop_dir, sizeof(mtop_dir), path);
return 0;
}
static int mtop_read(void)
{
int i;
char path[256];
struct node_info *node_info_aic = mtop->node_info_aic;
for (i = 0; i < node_info_cnt; i++) {
snprintf(path, sizeof(path), "%s/%s", path_prefix,
node_info_aic[i].name);
node_info_aic[i].fp = fopen(path, "r");
if (NULL == node_info_aic[i].fp) {
fprintf(stderr, "Could not open file %s: %s\n",
path, strerror(errno));
goto open_error;
}
fscanf(node_info_aic[i].fp, "%u", &node_info_aic[i].count);
fclose(node_info_aic[i].fp);
node_info_aic[i].fp = NULL;
}
return 0;
open_error:
for (i = 0; i < node_info_cnt; i++) {
if (node_info_aic[i].fp) {
fclose(node_info_aic[i].fp);
node_info_aic[i].fp = NULL;
}
}
return -1;
}
static void mtop_update(void)
{
int i;
const char **group_name = mtop->group_name;
struct node_info *node_info_aic = mtop->node_info_aic;
for (i = 0; i < node_info_cnt; i++)
node_info_aic[i].count /= delay;
/* save the cursor position */
printf("\033[s");
/* Clear content from the cursor to the end of line */
printf("\033[K");
fprintf(output_fp, "\n\n%12s %16s %16s %16s %16s\n", "name",
"read", "write", "total", "%percent");
for (i = 0; i < node_info_cnt; i += 2) {
fprintf(output_fp, "%12s %12.2fMB/s %12.2fMB/s %12.2fMB/s %12.2f\n",
group_name[i / 2],
(float)node_info_aic[i].count / 1000000,
(float)node_info_aic[i + 1].count / 1000000,
(float)(node_info_aic[i].count +
node_info_aic[i + 1].count) /1000000,
(float)(node_info_aic[i].count +
node_info_aic[i + 1].count) / 1000000 / ddr_bw * 100);
}
fprintf(output_fp, "\n");
/* restore cursor position */
if (iter != 0)
printf("\033[u");
}
static void mtop_ctrl(int val, enum mtop_ctrl ctrl_type)
{
char path[256];
char buf[8];
switch (ctrl_type) {
case MTOP_SWITCH:
case MTOP_PERIOD:
break;
default:
fprintf(stderr, "ctrl_type is error\n");
return;
}
snprintf(buf, sizeof(buf), "%d", val);
snprintf(path, sizeof(path), "%s/%s", mtop_dir,
node_ctrl_aic[ctrl_type].name);
node_ctrl_aic[ctrl_type].fp = fopen(path, "w");
if (NULL == node_ctrl_aic[ctrl_type].fp) {
fprintf(stderr, "Could not open file %s: %s\n",
path, strerror(errno));
return;
}
fwrite(buf, sizeof(buf[0]), sizeof(buf) / sizeof(buf[0]),
node_ctrl_aic[ctrl_type].fp);
fflush(node_ctrl_aic[ctrl_type].fp);
fclose(node_ctrl_aic[ctrl_type].fp);
node_ctrl_aic[ctrl_type].fp = NULL;
}
static void mtop_switch(int sw)
{
if (sw != 0 && sw != 1) {
printf("Invalid parameter sw: should be 0 or 1\n");
return;
}
mtop_ctrl(sw, MTOP_SWITCH);
}
static void mtop_set_period(int sec)
{
if (sec <= 0 || sec > 100) {
printf("Invalid sec parameter: should be in the"
"range of [1, 100]\n");
return;
}
mtop_ctrl(sec, MTOP_PERIOD);
}
static void sigterm(int signo)
{
int i;
struct node_info *node_info_aic = mtop->node_info_aic;
/* Disable MTOP */
mtop_switch(DISABLE);
/* Default period setting is 1 seconds */
mtop_set_period(1);
/* Cursor move down */
if (!output_fn[0])
printf("\033[9B");
/* Display the cursor */
printf("\033[?25h");
for (i = 0; i < node_info_cnt; i++)
if (node_info_aic[i].fp)
fclose(node_info_aic[i].fp);
if (output_fp)
fclose(output_fp);
exit(0);
}
static int get_ddr_band_width(void)
{
FILE *fp;
system("mount -t debugfs none /sys/kernel/debug/");
fp = fopen("/sys/kernel/debug/clk/ddr/clk_rate", "r");
if (!fp) {
printf("open ddr clock node error\n");
return -1;
}
/* Get DDR controller frequency */
fscanf(fp, "%u", &ddr_bw);
/* Get DDR band width */
ddr_bw = ddr_bw * 8 / 1000000;
fclose(fp);
system("umount -t debufs /sys/kernel/debug/");
return 0;
}
int main(int argc, char *argv[])
{
int opt, ret;
extern int optind, opterr, optopt;
extern char *optarg;
ret = get_version_data();
if (ret) {
printf("Can't get mtop version data\n");
return -1;
}
/* node_info_aic[6] is to set period, so node index is from 1 to 5*/
strncpy(path_prefix, mtop_dir, sizeof(path_prefix));
memset(output_fn, 0, sizeof(output_fn));
output_fp = stdout;
iter = -1;
while ((opt = getopt(argc, argv, "n:d:kmo:hv")) != -1) {
switch (opt) {
case 'n':
if (!optarg) {
usage(argv[0]);
exit(0);
}
iter = strtoul(optarg, (char **)NULL, 10);
break;
case 'd':
if (!optarg) {
usage(argv[0]);
exit(0);
}
delay = strtoul(optarg, (char **)NULL, 10);
break;
case 'o':
if (!optarg) {
usage(argv[0]);
exit(0);
}
strncpy(output_fn, optarg, sizeof(output_fn));
break;
case 'h':
usage(argv[0]);
exit(0);
case 'v':
version();
exit(0);
default:
usage(argv[0]);
exit(-1);
}
}
ret = get_ddr_band_width();
if (ret) {
printf("Can't get DDR bandwidth\n");
return -1;
}
mtop_set_period(delay);
mtop_switch(ENABLE);
signal(SIGTERM, sigterm);
signal(SIGHUP, sigterm);
signal(SIGINT, sigterm);
if (output_fn[0]) {
output_fp = fopen(output_fn, "wt+");
if (stdout == output_fp) {
fprintf(stderr, "Could not open file %s: %s\n",
output_fn, strerror(errno));
exit(-1);
}
}
if (!output_fn[0])
system("clear");
fprintf(output_fp, "mtop version: %s\n", mtop->version);
fprintf(output_fp, "mtop is used to show DDR bandwidth.\n");
fprintf(output_fp, "DDR total band width: %uMB/s\n", ddr_bw);
if (output_fn[0])
fprintf(output_fp, "output: %s\n", output_fn);
/* Hide the cursor */
printf("\033[?25l");
while (iter == -1 || iter-- > 0) {
mtop_read();
mtop_update();
sleep(delay);
}
mtop_switch(DISABLE);
mtop_set_period(1);
/* Display the cursor */
printf("\033[?25h");
if (output_fp)
fclose(output_fp);
return 0;
}