Addded basic profiling and fixed sd card init status being inverted

This commit is contained in:
Alex Duchesne 2020-09-09 16:43:17 -04:00
parent 2aa48f3871
commit e49b0e6cc3
23 changed files with 297 additions and 66 deletions

View File

@ -53,7 +53,7 @@ Retro-Go is a launcher and framework to run emulators on the ODROID-GO. It comes
Note: If you are stuck in an emulator, hold MENU while powering up the device to return to the launcher.
# Game covers
# Game covers
The preferred cover art format is 8bit PNG with a resolution of 168x168 and I recommend post-processing
with [pngquant](https://pngquant.org/). Retro-Go is also backwards-compatible with the official RAW565
Go-Play romart pack that you may already have.
@ -88,14 +88,13 @@ The official esp-idf version 3.3 or 4.0 is required and it is recommended to app
sdcard-fix patch located in the tools folder. Both make and cmake are supported.
## Build everything and generate .fw:
1. `python rg_tool.py release` or `python rg_tool.py release --use-make` (legacy make system)
1. `python rg_tool.py build-fw` or `python rg_tool.py build-fw --use-make` (legacy make system)
For a smaller build you can also specify which apps you want, for example the launcher + nes/gameboy only:
1. `python rg_tool.py release retro-go nofrendo-go gnuboy-go`
1. `python rg_tool.py build-fw retro-go nofrendo-go gnuboy-go`
## Build, flash, and monitor individual apps for faster development:
1. Build app: `python rg_tool.py build nofrendo-go`
2. Then flash and monitor: `python rg_tool.py --offset=0x100000 --port=COM3 flashmon nofrendo-go`
1. `python rg_tool.py run nofrendo-go --offset=0x100000 --port=COM3`
* Offset is required only if you use my multi-firmware, in which case it is displayed in the boot menu.

View File

@ -13,3 +13,13 @@ if(NOT PROJECT_VER)
string(TIMESTAMP TODAY "%Y.%m.%d")
set(PROJECT_VER "${TODAY}-nogit")
endif()
if($ENV{ENABLE_NETPLAY})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_NETPLAY")
set(ENABLE_NETPLAY 1)
endif()
if($ENV{ENABLE_PROFILING})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_PROFILING")
set(ENABLE_PROFILING 1)
endif()

View File

@ -1,14 +1,14 @@
#pragma once
/* GIMP RGBA C-Source image dump (hourglass_empty_black_48dp.c) */
/* This image is taken from https://icons8.de/icon/49923/sanduhr and is free license. */
static const struct {
unsigned int width;
unsigned int height;
unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
unsigned char pixel_data[48 * 48 * 2 + 1];
} image_hourglass_empty_black_48dp = {
} image_hourglass = {
48, 48, 2,
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"

View File

@ -0,0 +1 @@
#pragma once

View File

@ -1,3 +1,5 @@
#pragma once
/* GIMP RGBA C-Source image dump (Untitled.c) */
/* This image is based on a free icon from https://icons8.com/icons/set/micro-sd */
@ -6,7 +8,7 @@ static const struct {
unsigned int height;
unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
unsigned char pixel_data[48 * 48 * 2 + 1];
} image_sdcard_red_48dp = {
} image_sdcard = {
48, 48, 2,
"\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
"\377\377\377\377\377\377}\377Y\376\266\375\226\375\226\375\226\375\226\375"

View File

@ -9,7 +9,7 @@
#include "odroid_system.h"
#include "odroid_display.h"
#include "odroid_image_hourglass.h"
#include "bitmaps/image_hourglass.h"
#define SCREEN_WIDTH ODROID_SCREEN_WIDTH
#define SCREEN_HEIGHT ODROID_SCREEN_HEIGHT
@ -1102,11 +1102,11 @@ void odroid_display_clear(uint16_t color)
void odroid_display_show_hourglass()
{
odroid_display_write((SCREEN_WIDTH / 2) - (image_hourglass_empty_black_48dp.width / 2),
(SCREEN_HEIGHT / 2) - (image_hourglass_empty_black_48dp.height / 2),
image_hourglass_empty_black_48dp.width,
image_hourglass_empty_black_48dp.height,
(uint16_t*)image_hourglass_empty_black_48dp.pixel_data);
odroid_display_write((SCREEN_WIDTH / 2) - (image_hourglass.width / 2),
(SCREEN_HEIGHT / 2) - (image_hourglass.height / 2),
image_hourglass.width,
image_hourglass.height,
(uint16_t*)image_hourglass.pixel_data);
}
void odroid_display_deinit()

View File

@ -6,8 +6,8 @@
#include <stdio.h>
#include <math.h>
#include "bitmaps/font_basic.h"
#include "odroid_system.h"
#include "odroid_font8x8.h"
#include "odroid_overlay.h"
static uint16_t *overlay_buffer = NULL;

View File

@ -0,0 +1,125 @@
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <stdio.h>
#include "odroid_system.h"
#include "odroid_profiler.h"
// Note this profiler might be inaccurate because of:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=28205
static profile_t *profile;
static volatile bool profile_lock;
NO_PROFILE static inline profile_frame_t *find_frame(void *this_fn, void *call_site)
{
for (int i = 0; i < 512; ++i)
{
profile_frame_t *frame = &profile->frames[i];
if (frame->func_ptr == 0)
{
profile->total_frames = MAX(profile->total_frames, i + 1);
frame->func_ptr = this_fn;
frame->caller_ptr = call_site;
}
if (frame->func_ptr == this_fn && frame->caller_ptr == call_site)
{
return frame;
}
}
RG_PANIC("Profile memory exhausted!");
}
NO_PROFILE void rg_profiler_init(void)
{
printf("%s: Initializing profile...\n", __func__);
profile = rg_alloc(sizeof(profile_t), MEM_SLOW);
profile->time_started = get_elapsed_time();
}
NO_PROFILE void rg_profiler_free(void)
{
// free(call_stack);
free(profile);
profile = NULL;
}
NO_PROFILE static int list_comparator(const void *p, const void *q)
{
return ((profile_frame_t*)q)->run_time - ((profile_frame_t*)p)->run_time;
}
NO_PROFILE void rg_profiler_print(void)
{
if (!profile)
return;
// xSemaphoreTake(profile_lock, pdMS_TO_TICKS(10000));
profile_lock = true;
uint32_t time_running = get_elapsed_time_since(profile->time_started);
// probably should use a mutex here
qsort(profile->frames, profile->total_frames, sizeof(profile_frame_t), list_comparator);
for (int i = 0; i < profile->total_frames; ++i)
{
profile_frame_t *frame = &profile->frames[i];
printf(
"%p\t%p\t%d\t%d\t%.0f%%\n",
frame->caller_ptr,
frame->func_ptr,
frame->num_calls,
frame->run_time,
(float)frame->run_time / time_running * 100
);
}
printf("Profile frames: %d, total time: %d\n", profile->total_frames, time_running);
profile_lock = false;
}
NO_PROFILE void rg_profiler_push(char *section_name)
{
}
NO_PROFILE void rg_profiler_pop(void)
{
}
NO_PROFILE void __cyg_profile_func_enter(void *this_fn, void *call_site)
{
if (!profile)
return;
while (profile_lock);
profile_frame_t *fn = find_frame(this_fn, call_site);
// Recursion
if (fn->enter_time != 0)
fn->run_time += get_elapsed_time_since(fn->enter_time);
fn->enter_time = get_elapsed_time();
fn->num_calls++;
}
NO_PROFILE void __cyg_profile_func_exit(void *this_fn, void *call_site)
{
if (!profile)
return;
while (profile_lock);
profile_frame_t *fn = find_frame(this_fn, call_site);
fn->run_time += get_elapsed_time_since(fn->enter_time);
fn->enter_time = 0;
}

View File

@ -0,0 +1,40 @@
#pragma once
typedef struct
{
void *func_ptr;
void *caller_ptr;
uint32_t enter_time;
uint32_t num_calls;
uint32_t run_time;
} profile_frame_t;
typedef struct
{
uint64_t time_started;
uint64_t time_stopped;
uint32_t total_frames;
uint32_t locked;
profile_frame_t frames[512];
} profile_t;
#ifdef __cplusplus
extern "C" {
#endif
void rg_profiler_init(void);
void rg_profiler_free(void);
void rg_profiler_start(void);
void rg_profiler_stop(void);
void rg_profiler_print(void);
void rg_profiler_push(char *section_name);
void rg_profiler_pop(void);
void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);
#ifdef __cplusplus
}
#endif
#define NO_PROFILE __attribute((no_instrument_function))

View File

@ -16,7 +16,8 @@
static bool sdcardOpen = false;
#define SDCARD_ACCESS_BEGIN() { \
if (!sdcardOpen) RG_PANIC("SD Card not initialized"); \
/* if (!sdcardOpen) RG_PANIC("SD Card not initialized"); */ \
if (!sdcardOpen) { printf("SD Card not initialized\n"); return -1; } \
odroid_system_spi_lock_acquire(SPI_LOCK_SDCARD); \
}
#define SDCARD_ACCESS_END() odroid_system_spi_lock_release(SPI_LOCK_SDCARD)
@ -61,9 +62,10 @@ int odroid_sdcard_open()
if (ret != ESP_OK)
{
printf("odroid_sdcard_open: esp_vfs_fat_sdmmc_mount failed (%d)\n", ret);
return -1;
}
return (ret == ESP_OK) ? 0 : -1;
return 0;
}
int odroid_sdcard_close()
@ -146,7 +148,7 @@ int odroid_sdcard_unzip_file(const char* path, void* buf, size_t buf_size)
return count;
}
int odroid_sdcard_list(const char* path, char *out_files, size_t *out_count)
int odroid_sdcard_list(const char* path, char **out_files, size_t *out_count)
{
SDCARD_ACCESS_BEGIN();

View File

@ -10,7 +10,7 @@ int odroid_sdcard_open();
int odroid_sdcard_close();
int odroid_sdcard_mkdir(const char *path);
int odroid_sdcard_unlink(const char* path);
int odroid_sdcard_list(const char* path, char *out_files, size_t *out_count);
int odroid_sdcard_list(const char* path, char **out_files, size_t *out_count);
int odroid_sdcard_read_file(const char* path, void* buf, size_t buf_size);
int odroid_sdcard_unzip_file(const char* path, void* buf, size_t buf_size);
int odroid_sdcard_write_file(const char* path, void* buf, size_t buf_size);

View File

@ -12,7 +12,7 @@
#include <string.h>
#include <stdio.h>
#include "odroid_image_sdcard.h"
#include "bitmaps/image_sdcard.h"
#include "odroid_system.h"
// This is a direct pointer to rtc slow ram which isn't cleared on
@ -70,7 +70,7 @@ void odroid_system_init(int appId, int sampleRate)
// sdcard init must be before odroid_display_init()
// and odroid_settings_init() if JSON is used
bool sd_init = odroid_sdcard_open();
bool sd_init = (odroid_sdcard_open() == 0);
odroid_settings_init();
odroid_overlay_init();
@ -96,11 +96,11 @@ void odroid_system_init(int appId, int sampleRate)
if (!sd_init)
{
odroid_display_clear(C_WHITE);
odroid_display_write((ODROID_SCREEN_WIDTH - image_sdcard_red_48dp.width) / 2,
(ODROID_SCREEN_HEIGHT - image_sdcard_red_48dp.height) / 2,
image_sdcard_red_48dp.width,
image_sdcard_red_48dp.height,
(uint16_t*)image_sdcard_red_48dp.pixel_data);
odroid_display_write((ODROID_SCREEN_WIDTH - image_sdcard.width) / 2,
(ODROID_SCREEN_HEIGHT - image_sdcard.height) / 2,
image_sdcard.width,
image_sdcard.height,
(uint16_t*)image_sdcard.pixel_data);
odroid_system_halt();
}
@ -111,6 +111,11 @@ void odroid_system_init(int appId, int sampleRate)
panicTrace->magicWord = 0;
#ifdef ENABLE_PROFILING
printf("%s: Profiling has been enabled at compile time!\n", __func__);
rg_profiler_init();
#endif
printf("%s: System ready!\n\n", __func__);
}
@ -425,8 +430,9 @@ void odroid_system_set_led(int value)
static void odroid_system_monitor_task(void *arg)
{
runtime_counters_t current;
bool led_state = false;
bool letState = false;
float tickTime = 0;
uint loops = 0;
while (1)
{
@ -476,13 +482,13 @@ static void odroid_system_monitor_task(void *arg)
if (statistics.battery.percentage < 2)
{
led_state = !led_state;
odroid_system_set_led(led_state);
letState = !letState;
odroid_system_set_led(letState);
}
else if (led_state)
else if (letState)
{
led_state = false;
odroid_system_set_led(led_state);
letState = false;
odroid_system_set_led(letState);
}
printf("HEAP:%d+%d (%d+%d), BUSY:%.4f, FPS:%.4f (SKIP:%d, PART:%d, FULL:%d), BATTERY:%d\n",
@ -497,7 +503,15 @@ static void odroid_system_monitor_task(void *arg)
current.fullFrames,
statistics.battery.millivolts);
#ifdef ENABLE_PROFILING
if ((loops % 30) == 0)
{
rg_profiler_print();
}
#endif
vTaskDelay(pdMS_TO_TICKS(1000));
loops++;
}
vTaskDelete(NULL);
@ -524,7 +538,7 @@ IRAM_ATTR void odroid_system_spi_lock_acquire(spi_lock_res_t owner)
{
return;
}
else if (xSemaphoreTake(spiMutex, 10000 / portTICK_RATE_MS) == pdPASS)
else if (xSemaphoreTake(spiMutex, pdMS_TO_TICKS(10000)) == pdPASS)
{
spiMutexOwner = owner;
}
@ -551,29 +565,30 @@ void *rg_alloc(size_t size, uint32_t caps)
if (!(caps & MALLOC_CAP_32BIT))
{
caps |= MALLOC_CAP_8BIT;
caps |= MALLOC_CAP_8BIT;
}
ptr = heap_caps_calloc(1, size, caps);
printf("RG_ALLOC: SIZE: %u [SPIRAM: %u; 32BIT: %u; DMA: %u] PTR: %p\n",
size, (caps & MALLOC_CAP_SPIRAM) != 0, (caps & MALLOC_CAP_32BIT) != 0,
(caps & MALLOC_CAP_DMA) != 0, ptr);
if (!ptr)
{
size_t availaible = heap_caps_get_largest_free_block(caps);
size_t availaible = heap_caps_get_largest_free_block(caps);
// Loosen the caps and try again
ptr = heap_caps_calloc(1, size, caps & ~(MALLOC_CAP_SPIRAM|MALLOC_CAP_INTERNAL));
if (!ptr)
{
RG_PANIC("Memory allocation failed!");
}
// Loosen the caps and try again
ptr = heap_caps_calloc(1, size, caps & ~(MALLOC_CAP_SPIRAM|MALLOC_CAP_INTERNAL));
if (!ptr)
{
printf("RG_ALLOC: ^-- Allocation failed! (available: %d)\n", availaible);
RG_PANIC("Memory allocation failed!");
}
printf("RG_ALLOC: *** CAPS not fully met (req: %d, available: %d) ***\n", size, availaible);
printf("RG_ALLOC: ^-- CAPS not fully met! (available: %d)\n", availaible);
}
printf("RG_ALLOC: SIZE: %u [SPIRAM: %u; 32BIT: %u; DMA: %u] PTR: %p\n",
size, (caps & MALLOC_CAP_SPIRAM) != 0, (caps & MALLOC_CAP_32BIT) != 0,
(caps & MALLOC_CAP_DMA) != 0, ptr);
return ptr;
}

