[Update] Refactor devterm-a06-gearbox and fix gpu governor message.

This commit is contained in:
Mihaylov93 2022-04-10 00:25:28 +01:00
parent 0d92f26384
commit 3e16bb3254
4 changed files with 351 additions and 168 deletions

160
.gitignore vendored Normal file
View File

@ -0,0 +1,160 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

View File

@ -1,9 +1,10 @@
#!/usr/bin/python3 #!/usr/bin/python3
import argparse
import glob import glob
import os import os
import sys,getopt
import subprocess import subprocess
import sys
from typing import Any, NoReturn
# The gearings below were picked based on various tests by the ClockworkPi devs. # The gearings below were picked based on various tests by the ClockworkPi devs.
# The maximum-performance maximum-power gearing is present for completeness, but # The maximum-performance maximum-power gearing is present for completeness, but
@ -18,48 +19,48 @@ import subprocess
# #
# Gears are numbered in-order, starting from 1. # Gears are numbered in-order, starting from 1.
# It's up to you to ensure that they are sorted by performance :) # It's up to you to ensure that they are sorted by performance :)
def gears():
return [
gear(
little=(600000,),
use="simple writing tasks with long battery life"),
gear(
little=(800000,) * 2,
use="browsing most websites with long battery life"),
gear(
little=(1008000,) * 4,
gpu_freq=400000000,
use="most 2D games and emulators"),
gear(
big=(1008000,) * 2,
gpu_freq=400000000,
use="playing videos and 3D games"),
gear(
big=(1200000,) * 2,
gpu_freq=400000000,
use="performance-first tasks"),
gear(
little=(1416000,) * 4,
big=(1800000,) * 2,
gpu_freq=800000000,
use="max performance, max power (usage)"),
]
GPU_GOV_SIMPLE = "simple_ondemand" GPU_GOV_SIMPLE = "simple_ondemand"
GPU_GOV_PERF = "performance" GPU_GOV_PERF = "performance"
# Helper to convert the concise gear format above into a full description.
# def gears() -> list[dict[str, Any]]:
# `little` and `big` define the number of A53 and A72 CPU cores to enable, and return [
# their maximum frequencies (in kHZ). Cores that are omitted or set to zero are gear(little=(600000,), use="simple writing tasks with long battery life"),
# disabled. gear(little=(800000,) * 2, use="browsing most websites with long battery life"),
gear(little=(1008000,) * 4, gpu_freq=400000000, use="most 2D games and emulators"),
gear(big=(1008000,) * 2, gpu_freq=400000000, use="playing videos and 3D games"),
gear(big=(1200000,) * 2, gpu_freq=400000000, use="performance-first tasks"),
gear(
little=(1416000,) * 4,
big=(1800000,) * 2,
gpu_freq=800000000,
use="max performance, max power (usage)",
),
]
def gear( def gear(
little=(0, 0, 0, 0), little=(0, 0, 0, 0),
big=(0, 0), big=(0, 0),
gpu_freq=200000000, gpu_freq=200000000,
gpu_gov=GPU_GOV_SIMPLE, gpu_gov=GPU_GOV_SIMPLE,
use="", use="",
): ) -> dict[str, Any]:
"""Helper to convert the concise gear format above into a full description.
`little` and `big` define the number of A53 and A72 CPU cores to enable, and
their maximum frequencies (in kHz). Cores that are omitted or set to zero are
:param little: A53 Core.
:param big: A72 Core
:param gpu_freq: Gpu frequency in kHz
:param gpu_gov: Gpu Governor
:param use: Description of the gear.
:return: A dictionary of the parameters.
"""
# Extend to 4 little and 2 big cores (matching the A06). # Extend to 4 little and 2 big cores (matching the A06).
assert len(little) <= 4 assert len(little) <= 4
assert len(big) <= 2 assert len(big) <= 2
@ -74,122 +75,144 @@ def gear(
"use": use, "use": use,
} }
# We placed gears() at the top of the file to make it easier to find and edit. # We placed gears() at the top of the file to make it easier to find and edit.
# Now that we've defined the helpers it needs, evaluate the gears. # Now that we've defined the helpers it needs, evaluate the gears.
gears = gears() gears = gears()
def load_gear(gear):
def load_gear(gear: int) -> dict[str, Any]:
return gears[gear - 1] return gears[gear - 1]
cur_stat = [
cur_stat = [] "+-----------------------------------+-----------------+-----------+",
cur_stat.append("+-----------------------------------+-----------------+-----------+") "| Cortex-A53 | Cortex-A72 | Mali-T860 |",
cur_stat.append("| Cortex-A53 | Cortex-A72 | Mali-T860 |") "+--------+--------+--------+--------+--------+--------+-----------+",
cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") "| CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |",
cur_stat.append("| CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |") "+--------+--------+--------+--------+--------+--------+-----------+",
cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") "| 600 MHz| OFF | OFF | OFF | OFF | OFF | 400 MHz |",
cur_stat.append("| 600MHz | OFF | OFF | OFF | OFF | OFF | 400MHz |") #5 "+--------+--------+--------+--------+--------+--------+-----------+",
cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") ]
def isDigit(x):
try:
float(x)
return True
except ValueError:
return False
class A06: class A06:
"""A06 Module class."""
cpus = [] cpus = []
cpu_scaling_governor= "schedutil" cpu_scaling_governor: str = "schedutil"
gear = load_gear(1) # 1-5 gear = load_gear(1) # 1-5
null_out = "2>/dev/null" null_out: str = "2>/dev/null"
def __init__(self): def __init__(self):
self.cpus = [] self.cpus = []
self.init_cpu_infos() self.init_cpu_infos()
self.cpu_total_count = len(self.cpus) self.cpu_total_count = len(self.cpus)
def init_cpu_infos(self): def init_cpu_infos(self):
self.cpus = glob.glob('/sys/devices/system/cpu/cpu[0-9]') self.cpus = glob.glob("/sys/devices/system/cpu/cpu[0-9]")
self.cpus.sort() self.cpus.sort()
def get_cpu_gov(self): @property
def cpu_gov(self) -> str:
if self.gear["cpu"][0] > 0: if self.gear["cpu"][0] > 0:
cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor" cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor"
else: else:
cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor" cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor"
gov = "" with open(cpu_gov_path, "r") as f:
with open(cpu_gov_path,"r") as f: gov = f.read().strip() return f.read().strip()
return gov
def set_cpu_gov0( self,gov): @property
def gpu_gov(self) -> str:
with open(
"/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/governor", "r"
) as gov_file:
return gov_file.read().strip()
def set_cpu_gov0(self, gov):
cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor" cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor"
try: try:
subprocess.run( "echo %s | sudo tee %s " %(gov,cpu_gov_path),shell=True,stdout=subprocess.DEVNULL) subprocess.run(
except: "echo %s | sudo tee %s " % (gov, cpu_gov_path),
shell=True,
stdout=subprocess.DEVNULL,
)
except Exception:
print("set cpu governor failed") print("set cpu governor failed")
def set_cpu_gov4( self,gov): def set_cpu_gov4(self, gov):
cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor" cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor"
try: try:
subprocess.run( "echo %s | sudo tee %s" %(gov,cpu_gov_path),shell=True,stdout=subprocess.DEVNULL) subprocess.run(
except: "echo %s | sudo tee %s" % (gov, cpu_gov_path),
shell=True,
stdout=subprocess.DEVNULL,
)
except Exception:
print("set cpu governor failed") print("set cpu governor failed")
def get_cpu_on_off(self, cpu_num):
def get_cpu_on_off(self,cpu_num):
cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num
with open(cpu_onoff_file,"r") as f: onoff = f.read().strip() with open(cpu_onoff_file, "r") as f:
onoff = f.read().strip()
if onoff == "1": if onoff == "1":
cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num
with open(cpu_max_freq_file,"r") as f: max_freq = f.read().strip() with open(cpu_max_freq_file, "r") as f:
mhz = int(max_freq)/1000 max_freq = f.read().strip()
return "%dMhz" % mhz mhz = int(max_freq) // 1000
return f"{mhz} MHz"
return "OFF" return "OFF"
def set_cpu_on_off(self, cpu_num, onoff):
def set_cpu_on_off(self,cpu_num,onoff):
cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num
try: try:
#print("echo %d | sudo tee %s" %(onoff,cpu_onoff_file) ) subprocess.run(
subprocess.run( "echo %d | sudo tee %s" %(onoff,cpu_onoff_file),shell=True,stdout=subprocess.DEVNULL) "echo %d | sudo tee %s" % (onoff, cpu_onoff_file),
except: shell=True,
stdout=subprocess.DEVNULL,
)
except Exception:
print("set cpu %d on off failed" % cpu_num) print("set cpu %d on off failed" % cpu_num)
def set_cpu_max_freq(self,cpu_num,max_freq): def set_cpu_max_freq(self, cpu_num, max_freq):
cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num
try: try:
subprocess.run( "echo %d | sudo tee %s" %(max_freq,cpu_max_freq_file),shell=True,stdout=subprocess.DEVNULL) subprocess.run(
except: "echo %d | sudo tee %s" % (max_freq, cpu_max_freq_file),
shell=True,
stdout=subprocess.DEVNULL,
)
except Exception:
print("set cpu %d max freq failed" % cpu_num) print("set cpu %d max freq failed" % cpu_num)
def get_gpu_freq(self): def get_gpu_freq(self):
gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu" gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu"
gpu_freq_path = os.path.join(gpu_sys_path,"max_freq") gpu_freq_path = os.path.join(gpu_sys_path, "max_freq")
freq = "" with open(gpu_freq_path, "r") as f:
with open(gpu_freq_path,"r") as f: freq = f.read().strip() freq = f.read().strip()
mhz = int(freq)/1000000 mhz = int(freq) // 1000000
return "%dMHz" % mhz return f"{mhz} MHz"
def set_gpu(self,gov,hz): def set_gpu(self, gov, hz):
gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu" gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu"
gpu_gov_path = os.path.join(gpu_sys_path,"governor") gpu_gov_path = os.path.join(gpu_sys_path, "governor")
gpu_freq_path = os.path.join(gpu_sys_path,"max_freq") gpu_freq_path = os.path.join(gpu_sys_path, "max_freq")
try: try:
subprocess.run("echo %s | sudo tee %s" %(gov,gpu_gov_path),shell=True,stdout=subprocess.DEVNULL) subprocess.run(
subprocess.run("echo %d | sudo tee %s" %(hz, gpu_freq_path),shell=True,stdout=subprocess.DEVNULL) "echo %s | sudo tee %s" % (gov, gpu_gov_path), shell=True, stdout=subprocess.DEVNULL
except: )
subprocess.run(
"echo %d | sudo tee %s" % (hz, gpu_freq_path), shell=True, stdout=subprocess.DEVNULL
)
except Exception:
print("set gpu failed") print("set gpu failed")
def print_cpu_gpu_gov(self): def print_cpu_gpu_gov(self):
print("CPU Governor: %s GPU Governor: %s" % (self.get_cpu_gov(), self.gear["gpu_gov"])) print(f"CPU Governor: {self.cpu_gov} GPU Governor: {self.gpu_gov}")
def print_cur_status(self): def print_cur_status(self):
global cur_stat global cur_stat
stat_str = "|%s|%s|%s|%s|%s|%s|%s|" stat_str = "|%s|%s|%s|%s|%s|%s|%s|"
@ -199,11 +222,11 @@ class A06:
cpu3 = self.get_cpu_on_off(3).center(8)[:8] cpu3 = self.get_cpu_on_off(3).center(8)[:8]
cpu4 = self.get_cpu_on_off(4).center(8)[:8] cpu4 = self.get_cpu_on_off(4).center(8)[:8]
cpu5 = self.get_cpu_on_off(5).center(8)[:8] cpu5 = self.get_cpu_on_off(5).center(8)[:8]
gpu = self.get_gpu_freq().center(11)[:11] gpu = self.get_gpu_freq().center(11)[:11]
table_str = stat_str %(cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,gpu) table_str = stat_str % (cpu0, cpu1, cpu2, cpu3, cpu4, cpu5, gpu)
print("\nCurrent Status:") print("\nCurrent Status:")
for idx,val in enumerate(cur_stat): for idx, val in enumerate(cur_stat):
if idx == 5: if idx == 5:
print(table_str) print(table_str)
else: else:
@ -211,7 +234,7 @@ class A06:
self.print_cpu_gpu_gov() self.print_cpu_gpu_gov()
def set_gear(self,g): def set_gear(self, g):
self.gear = load_gear(g) self.gear = load_gear(g)
if g > 3: if g > 3:
@ -236,40 +259,45 @@ class A06:
self.set_cpu_gov4(self.cpu_scaling_governor) self.set_cpu_gov4(self.cpu_scaling_governor)
def print_gear_map(gear: int) -> NoReturn:
print(
" +-----------------------------------+-----------------+-----------+\n"
" | Cortex-A53 | Cortex-A72 | Mali-T860 |\n"
" +--------+--------+--------+--------+--------+--------+-----------+\n"
" | CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |\n"
"+---+--------+--------+--------+--------+--------+--------+-----------+"
)
def print_gear_map(gear): def freq(khz: int) -> str:
print(" +-----------------------------------+-----------------+-----------+") mhz = khz // 1000
print(" | Cortex-A53 | Cortex-A72 | Mali-T860 |")
print(" +--------+--------+--------+--------+--------+--------+-----------+")
print(" | CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |")
div = "+---+--------+--------+--------+--------+--------+--------+-----------+"
print(div)
def freq(khz):
mhz = khz/1000
if mhz >= 1000: if mhz >= 1000:
return "%d MHz" % mhz return f"{mhz} MHz"
elif mhz > 0: elif mhz > 0:
return " %d MHz" % mhz return f" {mhz} MHz"
else: else:
return " OFF " return " OFF "
for idx, val in enumerate(gears): for idx, val in enumerate(gears):
g = idx + 1 g = idx + 1
selected = g == gear selected = g == gear
print("|%s|%s| %s |%s" % ( print(
("*%s*" if selected else " %s ") % g, "|%s|%s| %s |%s"
"|".join([freq(cpu) for cpu in val["cpu"]]), % (
freq(val["gpu_freq"]/1000), ("*%s*" if selected else " %s ") % g,
" <===" if selected else "", "|".join([freq(cpu) for cpu in val["cpu"]]),
)) freq(val["gpu_freq"] // 1000),
print(div) " <===" if selected else "",
)
)
print("+---+--------+--------+--------+--------+--------+--------+-----------+")
def print_help_msg():
def print_help_msg() -> NoReturn:
print("Usage: devterm-a06-gearbox [OPTION]...") print("Usage: devterm-a06-gearbox [OPTION]...")
print("Show or set the CPU operating frequency,online status and GPU operating frequency for DevTerm A06.") print(
print() "Show or set the CPU operating frequency,online status and GPU operating frequency for DevTerm A06.\n"
print(" -s, --set [n] set a speed mode between the number 1-%d:" % len(gears)) )
print(f" -s, --set [n] set a speed mode between the number 1-{len(gears)}:")
for (i, _) in enumerate(gears): for (i, _) in enumerate(gears):
print(" %d for %s." % (i + 1, gears[i]["use"])) print(" %d for %s." % (i + 1, gears[i]["use"]))
print() print()
@ -278,43 +306,38 @@ def print_help_msg():
print("Set to mode 1, single LITTLE core @600MHz(max), GPU@200MHz.") print("Set to mode 1, single LITTLE core @600MHz(max), GPU@200MHz.")
print(" $ devterm-a06-gearbox -s 1") print(" $ devterm-a06-gearbox -s 1")
def is_root():
def is_root() -> bool:
return os.geteuid() == 0 return os.geteuid() == 0
def main(argv):
gear = 1 def main() -> SystemExit:
try: devterm = A06()
opts, args = getopt.getopt(argv,"hs:",["set="])
except getopt.GetoptError: parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("-s", "--set", type=int)
parser.add_argument("-h", "--help", action="store_true")
args = parser.parse_args()
if args.set:
gear = args.set
if gear not in range(1, len(gears) + 1):
print(f"Illegal input: mode range 1-{len(gears)}")
sys.exit(-1)
if is_root():
devterm.set_gear(gear)
print_gear_map(gear)
devterm.print_cpu_gpu_gov()
else:
print("Requires super user privilege to set mode, try running it with sudo.")
sys.exit(1)
elif args.help:
print_help_msg() print_help_msg()
sys.exit(2) sys.exit()
for opt, arg in opts:
if opt == '-h':
print_help_msg()
sys.exit()
elif opt in ("-s","--set"):
if(isDigit(arg)):
gear = int(arg)
if gear not in range(1, len(gears) + 1):
print("illegal input: mode range 1-%d" % len(gears))
sys.exit(-1)
DT = A06()
if len(argv) == 0:
DT.print_cur_status()
sys.exit(0)
DT = A06()
if is_root():
DT.set_gear(gear)
print_gear_map(gear)
DT.print_cpu_gpu_gov()
else: else:
print("Require super user privilege to set mode,try run it with sudo") if len(sys.argv) == 1:
sys.exit(1) devterm.print_cur_status()
sys.exit()
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) main()

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File