diff --git a/Makefile b/Makefile index 6a9d913..66e9adb 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,11 @@ ifneq ($(OS),Windows_NT) SOURCE += $(SRC_DIR)sysctl.c HEADERS += $(SRC_DIR)sysctl.h endif + else ifeq ($(arch), $(filter $(arch), riscv64 riscv32)) + SRC_DIR=src/riscv/ + SOURCE += $(COMMON_SRC) $(SRC_DIR)riscv.c $(SRC_DIR)uarch.c $(SRC_DIR)soc.c $(SRC_DIR)udev.c + HEADERS += $(COMMON_SRC) $(SRC_DIR)riscv.h $(SRC_DIR)uarch.h $(SRC_DIR)soc.h $(SRC_DIR)udev.h $(SRC_DIR)socs.h + CFLAGS += -DARCH_RISCV -Wno-unused-parameter -std=c99 -fstack-protector-all else # Error lines should not be tabulated because Makefile complains about it $(warning Unsupported arch detected: $(arch). See https://github.com/Dr-Noob/cpufetch#1-support) diff --git a/src/common/args.c b/src/common/args.c index 22d8ca7..b3d2b10 100644 --- a/src/common/args.c +++ b/src/common/args.c @@ -13,6 +13,7 @@ #define COLOR_STR_IBM "ibm" #define COLOR_STR_ARM "arm" #define COLOR_STR_ROCKCHIP "rockchip" +#define COLOR_STR_SIFIVE "sifive" static const char *SYTLES_STR_LIST[] = { [STYLE_EMPTY] = NULL, @@ -170,6 +171,7 @@ bool parse_color(char* optarg_str, struct color*** cs) { else if(strcmp(optarg_str, COLOR_STR_IBM) == 0) color_to_copy = COLOR_DEFAULT_IBM; else if(strcmp(optarg_str, COLOR_STR_ARM) == 0) color_to_copy = COLOR_DEFAULT_ARM; else if(strcmp(optarg_str, COLOR_STR_ROCKCHIP) == 0) color_to_copy = COLOR_DEFAULT_ROCKCHIP; + else if(strcmp(optarg_str, COLOR_STR_SIFIVE) == 0) color_to_copy = COLOR_DEFAULT_SIFIVE; else { str_to_parse = optarg_str; free_ptr = false; diff --git a/src/common/ascii.h b/src/common/ascii.h index 1c8e96e..2dd9fbc 100644 --- a/src/common/ascii.h +++ b/src/common/ascii.h @@ -246,6 +246,114 @@ $C1##. ###. ####. #### ### .## #### ### ### #.##### \ $C1 ## \ $C1 " +#define ASCII_RISCV \ +"$C1 \ +$C1 ************ \ +$C1 %%%%%%%%% *********** \ +$C1 %%%%%%%%%% ********** \ +$C1 %%%%%%%%% ********* \ +$C1 % ******** \ +$C1 %% .********* % \ +$C1 %%%% ******* %%% \ +$C1 %%%%%%. **** %%%%% \ +$C1 %%%%%%%%. %%%%%%% \ +$C1 \ +$C1 \ +$C1 ########### ## .######### ######### .## ## \ +$C1 ## ## ## ## ## ### ### \ +$C1 ########### ## ##########. ## #### .## ## \ +$C1 ## ### ## ##. ## ### ### \ +$C1 ## ### ## ##########. ########## ### \ +$C1 " + +#define ASCII_SIFIVE \ +"$C1 ############################################## \ +$C1 ###########@@@@@@@@@@@@@@@@@@@@@@@@########### \ +$C1 #########@@@@@@@@@@@@@@@@@@@@@@@@@@@@######### \ +$C1 ########@@@@######################@@@@######## \ +$C1 #######@@@@########################@@@@####### \ +$C1 ######@@@@##########################@@@@###### \ +$C1 #####@@@@#######@@@@@@@@@@@@@@@@@@@@@@@@@##### \ +$C1 ####@@@@#######@@@@@@@@@@@@@@@@@@@@@@@@@@@#### \ +$C1 ###@@@@################################@@@@### \ +$C1 ##@@@@##################################@@@@## \ +$C1 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#########@@@@# \ +$C1 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@###########@@@@# \ +$C1 ##@@@@@@############@@@@@@############@@@@@@## \ +$C1 ######@@@@@###########@@###########@@@@@###### \ +$C1 #########@@@@@@################@@@@@@######### \ +$C1 #############@@@@@##########@@@@@############# \ +$C1 ################@@@@@@##@@@@@@################ \ +$C1 ####################@@@@@@#################### \ +$C1 ############################################## " + +#define ASCII_SIFIVE_L \ +"$C1 ################################################### \ +$C1 ###########@@@@@@@@@@@@@@@@@@@@@@@@@@@@############ \ +$C1 ##########@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@########### \ +$C1 #########@@@@@#######################@@@@########## \ +$C1 ########@@@@@#########################@@@@######### \ +$C1 #######@@@@@###########################@@@@######## \ +$C1 ######@@@@@########@@@@@@@@@@@@@@@@@@@@@@@@@####### \ +$C1 #####@@@@@########@@@@@@@@@@@@@@@@@@@@@@@@@@@###### \ +$C1 ####@@@@@################################@@@@@##### \ +$C1 ###@@@@@##################################@@@@@#### \ +$C1 ##@@@@@####################################@@@@@### \ +$C1 ##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#########@@@@### \ +$C1 ##@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@############@@@@### \ +$C1 ####@@@@@#############@@@@@@@############@@@@@##### \ +$C1 #######@@@@@@############@############@@@@@@####### \ +$C1 ##########@@@@@@###################@@@@@@########## \ +$C1 ##############@@@@@@###########@@@@@@############## \ +$C1 #################@@@@@@#####@@@@@@################# \ +$C1 ####################@@@@@@@@@@@#################### \ +$C1 ########################@@@######################## \ +$C1 ################################################### " + +#define ASCII_STARFIVE \ +"$C1 # \ +$C1 ########## \ +$C1 ######## ######## \ +$C1 ######## #### \ +$C1 ####### #### \ +$C1 #### ####### \ +$C1 #### ##### ####### \ +$C1 ####### ######## .## \ +$C1 ######## ######## \ +$C1 ### ########. ####### \ +$C1 ####### ##### #### \ +$C1 ######## #. #### \ +$C1 # #### ####### \ +$C1 ##### ####### \ +$C1 ######## ######## \ +$C1 ######### \ +$C1 # " + +#define ASCII_STARFIVE_L \ +"$C1 ####### \ +$C1 ################. \ +$C1 ############ ########### \ +$C1 ############ ##########. \ +$C1 ############ # ###### \ +$C1 ########### ##### ## \ +$C1 #######. ########## \ +$C1 ###### ### *########### \ +$C1 ###### #######. ########## \ +$C1 ######### ############ ###### \ +$C1 ###########. ###########* # \ +$C1 ############ ############ \ +$C1 # ############. .########### \ +$C1 ###### ########### ######### \ +$C1 ########## .######, ##### \ +$C1 ############ ##. #####. \ +$C1 ######### ######## \ +$C1 ## ##### ##########. \ +$C1 ####### # ############ \ +$C1 ########### ###########. \ +$C1 ###########. ############ \ +$C1 ################ \ +$C1 ####### " + // --------------------- LONG LOGOS ------------------------- // #define ASCII_AMD_L \ "$C1 \ @@ -348,6 +456,9 @@ asciiL logo_ibm = { ASCII_IBM, 42, 9, false, {C_FG_CYAN, C_FG_W asciiL logo_apple = { ASCII_APPLE, 32, 17, false, {C_FG_WHITE}, {C_FG_CYAN, C_FG_B_WHITE} }; asciiL logo_allwinner = { ASCII_ALLWINNER, 47, 16, false, {C_FG_CYAN}, {C_FG_B_BLACK, C_FG_B_CYAN } }; asciiL logo_rockchip = { ASCII_ROCKCHIP, 58, 8, false, {C_FG_CYAN, C_FG_YELLOW}, {C_FG_CYAN, C_FG_YELLOW} }; +asciiL logo_riscv = { ASCII_RISCV, 63, 18, false, {C_FG_CYAN, C_FG_YELLOW}, {C_FG_CYAN, C_FG_YELLOW} }; +asciiL logo_sifive = { ASCII_SIFIVE, 48, 19, true, {C_BG_WHITE, C_BG_BLACK}, {C_FG_WHITE, C_FG_BLUE} }; +asciiL logo_starfive = { ASCII_STARFIVE, 33, 17, false, {C_FG_WHITE}, {C_FG_WHITE, C_FG_BLUE} }; // Long variants | ----------------------------------------------------------------------------------------------------| asciiL logo_amd_l = { ASCII_AMD_L, 62, 19, true, {C_BG_WHITE, C_BG_GREEN}, {C_FG_WHITE, C_FG_GREEN} }; @@ -355,6 +466,8 @@ asciiL logo_intel_l = { ASCII_INTEL_L, 62, 19, true, {C_BG_CYAN, C_BG_W asciiL logo_intel_l_new = { ASCII_INTEL_L_NEW, 57, 14, true, {C_BG_CYAN, C_BG_WHITE, C_BG_BLUE}, {C_FG_CYAN, C_FG_WHITE} }; asciiL logo_arm_l = { ASCII_ARM_L, 60, 8, true, {C_BG_CYAN}, {C_FG_WHITE, C_FG_CYAN} }; asciiL logo_ibm_l = { ASCII_IBM_L, 62, 13, true, {C_BG_CYAN, C_FG_WHITE}, {C_FG_CYAN, C_FG_WHITE} }; +asciiL logo_starfive_l = { ASCII_STARFIVE_L, 50, 22, false, {C_FG_WHITE}, {C_FG_WHITE, C_FG_BLUE} }; +asciiL logo_sifive_l = { ASCII_SIFIVE_L, 53, 21, true, {C_BG_WHITE, C_BG_BLACK}, {C_FG_WHITE, C_FG_CYAN} }; asciiL logo_unknown = { NULL, 0, 0, false, {COLOR_NONE}, {COLOR_NONE, COLOR_NONE} }; #endif diff --git a/src/common/cpu.c b/src/common/cpu.c index 2faa4bb..1987420 100644 --- a/src/common/cpu.c +++ b/src/common/cpu.c @@ -14,6 +14,8 @@ #include "../ppc/uarch.h" #elif ARCH_ARM #include "../arm/uarch.h" +#elif ARCH_RISCV + #include "../riscv/uarch.h" #endif #define STRING_YES "Yes" diff --git a/src/common/cpu.h b/src/common/cpu.h index ed08fc7..449fbe0 100644 --- a/src/common/cpu.h +++ b/src/common/cpu.h @@ -20,6 +20,10 @@ enum { CPU_VENDOR_SAMSUNG, CPU_VENDOR_MARVELL, CPU_VENDOR_PHYTIUM, +// ARCH_RISCV + CPU_VENDOR_RISCV, + CPU_VENDOR_SIFIVE, + CPU_VENDOR_THEAD, // OTHERS CPU_VENDOR_UNKNOWN, CPU_VENDOR_INVALID @@ -116,16 +120,28 @@ struct features { #endif }; +struct extensions { + char* str; + uint32_t mask; +}; + struct cpuInfo { - VENDOR cpu_vendor; + VENDOR cpu_vendor; struct uarch* arch; struct hypervisor* hv; struct frequency* freq; struct cache* cach; struct topology* topo; - struct features* feat; int64_t peak_performance; + // Similar but not exactly equal + // to struct features +#ifdef ARCH_RISCV + struct extensions* ext; +#else + struct features* feat; +#endif + #if defined(ARCH_X86) || defined(ARCH_PPC) // CPU name from model char* cpu_name; @@ -149,7 +165,7 @@ struct cpuInfo { uint32_t midr; #endif -#ifdef ARCH_ARM +#if defined(ARCH_ARM) || defined(ARCH_RISCV) struct system_on_chip* soc; #endif diff --git a/src/common/global.c b/src/common/global.c index 2ed88ba..e62e695 100644 --- a/src/common/global.c +++ b/src/common/global.c @@ -29,6 +29,9 @@ #elif ARCH_ARM static const char* ARCH_STR = "ARM build"; #include "../arm/midr.h" +#elif ARCH_RISCV + static const char* ARCH_STR = "RISC-V build"; + #include "../riscv/riscv.h" #endif #ifdef __linux__ diff --git a/src/common/main.c b/src/common/main.c index 6f2900f..6371e9b 100644 --- a/src/common/main.c +++ b/src/common/main.c @@ -44,6 +44,7 @@ void print_help(char *argv[]) { printf(" * \"amd\": Use AMD default color scheme \n"); printf(" * \"ibm\", Use IBM default color scheme \n"); printf(" * \"arm\": Use ARM default color scheme \n"); + printf(" * \"sifive\": Use SiFive default color scheme \n"); printf(" * custom: If the argument of --color does not match any of the previous strings, a custom scheme can be specified.\n"); printf(" 5 colors must be given in RGB with the format: R,G,B:R,G,B:...\n"); printf(" The first 3 colors are the CPU art color and the next 2 colors are the text colors\n"); diff --git a/src/common/printer.c b/src/common/printer.c index cb0b4a8..9216fb7 100644 --- a/src/common/printer.c +++ b/src/common/printer.c @@ -16,10 +16,14 @@ #elif ARCH_PPC #include "../ppc/uarch.h" #include "../ppc/ppc.h" -#else +#elif ARCH_ARM #include "../arm/uarch.h" #include "../arm/midr.h" #include "../arm/soc.h" +#elif ARCH_RISCV + #include "../riscv/riscv.h" + #include "../riscv/uarch.h" + #include "../riscv/soc.h" #endif #ifdef _WIN32 @@ -42,7 +46,7 @@ enum { #if defined(ARCH_X86) || defined(ARCH_PPC) ATTRIBUTE_NAME, -#elif ARCH_ARM +#elif defined(ARCH_ARM) || defined(ARCH_RISCV) ATTRIBUTE_SOC, #endif #if defined(ARCH_X86) || defined(ARCH_ARM) @@ -62,6 +66,8 @@ enum { ATTRIBUTE_ALTIVEC, #elif ARCH_ARM ATTRIBUTE_FEATURES, +#elif ARCH_RISCV + ATTRIBUTE_EXTENSIONS, #endif ATTRIBUTE_L1i, ATTRIBUTE_L1d, @@ -75,7 +81,7 @@ static const char* ATTRIBUTE_FIELDS [] = { "Name:", #elif ARCH_PPC "Part Number:", -#elif ARCH_ARM +#elif defined(ARCH_ARM) || defined(ARCH_RISCV) "SoC:", #endif #if defined(ARCH_X86) || defined(ARCH_ARM) @@ -95,6 +101,8 @@ static const char* ATTRIBUTE_FIELDS [] = { "Altivec: ", #elif defined(ARCH_ARM) "Features: ", +#elif defined(ARCH_RISCV) + "Extensions: ", #endif "L1i Size:", "L1d Size:", @@ -128,6 +136,8 @@ static const char* ATTRIBUTE_FIELDS_SHORT [] = { "Altivec: ", #elif defined(ARCH_ARM) "Features: ", +#elif defined(ARCH_RISCV) + "Extensions: ", #endif "L1i Size:", "L1d Size:", @@ -364,6 +374,15 @@ void choose_ascii_art(struct ascii* art, struct color** cs, struct terminal* ter else { art->art = choose_ascii_art_aux(&logo_arm_l, &logo_arm, term, lf); } +#elif ARCH_RISCV + if(art->vendor == SOC_VENDOR_SIFIVE) + art->art = choose_ascii_art_aux(&logo_sifive_l, &logo_sifive, term, lf); + else if(art->vendor == SOC_VENDOR_STARFIVE) + art->art = choose_ascii_art_aux(&logo_starfive_l, &logo_starfive, term, lf); + else if(art->vendor == SOC_VENDOR_ALLWINNER) + art->art = &logo_allwinner; + else + art->art = &logo_riscv; #endif // 2. Choose colors @@ -911,6 +930,154 @@ bool print_cpufetch_arm(struct cpuInfo* cpu, STYLE s, struct color** cs, struct } #endif +#ifdef ARCH_RISCV +// https://stackoverflow.com/questions/109023/count-the-number-of-set-bits-in-a-32-bit-integer +int number_of_bits(uint32_t i) { + i = i - ((i >> 1) & 0x55555555); // add pairs of bits + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); // quads + i = (i + (i >> 4)) & 0x0F0F0F0F; // groups of 8 + + return (i * 0x01010101) >> 24; // horizontal sum of bytes +} + +void print_ascii_riscv(struct ascii* art, uint32_t la, int32_t termw, const char** attribute_fields, uint32_t extensions_mask) { + struct ascii_logo* logo = art->art; + int attr_to_print = 0; + int attr_type; + char* attr_value; + int32_t beg_space; + int32_t space_right; + int32_t ext_list_size = sizeof(extension_list)/sizeof(extension_list[0]); + int32_t ext_num = 0; + int32_t ext_to_print = 0; + int32_t num_extensions = number_of_bits(extensions_mask); + int32_t space_up = ((int)logo->height - (int)(art->n_attributes_set + num_extensions))/2; + int32_t space_down = (int)logo->height - (int)(art->n_attributes_set + num_extensions) - (int)space_up; + uint32_t logo_pos = 0; + int32_t iters = max(logo->height, art->n_attributes_set); + + struct line_buffer* lbuf = emalloc(sizeof(struct line_buffer)); + lbuf->buf = emalloc(sizeof(char) * LINE_BUFFER_SIZE); + lbuf->pos = 0; + lbuf->chars = 0; + + printf("\n"); + for(int32_t n=0; n < iters; n++) { + // 1. Print logo + if(space_up > 0 || (space_up + n >= 0 && space_up + n < (int)logo->height)) { + for(uint32_t i=0; i < logo->width; i++) { + if(logo->art[logo_pos] == '$') { + if(logo->replace_blocks) logo_pos += 3; + else parse_print_color(art, lbuf, &logo_pos); + } + if(logo->replace_blocks && logo->art[logo_pos] != ' ') { + if(logo->art[logo_pos] == '#') printOut(lbuf, 1, "%s%c%s", logo->color_ascii[0], ' ', art->reset); + else if(logo->art[logo_pos] == '@') printOut(lbuf, 1, "%s%c%s", logo->color_ascii[1], ' ', art->reset); + else if(logo->art[logo_pos] == '%') printOut(lbuf, 1, "%s%c%s", logo->color_ascii[2], ' ', art->reset); + else printOut(lbuf, 1, "%c", logo->art[logo_pos]); + } + else + printOut(lbuf, 1, "%c", logo->art[logo_pos]); + + logo_pos++; + } + printOut(lbuf, 0, "%s", art->reset); + } + else { + // If logo should not be printed, fill with spaces + printOut(lbuf, logo->width, "%*c", logo->width, ' '); + } + + // 2. Print text + if(space_up < 0 || (n > space_up-1 && n < (int)logo->height - space_down)) { + attr_type = art->attributes[attr_to_print]->type; + attr_value = art->attributes[attr_to_print]->value; + + // Print extension + if(attr_to_print > 0 && art->attributes[attr_to_print-1]->type == ATTRIBUTE_EXTENSIONS && ext_num != num_extensions) { + // Search for the extension to print + while(ext_to_print < ext_list_size && !((extensions_mask >> extension_list[ext_to_print].id) & 1U)) ext_to_print++; + if(ext_to_print == ext_list_size) { + printBug("print_ascii_riscv: Unable to find the extension to print"); + } + + printOut(lbuf, 3 + strlen(extension_list[ext_to_print].str), "%s - %s%s", logo->color_text[0], extension_list[ext_to_print].str, art->reset); + ext_num++; + ext_to_print++; + } + else { + attr_to_print++; + beg_space = 0; + space_right = 2 + 1 + (la - strlen(attribute_fields[attr_type])); + + printOut(lbuf, beg_space + strlen(attribute_fields[attr_type]) + space_right + strlen(attr_value), + "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attribute_fields[attr_type], art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); + } + } + printOutLine(lbuf, art, termw); + printf("\n"); + } + printf("\n"); + + free(lbuf->buf); + free(lbuf); +} + +bool print_cpufetch_riscv(struct cpuInfo* cpu, STYLE s, struct color** cs, struct terminal* term) { + struct ascii* art = set_ascii(get_soc_vendor(cpu->soc), s); + if(art == NULL) + return false; + + // Step 1. Retrieve attributes + char* uarch = get_str_uarch(cpu); + char* manufacturing_process = get_str_process(cpu->soc); + char* soc_name = get_soc_name(cpu->soc); + char* extensions = get_str_extensions(cpu); + char* max_frequency = get_str_freq(cpu->freq); + char* n_cores = get_str_topology(cpu, cpu->topo); + + /*char* l1i = get_str_l1i(cpu->cach); + char* l1d = get_str_l1d(cpu->cach); + char* l2 = get_str_l2(cpu->cach); + char* l3 = get_str_l3(cpu->cach);*/ + char* pp = get_str_peak_performance(cpu->peak_performance); + + // Step 2. Set attributes + setAttribute(art,ATTRIBUTE_SOC,soc_name); + setAttribute(art,ATTRIBUTE_TECHNOLOGY,manufacturing_process); + setAttribute(art,ATTRIBUTE_UARCH,uarch); + setAttribute(art,ATTRIBUTE_NCORES, n_cores); + setAttribute(art,ATTRIBUTE_FREQUENCY,max_frequency); + if(extensions != NULL) { + setAttribute(art,ATTRIBUTE_EXTENSIONS,extensions); + } + /*setAttribute(art,ATTRIBUTE_L1i,l1i); + setAttribute(art,ATTRIBUTE_L1d,l1d); + setAttribute(art,ATTRIBUTE_L2,l2); + if(l3 != NULL) { + setAttribute(art,ATTRIBUTE_L3,l3); + }*/ + setAttribute(art,ATTRIBUTE_PEAK,pp); + + // Step 3. Print output + const char** attribute_fields = ATTRIBUTE_FIELDS; + uint32_t longest_attribute = longest_attribute_length(art, attribute_fields); + uint32_t longest_field = longest_field_length(art, longest_attribute); + choose_ascii_art(art, cs, term, longest_field); + + if(!ascii_fits_screen(term->w, *art->art, longest_field)) { + // Despite of choosing the smallest logo, the output does not fit + // Choose the shorter field names and recalculate the longest attr + attribute_fields = ATTRIBUTE_FIELDS_SHORT; + longest_attribute = longest_attribute_length(art, attribute_fields); + } + + print_ascii_riscv(art, longest_attribute, term->w, attribute_fields, cpu->ext->mask); + + return true; +} +#endif + struct terminal* get_terminal_size(void) { struct terminal* term = emalloc(sizeof(struct terminal)); @@ -948,5 +1115,7 @@ bool print_cpufetch(struct cpuInfo* cpu, STYLE s, struct color** cs, bool show_f return print_cpufetch_ppc(cpu, s, cs, term, show_full_cpu_name); #elif ARCH_ARM return print_cpufetch_arm(cpu, s, cs, term); +#elif ARCH_RISCV + return print_cpufetch_riscv(cpu, s, cs, term); #endif } diff --git a/src/common/printer.h b/src/common/printer.h index 6863d4b..26a1fa3 100644 --- a/src/common/printer.h +++ b/src/common/printer.h @@ -11,6 +11,8 @@ typedef int STYLE; #include "../ppc/ppc.h" #elif ARCH_ARM #include "../arm/midr.h" +#elif ARCH_RISCV + #include "../riscv/riscv.h" #endif // +-----------------------------------+-----------------------+ @@ -22,6 +24,7 @@ typedef int STYLE; #define COLOR_DEFAULT_IBM "092,119,172:092,119,172:000,000,000:240,240,240:092,119,172" #define COLOR_DEFAULT_ARM "000,145,189:000,145,189:000,000,000:240,240,240:000,145,189" #define COLOR_DEFAULT_ROCKCHIP "114,159,207:229,195,000:000,000,000:240,240,240:114,159,207" +#define COLOR_DEFAULT_SIFIVE "255,255,255:000,000,000:000,000,000:255,255,255:000,000,000" #ifdef ARCH_X86 void print_levels(struct cpuInfo* cpu); diff --git a/src/riscv/riscv.c b/src/riscv/riscv.c new file mode 100644 index 0000000..67e40b7 --- /dev/null +++ b/src/riscv/riscv.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include "../common/global.h" +#include "../common/udev.h" +#include "udev.h" +#include "uarch.h" +#include "soc.h" + +struct frequency* get_frequency_info(uint32_t core) { + struct frequency* freq = emalloc(sizeof(struct frequency)); + + freq->base = UNKNOWN_DATA; + freq->max = get_max_freq_from_file(core); + + return freq; +} + +int64_t get_peak_performance(struct cpuInfo* cpu) { + //First check we have consistent data + if(get_freq(cpu->freq) == UNKNOWN_DATA) { + return -1; + } + + int64_t flops = cpu->topo->total_cores * (get_freq(cpu->freq) * 1000000); + return flops; +} + +struct extensions* get_extensions_from_str(char* str) { + struct extensions* ext = emalloc(sizeof(struct extensions)); + ext->mask = 0; + ext->str = NULL; + + if(str == NULL) { + return ext; + } + + int len = sizeof(char) * (strlen(str)+1); + ext->str = emalloc(sizeof(char) * len); + memset(ext->str, 0, len); + strncpy(ext->str, str, sizeof(char) * len); + + // Code inspired in Linux kernel: + // https://elixir.bootlin.com/linux/v6.2.10/source/arch/riscv/kernel/cpufeature.c + char* isa = str; + if (!strncmp(isa, "rv32", 4)) + isa += 4; + else if (!strncmp(isa, "rv64", 4)) + isa += 4; + else { + printBug("get_extensions_from_str: ISA string must start with rv64 or rv32"); + return ext; + } + + for(char* e = isa; *e != '\0'; e++) { + int n = *e - 'a'; + ext->mask |= 1UL << n; + } + + return ext; +} + +struct cpuInfo* get_cpu_info(void) { + struct cpuInfo* cpu = malloc(sizeof(struct cpuInfo)); + //init_cpu_info(cpu); + struct topology* topo = emalloc(sizeof(struct topology)); + topo->total_cores = get_ncores_from_cpuinfo(); + topo->cach = NULL; + cpu->topo = topo; + + char* cpuinfo_str = get_uarch_from_cpuinfo(); + char* ext_str = get_extensions_from_cpuinfo(); + cpu->hv = emalloc(sizeof(struct hypervisor)); + cpu->hv->present = false; + cpu->ext = get_extensions_from_str(ext_str); + if(cpu->ext->str != NULL && cpu->ext->mask == 0) return NULL; + cpu->arch = get_uarch_from_cpuinfo_str(cpuinfo_str, cpu); + cpu->soc = get_soc(); + cpu->freq = get_frequency_info(0); + cpu->peak_performance = get_peak_performance(cpu); + + return cpu; +} + +//TODO: Might be worth refactoring with other archs +char* get_str_topology(struct cpuInfo* cpu, struct topology* topo) { + uint32_t size = 3+7+1; + char* string = emalloc(sizeof(char)*size); + snprintf(string, size, "%d cores", topo->total_cores); + + return string; +} + +char* get_str_extensions(struct cpuInfo* cpu) { + if(cpu->ext != NULL) { + return cpu->ext->str; + } + return NULL; +} + +void print_debug(struct cpuInfo* cpu) { + printf("Unimplemented!\n"); +} diff --git a/src/riscv/riscv.h b/src/riscv/riscv.h new file mode 100644 index 0000000..fcbdff1 --- /dev/null +++ b/src/riscv/riscv.h @@ -0,0 +1,37 @@ +#ifndef __RISCV__ +#define __RISCV__ + +#include "../common/cpu.h" + +struct extension { + int id; + char* str; +}; + +// https://en.wikichip.org/wiki/risc-v/standard_extensions +// Included all except for G +static const struct extension extension_list[] = { + { 'i' - 'a', "(I) Integer Instruction Set" }, + { 'm' - 'a', "(M) Integer Multiplication and Division" }, + { 'a' - 'a', "(A) Atomic Instructions" }, + { 'f' - 'a', "(F) Single-Precision Floating-Point" }, + { 'd' - 'a', "(D) Double-Precision Floating-Point" }, + { 'q' - 'a', "(Q) Quad-Precision Floating-Point" }, + { 'l' - 'a', "(L) Decimal Floating-Point" }, + { 'c' - 'a', "(C) Compressed Instructions" }, + { 'b' - 'a', "(B) Double-Precision Floating-Point" }, + { 'j' - 'a', "(J) Dynamically Translated Languages" }, + { 't' - 'a', "(T) Transactional Memory" }, + { 'p' - 'a', "(P) Packed-SIMD Instructions" }, + { 'v' - 'a', "(V) Vector Operations" }, + { 'n' - 'a', "(N) User-Level Interrupts" }, + { 'h' - 'a', "(H) Hypervisor" }, + { 's' - 'a', "(S) Supervisor-level Instructions" } +}; + +struct cpuInfo* get_cpu_info(void); +char* get_str_topology(struct cpuInfo* cpu, struct topology* topo); +char* get_str_extensions(struct cpuInfo* cpu); +void print_debug(struct cpuInfo* cpu); + +#endif diff --git a/src/riscv/soc.c b/src/riscv/soc.c new file mode 100644 index 0000000..6f3c7c9 --- /dev/null +++ b/src/riscv/soc.c @@ -0,0 +1,144 @@ +#include "soc.h" +#include "socs.h" +#include "udev.h" +#include "../common/global.h" + +#include + +VENDOR get_soc_vendor(struct system_on_chip* soc) { + return soc->soc_vendor; +} + +char* get_str_process(struct system_on_chip* soc) { + char* str; + + if(soc->process == UNKNOWN) { + str = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1)); + snprintf(str, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN); + } + else { + str = emalloc(sizeof(char) * 5); + memset(str, 0, sizeof(char) * 5); + snprintf(str, 5, "%dnm", soc->process); + } + return str; +} + +char* get_soc_name(struct system_on_chip* soc) { + if(soc->soc_model == SOC_MODEL_UNKNOWN) + return soc->raw_name; + return soc->soc_name; +} + +static char* soc_trademark_string[] = { + [SOC_VENDOR_SIFIVE] = "SiFive ", + [SOC_VENDOR_STARFIVE] = "StarFive ", + [SOC_VENDOR_ALLWINNER] = "Allwinner " +}; + +void fill_soc(struct system_on_chip* soc, char* soc_name, SOC soc_model, int32_t process) { + soc->soc_model = soc_model; + soc->soc_vendor = get_soc_vendor_from_soc(soc_model); + soc->process = process; + int len = strlen(soc_name) + strlen(soc_trademark_string[soc->soc_vendor]) + 1; + soc->soc_name = emalloc(sizeof(char) * len); + memset(soc->soc_name, 0, sizeof(char) * len); + sprintf(soc->soc_name, "%s%s", soc_trademark_string[soc->soc_vendor], soc_name); +} + +bool match_soc(struct system_on_chip* soc, char* raw_name, char* expected_name, char* soc_name, SOC soc_model, int32_t process) { + int len1 = strlen(raw_name); + int len2 = strlen(expected_name); + int len = min(len1, len2); + + if(strncmp(raw_name, expected_name, len) != 0) { + return false; + } + else { + fill_soc(soc, soc_name, soc_model, process); + return true; + } +} + +#define SOC_START if (false) {} +#define SOC_EQ(raw_name, expected_name, soc_name, soc_model, soc, process) \ + else if (match_soc(soc, raw_name, expected_name, soc_name, soc_model, process)) return true; +#define SOC_END else { return false; } + +bool match_sifive(char* soc_name, struct system_on_chip* soc) { + char* tmp = soc_name; + + // Dont know if it makes sense in RISC-V + /*if((tmp = strstr(soc_name, "???")) == NULL) + return false;*/ + + //soc->soc_vendor = ??? + + SOC_START + SOC_EQ(tmp, "fu740", "Freedom U740", SOC_SIFIVE_U740, soc, 40) + SOC_END +} + +bool match_starfive(char* soc_name, struct system_on_chip* soc) { + SOC_START + SOC_EQ(soc_name, "jh7110#", "VisionFive 2", SOC_STARFIVE_VF2, soc, 28) // https://blog.bitsofnetworks.org/benchmarking-risc-v-visionfive-2-vs-the-world.html + SOC_EQ(soc_name, "jh7110", "VisionFive 2", SOC_STARFIVE_VF2, soc, 28) + SOC_END +} + +bool match_allwinner(char* soc_name, struct system_on_chip* soc) { + SOC_START + SOC_EQ(soc_name, "sun20i-d1", "D1-H", SOC_ALLWINNER_D1H, soc, 22) + SOC_END +} + +struct system_on_chip* parse_soc_from_string(struct system_on_chip* soc) { + char* raw_name = soc->raw_name; + + if(match_starfive(raw_name, soc)) + return soc; + + if(match_allwinner(raw_name, soc)) + return soc; + + match_sifive(raw_name, soc); + return soc; +} + +struct system_on_chip* guess_soc_from_devtree(struct system_on_chip* soc) { + char* tmp = get_hardware_from_devtree(); + + if(tmp != NULL) { + soc->raw_name = tmp; + return parse_soc_from_string(soc); + } + + return soc; +} + +struct system_on_chip* get_soc(void) { + struct system_on_chip* soc = emalloc(sizeof(struct system_on_chip)); + soc->raw_name = NULL; + soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->soc_model = SOC_MODEL_UNKNOWN; + soc->process = UNKNOWN; + + soc = guess_soc_from_devtree(soc); + if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->raw_name != NULL) { + printWarn("SoC detection failed using device tree: Found '%s' string", soc->raw_name); + } + else { + printWarn("SoC detection failed using device tree"); + } + } + + if(soc->soc_model == SOC_MODEL_UNKNOWN) { + // raw_name might not be NULL, but if we were unable to find + // the exact SoC, just print "Unkwnown" + soc->raw_name = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1)); + snprintf(soc->raw_name, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN); + } + + return soc; +} diff --git a/src/riscv/soc.h b/src/riscv/soc.h new file mode 100644 index 0000000..b6ba2dd --- /dev/null +++ b/src/riscv/soc.h @@ -0,0 +1,29 @@ +#ifndef __SOC__ +#define __SOC__ + +#include "../common/cpu.h" +#include + +typedef int32_t SOC; + +enum { + SOC_VENDOR_UNKNOWN, + SOC_VENDOR_SIFIVE, + SOC_VENDOR_STARFIVE, + SOC_VENDOR_ALLWINNER +}; + +struct system_on_chip { + SOC soc_model; + VENDOR soc_vendor; + int32_t process; + char* soc_name; + char* raw_name; +}; + +struct system_on_chip* get_soc(void); +char* get_soc_name(struct system_on_chip* soc); +VENDOR get_soc_vendor(struct system_on_chip* soc); +char* get_str_process(struct system_on_chip* soc); + +#endif diff --git a/src/riscv/socs.h b/src/riscv/socs.h new file mode 100644 index 0000000..0833c77 --- /dev/null +++ b/src/riscv/socs.h @@ -0,0 +1,25 @@ +#ifndef __SOCS__ +#define __SOCS__ + +#include "soc.h" + +// List of supported SOCs +enum { + // SIFIVE + SOC_SIFIVE_U740, + // STARFIVE + SOC_STARFIVE_VF2, + // ALLWINNER + SOC_ALLWINNER_D1H, + // UNKNOWN + SOC_MODEL_UNKNOWN +}; + +inline static VENDOR get_soc_vendor_from_soc(SOC soc) { + if(soc >= SOC_SIFIVE_U740 && soc <= SOC_SIFIVE_U740) return SOC_VENDOR_SIFIVE; + if(soc >= SOC_STARFIVE_VF2 && soc <= SOC_STARFIVE_VF2) return SOC_VENDOR_STARFIVE; + if(soc >= SOC_ALLWINNER_D1H && soc <= SOC_ALLWINNER_D1H) return SOC_VENDOR_ALLWINNER; + return SOC_VENDOR_UNKNOWN; +} + +#endif diff --git a/src/riscv/uarch.c b/src/riscv/uarch.c new file mode 100644 index 0000000..0821993 --- /dev/null +++ b/src/riscv/uarch.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#include "uarch.h" +#include "../common/global.h" + +typedef uint32_t MICROARCH; + +struct uarch { + MICROARCH uarch; + char* uarch_str; +}; + +enum { + UARCH_UNKNOWN, + // SIFIVE + UARCH_U54, + UARCH_U74, + // THEAD + UARCH_C906, + UARCH_C910 +}; + +#define UARCH_START if (false) {} +#define CHECK_UARCH(arch, cpu, cpuinfo_str, uarch_str, str, uarch, vendor) \ + else if (strcmp(cpuinfo_str, uarch_str) == 0) fill_uarch(arch, cpu, str, uarch, vendor); +#define UARCH_END else { printBug("Unknown microarchitecture detected: uarch='%s'", cpuinfo_str); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } + +void fill_uarch(struct uarch* arch, struct cpuInfo* cpu, char* str, MICROARCH u, VENDOR vendor) { + arch->uarch = u; + cpu->cpu_vendor = vendor; + arch->uarch_str = emalloc(sizeof(char) * (strlen(str)+1)); + strcpy(arch->uarch_str, str); +} + +// https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/riscv/cpus.yaml +// SiFive: https://www.sifive.com/risc-v-core-ip +// T-Head: https://www.t-head.cn/product/c906 +struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu) { + struct uarch* arch = emalloc(sizeof(struct uarch)); + if(cpuinfo_str == NULL) { + printWarn("get_uarch_from_cpuinfo: Unable to detect microarchitecture, cpuinfo_str is NULL"); + fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); + return arch; + } + + // U74/U74-MC: + // SiFive says that U74-MC is "Multicore: four U74 cores and one S76 core" while + // U74 is "High performance Linux-capable processor". It's like U74-MC is somehow a small SoC containing + // the U74 and the S76? Then U74-MC is not a microarchitecture per se... + UARCH_START + CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,bullet0", "U74", UARCH_U74, CPU_VENDOR_SIFIVE) // bullet0 is present in U740, which has U74 + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,e5", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,e7", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,e71", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,rocket0", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,u5", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,u54", "U54", UARCH_U54, CPU_VENDOR_SIFIVE) + // CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,u7", "XXXXXX", UARCH_U74, CPU_VENDOR_SIFIVE) + CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,u74", "U74", UARCH_U74, CPU_VENDOR_SIFIVE) + CHECK_UARCH(arch, cpu, cpuinfo_str, "sifive,u74-mc", "U74", UARCH_U74, CPU_VENDOR_SIFIVE) + CHECK_UARCH(arch, cpu, cpuinfo_str, "thead,c906", "T-Head C906", UARCH_C906, CPU_VENDOR_THEAD) + CHECK_UARCH(arch, cpu, cpuinfo_str, "thead,c910", "T-Head C910", UARCH_C910, CPU_VENDOR_THEAD) + UARCH_END + + return arch; +} + +char* get_str_uarch(struct cpuInfo* cpu) { + return cpu->arch->uarch_str; +} + +void free_uarch_struct(struct uarch* arch) { + free(arch->uarch_str); + free(arch); +} diff --git a/src/riscv/uarch.h b/src/riscv/uarch.h new file mode 100644 index 0000000..4a9aa24 --- /dev/null +++ b/src/riscv/uarch.h @@ -0,0 +1,13 @@ +#ifndef __UARCH__ +#define __UARCH__ + +#include +#include "riscv.h" + +struct uarch; + +char* get_str_uarch(struct cpuInfo* cpu); +void free_uarch_struct(struct uarch* arch); +struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu); + +#endif diff --git a/src/riscv/udev.c b/src/riscv/udev.c new file mode 100644 index 0000000..1769e9e --- /dev/null +++ b/src/riscv/udev.c @@ -0,0 +1,92 @@ +#include +#include + +#include "../common/global.h" +#include "udev.h" + +#define _PATH_CPUINFO "/proc/cpuinfo" +#define _PATH_DEVTREE "/proc/device-tree/compatible" +#define CPUINFO_UARCH_STR "uarch\t\t: " +#define CPUINFO_EXTENSIONS_STR "isa\t\t: " +#define DEVTREE_HARDWARE_FIELD 0 + +char* get_field_from_devtree(int DEVTREE_FIELD) { + int filelen; + char* buf; + if((buf = read_file(_PATH_DEVTREE, &filelen)) == NULL) { + printWarn("read_file: %s: %s", _PATH_DEVTREE, strerror(errno)); + return NULL; + } + + // Here we would use strstr to find the comma. + // However, the device-tree file may contain NULL + // bytes in the middle of the string, which would + // cause strstr to return NULL even when there might + // be an occurence after the NULL byte + // + // We iterate the string backwards to find the field + // in position n-DEVTREE_HARDWARE_FIELD where n + // is the number of fields. + int i=0; + char* tmp1 = buf+filelen-1; + do { + tmp1--; + if(*tmp1 == ',') i++; + } while(tmp1 != buf && i <= DEVTREE_FIELD); + + if(tmp1 == buf) { + printWarn("get_field_from_devtree: Unable to find field %d", DEVTREE_FIELD); + return NULL; + } + + tmp1++; + int strlen = filelen-(tmp1-buf); + char* hardware = emalloc(sizeof(char) * strlen); + memset(hardware, 0, sizeof(char) * strlen); + strncpy(hardware, tmp1, strlen-1); + + return hardware; +} + +char* parse_cpuinfo_field(char* field_str) { + int filelen; + char* buf; + if((buf = read_file(_PATH_CPUINFO, &filelen)) == NULL) { + printWarn("read_file: %s: %s", _PATH_CPUINFO, strerror(errno)); + return NULL; + } + + char* tmp = strstr(buf, field_str); + if(tmp == NULL) { + printWarn("parse_cpuinfo_field: Unable to find field %s", field_str); + return NULL; + } + + tmp += strlen(field_str); + char* end = strstr(tmp, "\n"); + + if(end == NULL) { + printWarn("parse_cpuinfo_field: Unable to find newline after field %s", field_str); + return NULL; + } + + int ret_strlen = (end-tmp); + char* ret = emalloc(sizeof(char) * (ret_strlen+1)); + memset(ret, 0, sizeof(char) * (ret_strlen+1)); + strncpy(ret, tmp, ret_strlen); + + return ret; +} + +char* get_hardware_from_devtree(void) { + return get_field_from_devtree(DEVTREE_HARDWARE_FIELD); +} + +char* get_uarch_from_cpuinfo(void) { + return parse_cpuinfo_field(CPUINFO_UARCH_STR); +} + +char* get_extensions_from_cpuinfo() { + return parse_cpuinfo_field(CPUINFO_EXTENSIONS_STR); +} + diff --git a/src/riscv/udev.h b/src/riscv/udev.h new file mode 100644 index 0000000..48a54db --- /dev/null +++ b/src/riscv/udev.h @@ -0,0 +1,12 @@ +#ifndef __UDEV_RISCV__ +#define __UDEV_RISCV__ + +#include "../common/udev.h" + +#define UNKNOWN -1 + +char* get_hardware_from_devtree(void); +char* get_uarch_from_cpuinfo(void); +char* get_extensions_from_cpuinfo(void); + +#endif