View File

@ -14,8 +14,9 @@
#include "odroid_audio.h"
#include "odroid_display.h"
#include "odroid_input.h"
#include "odroid_overlay.h"
#include "odroid_netplay.h"
#include "odroid_overlay.h"
#include "odroid_profiler.h"
#include "odroid_sdcard.h"
#include "odroid_settings.h"
@ -187,6 +188,12 @@ static inline uint get_elapsed_time_since(uint start)
#undef MAX
#define MAX(a,b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b);_a > _b ? _a : _b; })
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C
#endif
#define RG_PANIC(x) odroid_system_panic(x, __FUNCTION__, __FILE__)
#define MEM_ANY 0
@ -199,9 +206,3 @@ static inline uint get_elapsed_time_since(uint start)
void *rg_alloc(size_t size, uint32_t caps);
void rg_free(void *ptr);
void rg_perf_init(void);
void rg_perf_reset(void);
void rg_perf_print(void);
void rg_perf_func_enter(void *func_ptr, char *func_name);
void rg_perf_func_leave(void);

View File

@ -3,3 +3,7 @@ set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES "odroid")
register_component()
component_compile_options(-O3 -DIS_LITTLE_ENDIAN)
if(DEFINED ENABLE_PROFILING)
component_compile_options(-finstrument-functions)
endif()

View File

@ -3,3 +3,7 @@ set(COMPONENT_ADD_INCLUDEDIRS ".")
set(COMPONENT_REQUIRES "odroid")
register_component()
component_compile_options(-O3 -Wall -Wno-comment -Wno-error=comment)
if(DEFINED ENABLE_PROFILING)
component_compile_options(-finstrument-functions)
endif()

View File

@ -3,3 +3,7 @@ set(COMPONENT_ADD_INCLUDEDIRS ". includes engine netplay")
set(COMPONENT_REQUIRES "odroid")
register_component()
component_compile_options(-O3 -Wno-all -Wno-error -Wno-sequence-point)
if(DEFINED ENABLE_PROFILING)
component_compile_options(-finstrument-functions)
endif()

View File

@ -3,3 +3,7 @@ set(COMPONENT_ADD_INCLUDEDIRS "cpu nes mappers .")
set(COMPONENT_REQUIRES "odroid")
register_component()
component_compile_options(-O3 -Wno-error=char-subscripts -Wno-error=attributes)
if(DEFINED ENABLE_PROFILING)
component_compile_options(-finstrument-functions)
endif()

View File

@ -120,7 +120,7 @@ void emulator_init(retro_emulator_t *emu)
sprintf(path, ODROID_BASE_PATH_ROMS "/%s", emu->dirname);
odroid_sdcard_mkdir(path);
if (odroid_sdcard_list(path, &files, &count) == 0)
if (odroid_sdcard_list(path, &files, &count) == 0 && count > 0)
{
emu->roms.files = rg_alloc(count * sizeof(retro_emulator_file_t), MEM_ANY);
emu->roms.count = 0;

View File

@ -1,6 +1,7 @@
# Important: This file is now exec() in the global scope
# Notes:
# - Enabling netplay in an emulator increases its size by ~350KB
# - Enabling profiling in an emulator increases its size by ~50KB
# - Keep at least 32KB free in a partition for future updates
# - Partitions must be 64K aligned

View File

@ -94,7 +94,7 @@ def monitor_app(target, port):
parser = argparse.ArgumentParser(description="Retro-Go build tool")
parser.add_argument(
"command", choices=["build", "clean", "release", "mkfw", "flash", "flashmon", "monitor"],
"command", choices=["build-fw", "build", "clean", "flash", "monitor", "run"],
)
parser.add_argument(
"targets", nargs="*", default="all", choices=["all"] + list(PROJECT_APPS.keys())
@ -102,6 +102,12 @@ parser.add_argument(
parser.add_argument(
"--use-make", action="store_const", const=True, help="Use legacy make build system"
)
parser.add_argument(
"--profile", action="store_const", const=True, help="Build with profiling enabled"
)
parser.add_argument(
"--debug", action="store_const", const=True, help="Build with debugging enabled"
)
parser.add_argument(
"--port", default="COM3", help="Serial port to use for flash and monitor"
)
@ -113,26 +119,35 @@ parser.add_argument(
)
args = parser.parse_args()
command = args.command
targets = args.targets if not "all" in args.targets else PROJECT_APPS.keys()
if command == "clean" or command == "release":
if command == "build-fw":
for target in targets:
clean_app(target)
build_app(target, args.use_make)
build_firmware(targets)
if command == "build" or command == "release":
if command == "build":
for target in targets:
build_app(target, args.use_make)
if command == "release" or command == "mkfw":
build_firmware(targets)
if command == "flash" or command == "flashmon":
if command == "clean":
for target in targets:
offset = find_app(target, args.offset, args.app_offset)
flash_app(target, args.port, offset)
clean_app(target)
if command == "monitor" or command == "flashmon":
if command == "flash":
for target in targets:
flash_app(target, args.port, find_app(target, args.offset, args.app_offset))
if command == "monitor":
monitor_app(targets[0], args.port)
if command == "run":
build_app(targets[0], args.use_make)
flash_app(targets[0], args.port, find_app(targets[0], args.offset, args.app_offset))
monitor_app(targets[0], args.port)

View File

@ -3,3 +3,7 @@ set(COMPONENT_ADD_INCLUDEDIRS ". cpu sound")
set(COMPONENT_REQUIRES "odroid")
register_component()
component_compile_options(-O3 -DIS_LITTLE_ENDIAN)
if(DEFINED ENABLE_PROFILING)
component_compile_options(-finstrument-functions)
endif()

View File

@ -5,7 +5,7 @@ def readfile(filepath):
with open(filepath, "rb") as f: return f.read()
if len(sys.argv) < 4:
exit("usage: mkfw.py output_file description tile <type subtype length label binary> [, ...]")
exit("usage: mkfw.py output_file description tile <type subtype length label binfile> [, ...]")
fw_name = sys.argv[1]
@ -31,7 +31,7 @@ while pos < len(sys.argv):
size = max(length, math.ceil(len(data) / 0x10000) * 0x10000)
if size > length:
print(" > WARNING: Partition will be expanded by %d bytes" % (size - length))
print(" > WARNING: Partition smaller than file (+%d bytes), increasing length to %d" % (len(data) - length, size))
fw_data += struct.pack("<BBxx16sIII", partype, subtype, label.encode(), 0, size, len(data))
fw_data += data