diff --git a/.gitignore b/.gitignore index 607bbf772..0e43bb224 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ ci_logs/ zig-out/ .zig-cache/ venv/ +target/ __pycache__ .*.sw* *.dtb diff --git a/.reuse/dep5 b/.reuse/dep5 index cbab57da7..cd64c0bf6 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -35,6 +35,7 @@ Files: drivers/timer/cdns/config.json drivers/timer/bcm2835/config.json drivers/timer/apb_timer/config.json + drivers/dvfs/Cargo.lock Copyright: UNSW License: BSD-2-Clause @@ -52,3 +53,7 @@ License: ISC Files: network/ipstacks/lwip/* Copyright: Copyright (c) 2001-2004 Swedish Institute of Computer Science License: LicenseRef-BSD-LWIP + +Files: support/targets/aarch64-sel4-microkit-minimal.json +Copyright: Copyright 2023, Colias Group, LLC +License: BSD-2-Clause diff --git a/docs/design/design.tex b/docs/design/design.tex index eadbc14d7..dcb267eec 100644 --- a/docs/design/design.tex +++ b/docs/design/design.tex @@ -2542,9 +2542,92 @@ \subsection{Status} This device-class specification is \textbf{subject to change}. +\section{Dynamic Voltage and Frequency Scaling}\label{s:DVFS} +Dynamic Voltage and Frequency Scaling(DVFS) is a power management technology +to adjust the frequency and the voltage applied on the processing units in runtime +depending on the actual needs. + +\subsection{Principle of the operation} +The power consumption of CMOS(Complementary Metal-Oxide-Semiconductor) circuits can +be defined by the following formula: + +\begin{equation} + P_{total} = P_{dynamic} + P_{static} +\end{equation} + +Where dynamic power can be roughly defined as: + +\begin{equation} + P_{dynamic} \approx C \cdot V^2 \cdot f +\end{equation} + +\begin{itemize} + \item \textbf{C}: Capacitance (fixed by hardware design). + \item \textbf{V}: Supply Voltage. + \item \textbf{f}: Clock Frequency. +\end{itemize} + +The power scales linearly with the frequency and quadratically with voltage. Lowering +the frequency, which usually allows a lower supply voltage, can dramatically lower +the power consumption as well as the generated heat. + +\subsection{Implementing DVFS on different platforms} +There are generally two ways to implement DVFS, and which one to use is heavily depended +on the hardware platform. + +\paragraph{Direct interact with the clock/regulator:} The DVFS driver directly interacts +with the clock and the regulator that control the frequency and the voltage the processing +units are operating on. This approach is used on most of the ARM or RISC-V platforms. + +\paragraph{Firmware Abstraction:} For some very new ARM platforms, System Control and +Management Interface (SCMI) is supported, providing a unified framework to manage the +the power state of the processing units. For x86 platforms, ACPI and HWP are the common +framework to provide platform agnostic power management interfaces. + +\subsection{Data Structures and Interface Specification} +The interface contains static configuration tables to describe the hardware. These are +exposed to the client at compile time. + +\paragraph{Operating Performance Point} +Describes a valid discrete state the processing unit can operate on. +\begin{lstlisting} +typedef struct { + uint64_t freq_hz; // Target Frequency in Hertz + uint64_t voltage_uv; // Target Voltage in Microvolts + uint64_t latency_ns; // Transition latency in Nanoseconds +} OppEntry; +\end{lstlisting} + +\paragraph{Core Configuration (CoreInfo)} +Describes the topology of the processor. The configuration describes +the number of the processing units, the clock source they are operating under +(one clock source may provide clock for multiple processing units, which means +changing the frequency for one unit would change the frequency of all the units +which has the same clock source), and the Operating Performance Point table. + +\begin{lstlisting} +typedef struct { + uint64_t core_ident; // Logical Core ID + uint64_t clock_source_ident; // ID of the clock source + const OppEntry *opptable; // Pointer to valid OPPs for this core + size_t opptable_len; // Number of OPPs +} CoreInfo; +\end{lstlisting} + +The interface for DVFS uses \gls{ppc}, and has the following +procedures that can be called: +\begin{description} + \item[\texttt{get\_freq(core\_identifier)}] + Retrieves the current actual frequency of a specific processing unit. + \item[\texttt{set\_freq(core\_identifier, frequency)}] + Sets the designated processing unit to a specific target frequency. The frequency + must be a valid value from the opp table for that specific core or the request + will be rejected. +\end{description} + \section{Sensors, PWM, GPIO, etc}\label{s:sensors} -These device classes are all very similar, and tend tend to be low +These device classes are all very similar, and tend to be low bandwidth --- transferring one bit to a few bytes at a time. They fall into the ``simple device'' category introduced in \autoref{s:dr-overview}; rather than diff --git a/drivers/dvfs/.cargo/config.toml b/drivers/dvfs/.cargo/config.toml new file mode 100644 index 000000000..15b397e4f --- /dev/null +++ b/drivers/dvfs/.cargo/config.toml @@ -0,0 +1,14 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause + +[build] +target = "../../support/targets/aarch64-sel4-microkit-minimal.json" + +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] +build-std-features = ["compiler-builtins-mem"] + +[net] +git-fetch-with-cli = true + +[target."aarch64-sel4-microkit-minimal"] \ No newline at end of file diff --git a/drivers/dvfs/Cargo.lock b/drivers/dvfs/Cargo.lock new file mode 100644 index 000000000..f5201acca --- /dev/null +++ b/drivers/dvfs/Cargo.lock @@ -0,0 +1,739 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufreq" +version = "0.1.0" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlmalloc" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6738d2e996274e499bc7b0d693c858b7720b9cd2543a0643a3087e6cb0a4fa16" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "dvfs_driver" +version = "0.1.0" +dependencies = [ + "cpufreq", + "sddf-ipc-types", + "sddf-rust", + "sel4-microkit", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "one-shot-mutex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb10614a03e671fcbb7f1421656788f9a761aab44563b83b07140c354fa9334" +dependencies = [ + "lock_api", +] + +[[package]] +name = "pest" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sddf-ipc-types" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sddf-rust" +version = "0.1.0" +dependencies = [ + "num_enum", + "sddf-ipc-types", + "sel4-microkit", +] + +[[package]] +name = "sel4" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", + "sel4-config", + "sel4-sys", +] + +[[package]] +name = "sel4-alloca" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "sel4-bitfield-ops" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "rustversion", +] + +[[package]] +name = "sel4-build-env" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-config" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sel4-config-data", + "sel4-config-macros", + "sel4-config-types", + "syn", +] + +[[package]] +name = "sel4-config-data" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "sel4-build-env", + "sel4-config-types", + "serde_json", +] + +[[package]] +name = "sel4-config-macros" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "fallible-iterator", + "proc-macro2", + "quote", + "sel4-config-data", + "sel4-config-types", + "syn", +] + +[[package]] +name = "sel4-config-types" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "serde", +] + +[[package]] +name = "sel4-ctors-dtors" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-dlmalloc" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "dlmalloc", + "lock_api", +] + +[[package]] +name = "sel4-elf-header" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-immediate-sync-once-cell" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-immutable-cell" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-initialize-tls" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", + "sel4-alloca", +] + +[[package]] +name = "sel4-microkit" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", + "one-shot-mutex", + "sel4", + "sel4-dlmalloc", + "sel4-immediate-sync-once-cell", + "sel4-microkit-base", + "sel4-microkit-macros", + "sel4-panicking", + "sel4-panicking-env", + "sel4-runtime-common", +] + +[[package]] +name = "sel4-microkit-base" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "sel4", + "sel4-immutable-cell", +] + +[[package]] +name = "sel4-microkit-macros" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sel4-panicking" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", + "sel4-immediate-sync-once-cell", + "sel4-panicking-env", + "unwinding", +] + +[[package]] +name = "sel4-panicking-env" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-runtime-common" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "cfg-if", + "sel4", + "sel4-ctors-dtors", + "sel4-elf-header", + "sel4-initialize-tls", + "sel4-panicking-env", + "sel4-stack", + "unwinding", +] + +[[package]] +name = "sel4-stack" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" + +[[package]] +name = "sel4-sys" +version = "0.1.0" +source = "git+https://github.com/seL4/rust-sel4.git?tag=v3.0.0#2c9786744900433f37e803c25ab208835a802cf3" +dependencies = [ + "bindgen", + "glob", + "log", + "pest", + "pest_derive", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "sel4-bitfield-ops", + "sel4-build-env", + "sel4-config", + "sel4-config-data", + "syn", + "xmltree", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unwinding" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60612c845ef41699f39dc8c5391f252942c0a88b7d15da672eff0d14101bbd6d" +dependencies = [ + "gimli", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmltree" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b619f8c85654798007fb10afa5125590b43b088c225a25fc2fec100a9fad0fc6" +dependencies = [ + "xml-rs", +] diff --git a/drivers/dvfs/Cargo.toml b/drivers/dvfs/Cargo.toml new file mode 100644 index 000000000..1066d1b7d --- /dev/null +++ b/drivers/dvfs/Cargo.toml @@ -0,0 +1,14 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause + +[package] +name = "dvfs_driver" +version = "0.1.0" +edition = "2024" +authors = ["Cheng Li 李澄 "] + +[dependencies] +cpufreq = { path = "cpufreq" } +sddf-rust = { path = "../../include/sddf-rust" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", tag = "v3.0.0" } +sddf-ipc-types = { git = "https://github.com/seL4/rust-sel4.git", tag = "v3.0.0" } \ No newline at end of file diff --git a/drivers/dvfs/cpufreq/Cargo.toml b/drivers/dvfs/cpufreq/Cargo.toml new file mode 100644 index 000000000..e94e8b4c3 --- /dev/null +++ b/drivers/dvfs/cpufreq/Cargo.toml @@ -0,0 +1,12 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause + +[package] +name = "cpufreq" +version = "0.1.0" +edition = "2024" +authors = ["Cheng Li 李澄 "] + +[lib] +name = "cpufreq" +path = "lib.rs" \ No newline at end of file diff --git a/drivers/dvfs/cpufreq/freq_trait.rs b/drivers/dvfs/cpufreq/freq_trait.rs new file mode 100644 index 000000000..9c456cdf2 --- /dev/null +++ b/drivers/dvfs/cpufreq/freq_trait.rs @@ -0,0 +1,48 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +#[derive(Debug)] +pub struct OppEntry { + pub freq_hz: u64, + pub voltage_uv: u64, + pub latency_ns: u64, +} + +#[derive(Debug)] +pub struct CoreInfo { + pub core_ident: u64, + pub clock_source_ident: u64, + pub opptable: &'static [OppEntry], +} + +#[derive(Debug)] +pub enum Error { + EINVAL = 0, +} + +pub trait FreqOps { + const CPU_OPP_TABLE: &[CoreInfo]; + const CORE_NUM: u32 = Self::CPU_OPP_TABLE.len() as u32; + + const _CHECK_OPP_TABLE_LEN: () = { + core::assert!( + Self::CORE_NUM as usize == Self::CPU_OPP_TABLE.len(), + "Mismatch between CORE_NUM and the actual length of OPP_TABLE" + ); + }; + + fn get_freq(core_ident: u64) -> Result { + core::panic!( + "DVFS is not implemented by get request for CPU{:?}", + core_ident + ); + } + + fn set_freq(core_ident: u64, freq_hz: u64) -> Result<(), Error> { + core::panic!( + "DVFS is not implemented by get request for CPU{:?}, freq {:?} cannot be set", + core_ident, + freq_hz + ); + } +} diff --git a/drivers/dvfs/cpufreq/lib.rs b/drivers/dvfs/cpufreq/lib.rs new file mode 100644 index 000000000..dfb1b09e6 --- /dev/null +++ b/drivers/dvfs/cpufreq/lib.rs @@ -0,0 +1,7 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +#![no_std] // Don't link the standard library + +pub mod freq_trait; +pub mod platforms; diff --git a/drivers/dvfs/cpufreq/platforms/mod.rs b/drivers/dvfs/cpufreq/platforms/mod.rs new file mode 100644 index 000000000..4de67f99d --- /dev/null +++ b/drivers/dvfs/cpufreq/platforms/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +pub mod xilinx; diff --git a/drivers/dvfs/cpufreq/platforms/xilinx.rs b/drivers/dvfs/cpufreq/platforms/xilinx.rs new file mode 100644 index 000000000..3682b779c --- /dev/null +++ b/drivers/dvfs/cpufreq/platforms/xilinx.rs @@ -0,0 +1,100 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +use core::ptr::{read_volatile, write_volatile}; + +use crate::freq_trait::{CoreInfo, Error, FreqOps, OppEntry}; + +// The memory address of ACPU controller +const ACPU_CTRL: usize = 0x00FD1A0060; +const ACPU_CTRL_DIVISOR_MASK: u32 = 0x3F00; + +// When uboot boots up, the APLL clock is set to 1199999988 +const APLL_CLK_FREQ: u32 = 1199999988; + +pub const OPP_TABLE: &[OppEntry] = &[ + OppEntry { + freq_hz: 1199999988, + voltage_uv: 1000000, + latency_ns: 500000, + }, + OppEntry { + freq_hz: 599999994, + voltage_uv: 1000000, + latency_ns: 500000, + }, + OppEntry { + freq_hz: 399999996, + voltage_uv: 1000000, + latency_ns: 500000, + }, + OppEntry { + freq_hz: 299999997, + voltage_uv: 1000000, + latency_ns: 500000, + }, +]; + +pub const ZCU102_CPU: &[CoreInfo] = &[ + CoreInfo { + core_ident: 0, + clock_source_ident: 0, + opptable: OPP_TABLE, + }, + CoreInfo { + core_ident: 1, + clock_source_ident: 0, + opptable: OPP_TABLE, + }, + CoreInfo { + core_ident: 2, + clock_source_ident: 0, + opptable: OPP_TABLE, + }, + CoreInfo { + core_ident: 3, + clock_source_ident: 0, + opptable: OPP_TABLE, + }, +]; + +pub struct Xilinx {} + +impl FreqOps for Xilinx { + const CPU_OPP_TABLE: &[CoreInfo] = ZCU102_CPU; + + fn get_freq(core_ident: u64) -> Result { + if core_ident >= Self::CORE_NUM as u64 { + return Err(Error::EINVAL); + } + + let divisor = (unsafe { read_volatile(ACPU_CTRL as *const u32) } & ACPU_CTRL_DIVISOR_MASK) + >> ACPU_CTRL_DIVISOR_MASK.trailing_zeros(); + + Ok((APLL_CLK_FREQ / divisor) as u64) + } + + fn set_freq(core_ident: u64, freq_hz: u64) -> Result<(), Error> { + if core_ident < Self::CORE_NUM as u64 + && let Some(entry) = ZCU102_CPU[core_ident as usize] + .opptable + .iter() + .find(|item| item.freq_hz == freq_hz) + { + let divisor = (APLL_CLK_FREQ as u64 / entry.freq_hz) as u32; + + unsafe { + let register_value = read_volatile(ACPU_CTRL as *const u32); + + write_volatile( + ACPU_CTRL as *mut u32, + register_value & (!ACPU_CTRL_DIVISOR_MASK) + | (divisor << ACPU_CTRL_DIVISOR_MASK.trailing_zeros()), + ); + } + return Ok(()); + } + + Err(Error::EINVAL) + } +} diff --git a/drivers/dvfs/rust-toolchain.toml b/drivers/dvfs/rust-toolchain.toml new file mode 100644 index 000000000..65b7a8554 --- /dev/null +++ b/drivers/dvfs/rust-toolchain.toml @@ -0,0 +1,23 @@ +# +# Copyright 2023, Colias Group, LLC +# +# SPDX-License-Identifier: BSD-2-Clause +# + +[toolchain] +channel = "nightly-2025-10-20" +profile = "default" +components = [ + "rust-src", + "rustc-dev", + "llvm-tools-preview", + "rust-analyzer", +] +targets = [ + "x86_64-unknown-linux-musl", + "aarch64-unknown-linux-musl", + "armv7-unknown-linux-musleabi", + "riscv64gc-unknown-linux-musl", + # tier 2, not available via rustup + # "riscv32gc-unknown-linux-musl", +] \ No newline at end of file diff --git a/drivers/dvfs/src/main.rs b/drivers/dvfs/src/main.rs new file mode 100644 index 000000000..7ab838d90 --- /dev/null +++ b/drivers/dvfs/src/main.rs @@ -0,0 +1,53 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +#![no_std] // Don't link the standard library +#![no_main] // Don't use the default entry point + +use core::convert::Infallible; + +use cpufreq::{freq_trait::FreqOps, platforms::xilinx::Xilinx}; +use sddf_ipc_types::{MessageWriter, ReadFromMessage}; +use sddf_rust::dvfs::dvfs::{DvfsReq, DvfsResp}; +use sel4_microkit::{ + Channel, Handler, MessageInfo, protection_domain, with_msg_regs, with_msg_regs_mut, +}; + +#[protection_domain] +fn init() -> impl Handler { + HandlerImpl {} +} + +struct HandlerImpl {} + +impl Handler for HandlerImpl { + type Error = Infallible; + fn protected( + &mut self, + _channel: Channel, + msg_info: MessageInfo, + ) -> Result { + let message = with_msg_regs(|buf| DvfsReq::read_from_message(msg_info.label(), buf)); + + let resp = match message { + Ok(req) => match req { + DvfsReq::GetFreq { core_ident } => match Xilinx::get_freq(core_ident) { + Ok(freq) => DvfsResp::GetFreq { freq_hz: freq }, + Err(_) => DvfsResp::Error, + }, + DvfsReq::SetFreq { + core_ident, + freq_hz, + } => match Xilinx::set_freq(core_ident, freq_hz) { + Ok(()) => DvfsResp::SetFreq, + Err(_) => DvfsResp::Error, + }, + }, + Err(_) => DvfsResp::Error, + }; + + let (label, count) = with_msg_regs_mut(|buf| resp.write_message(buf)).unwrap(); + + Ok(MessageInfo::new(label, count)) + } +} diff --git a/examples/dvfs/Makefile b/examples/dvfs/Makefile new file mode 100644 index 000000000..75aa8c7ae --- /dev/null +++ b/examples/dvfs/Makefile @@ -0,0 +1,32 @@ +# +# Copyright 2025, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(MICROKIT_BOARD)),) +$(error MICROKIT_BOARD must be specified) +endif +BUILD_DIR ?= build +override BUILD_DIR := $(abspath ${BUILD_DIR}) +export BUILD_DIR +export SDDF := $(abspath ../..) +override MICROKIT_SDK := $(abspath ${MICROKIT_SDK}) + +IMAGE_FILE := $(BUILD_DIR)/loader.img +REPORT_FILE := $(BUILD_DIR)/report.txt + +all: ${IMAGE_FILE} + +qemu ${IMAGE_FILE} ${REPORT_FILE} clean clobber: $(IMAGE_FILE) ${BUILD_DIR}/Makefile FORCE + ${MAKE} -C ${BUILD_DIR} MICROKIT_SDK=${MICROKIT_SDK} $(notdir $@) + +${BUILD_DIR}/Makefile: dvfs.mk + mkdir -p ${BUILD_DIR} + cp dvfs.mk ${BUILD_DIR}/Makefile + +FORCE: diff --git a/examples/dvfs/client.c b/examples/dvfs/client.c new file mode 100644 index 000000000..f2408195e --- /dev/null +++ b/examples/dvfs/client.c @@ -0,0 +1,68 @@ +/* + * Copyright 2025, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "microkit.h" +#include +#include +#include +#include +#include + +__attribute__((__section__(".timer_client_config"))) timer_client_config_t config; + +#define DVFS_CHANNEL 0 + +#define LOOP_LIMIT 5000000 + +void cpu_intensive_loop() +{ + volatile int i; + + for (i = 0; i < LOOP_LIMIT; i++) {} +} + +int test_cpu_freq(uint64_t core_freq) +{ + uint32_t res = sddf_dvfs_set_freq(DVFS_CHANNEL, CPU_INFO[0].core_ident, core_freq); + + if (res != SDDF_DVFS_SUCCESS) { + sddf_printf_("DVFS Client: Fail to get the frequency, Error: %d\n", res); + return 1; + } + + uint64_t freq = 0; + + res = sddf_dvfs_get_freq(DVFS_CHANNEL, CPU_INFO[0].core_ident, &freq); + + if (res != SDDF_DVFS_SUCCESS) { + sddf_printf_("DVFS Client: Fail to get the frequency, Error: %d\n", res); + return 1; + } + + sddf_printf_("\nDVFS Client: CURRENT FREQ: %lu\n", freq); + + uint64_t time_start = sddf_timer_time_now(config.driver_id); + + cpu_intensive_loop(); + + uint64_t time_end = sddf_timer_time_now(config.driver_id); + + sddf_printf_("DVFS Client: %lu ns was used for running the test\n", time_end - time_start); + + return 0; +} + +void init(void) +{ + test_cpu_freq(CPU_INFO[0].opptable[0].freq_hz); + + test_cpu_freq(CPU_INFO[0].opptable[1].freq_hz); + + test_cpu_freq(CPU_INFO[0].opptable[2].freq_hz); +} + +void notified(microkit_channel ch) +{ +} \ No newline at end of file diff --git a/examples/dvfs/dvfs.mk b/examples/dvfs/dvfs.mk new file mode 100644 index 000000000..98c06873c --- /dev/null +++ b/examples/dvfs/dvfs.mk @@ -0,0 +1,123 @@ +# +# Copyright 2025, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# +# This Makefile is copied into the build directory +# and operated on from there. +# + +ifeq ($(strip $(MICROKIT_SDK)),) +$(error MICROKIT_SDK must be specified) +endif + +ifeq ($(strip $(SDDF)),) +$(error SDDF must be specified) +endif + +ifeq ($(strip $(TOOLCHAIN)),) + TOOLCHAIN := clang +endif + +ifeq ($(strip $(TOOLCHAIN)), clang) + CC := clang + LD := ld.lld + AR := llvm-ar + RANLIB := llvm-ranlib + OBJCOPY := llvm-objcopy +else + CC := $(TOOLCHAIN)-gcc + LD := $(TOOLCHAIN)-ld + AS := $(TOOLCHAIN)-as + AR := $(TOOLCHAIN)-ar + RANLIB := $(TOOLCHAIN)-ranlib + OBJCOPY := $(TOOLCHAIN)-objcopy +endif + +IMAGE_FILE := loader.img +REPORT_FILE := report.txt +SYSTEM_FILE := dvfs.system + +ifeq ($(strip $(MICROKIT_BOARD)), zcu102) + CPU := cortex-a53 + TIMER_DRIVER_DIR := cdns +else +$(error Unsupported MICROKIT_BOARD given) +endif + +DTC := dtc +PYTHON ?= python3 + +BUILD_DIR ?= build +MICROKIT_CONFIG ?= debug + +TOP := ${SDDF}/examples/dvfs +CONFIGS_INCLUDE := ${TOP} + +MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit + +BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) +ARCH := ${shell grep 'CONFIG_SEL4_ARCH ' $(BOARD_DIR)/include/kernel/gen_config.h | cut -d' ' -f4} +SDDF_CUSTOM_LIBC := 1 + +IMAGES := dvfs.elf timer_driver.elf client.elf +CFLAGS := -nostdlib \ + -ffreestanding \ + -g3 \ + -O0 \ + -Wall -Wno-unused-function -Werror -Wno-unused-command-line-argument \ + -I$(BOARD_DIR)/include \ + -I$(SDDF)/include \ + -I$(SDDF)/include/microkit \ + -I$(CONFIGS_INCLUDE) +LDFLAGS := -L$(BOARD_DIR)/lib +LIBS := --start-group -lmicrokit -Tmicrokit.ld libsddf_util_debug.a --end-group + +ifeq ($(ARCH),aarch64) + CFLAGS += -mcpu=$(CPU) -target aarch64-none-elf +else ifeq ($(ARCH),riscv64) + CFLAGS += -march=rv64imafdc -target riscv64-none-elf +endif + +DTS := $(SDDF)/dts/$(MICROKIT_BOARD).dts +DTB := $(MICROKIT_BOARD).dtb +METAPROGRAM := $(TOP)/meta.py + +all: $(IMAGE_FILE) + +include ${SDDF}/drivers/timer/${TIMER_DRIVER_DIR}/timer_driver.mk + +include ${SDDF}/util/util.mk + +${IMAGES}: libsddf_util_debug.a + +microkit_sdk_config_dir := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) +sel4_include_dirs := $(microkit_sdk_config_dir)/include + +client.o: ${TOP}/client.c + $(CC) -c $(CFLAGS) $< -o client.o + +client.elf: client.o + $(LD) $(LDFLAGS) $< $(LIBS) -o $@ + +dvfs.elf: + @echo "Building dvfs.elf for board $(MICROKIT_BOARD)..." && \ + echo "MICROKIT SDK config directory: $(microkit_sdk_config_dir)" && \ + echo "SEl4 include directories: $(sel4_include_dirs)" && \ + cd ${SDDF}/drivers/dvfs && \ + SEL4_INCLUDE_DIRS=$(abspath $(sel4_include_dirs)) \ + cargo build \ + --target-dir $(BUILD_DIR) + @echo "Build complete: $(TARGET_ELF)" + cp ./aarch64-sel4-microkit-minimal/debug/dvfs_driver.elf $(BUILD_DIR) + +$(DTB): $(DTS) + dtc -q -I dts -O dtb $(DTS) > $(DTB) + +$(SYSTEM_FILE): $(METAPROGRAM) $(IMAGES) $(DTB) + $(PYTHON) $(METAPROGRAM) --sddf $(SDDF) --board $(MICROKIT_BOARD) --dtb $(DTB) --output . --sdf $(SYSTEM_FILE) $(PARTITION_ARG) + $(OBJCOPY) --update-section .device_resources=timer_driver_device_resources.data timer_driver.elf + $(OBJCOPY) --update-section .timer_client_config=timer_client_client.data client.elf + +$(IMAGE_FILE) $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) + $(MICROKIT_TOOL) $(SYSTEM_FILE) --search-path $(BUILD_DIR) --board $(MICROKIT_BOARD) --config $(MICROKIT_CONFIG) -o $(IMAGE_FILE) -r $(REPORT_FILE) diff --git a/examples/dvfs/meta.py b/examples/dvfs/meta.py new file mode 100644 index 000000000..ae69e3af9 --- /dev/null +++ b/examples/dvfs/meta.py @@ -0,0 +1,84 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause +import argparse +from typing import List +from dataclasses import dataclass +from sdfgen import SystemDescription, Sddf, DeviceTree +from importlib.metadata import version + +assert version("sdfgen").split(".")[1] == "27", "Unexpected sdfgen version" + +ProtectionDomain = SystemDescription.ProtectionDomain +MemoryRegion = SystemDescription.MemoryRegion +Map = SystemDescription.Map +Channel = SystemDescription.Channel + + +@dataclass +class Board: + name: str + arch: SystemDescription.Arch + paddr_top: int + timer: str + + +BOARDS: List[Board] = [ + Board( + name="zcu102", + arch=SystemDescription.Arch.AARCH64, + paddr_top=0xA0000000, + timer="axi/timer@ff140000", + ), +] + + +def generate(sdf_file: str, output_dir: str, dtb: DeviceTree): + timer_driver = ProtectionDomain("timer_driver", "timer_driver.elf", priority=254) + + dvfs_driver = ProtectionDomain("dvfs_driver", "dvfs_driver.elf", priority=100) + + client = ProtectionDomain("client", "client.elf", priority=1) + + clk_mr = MemoryRegion(sdf, "clk", 0x1000, paddr=0xFD1A0000) + dvfs_driver.add_map(Map(clk_mr, 0xFD1A0000, "rw", cached=False)) + sdf.add_mr(clk_mr) + + timer_node = dtb.node(board.timer) + assert timer_node is not None + + timer_system = Sddf.Timer(sdf, timer_node, timer_driver) + timer_system.add_client(client) + + dvfs_ch = Channel(client, dvfs_driver, pp_a=True) + sdf.add_channel(dvfs_ch) + + pds = [timer_driver, client, dvfs_driver] + for pd in pds: + sdf.add_pd(pd) + + assert timer_system.connect() + assert timer_system.serialise_config(output_dir) + + with open(f"{output_dir}/{sdf_file}", "w+") as f: + f.write(sdf.render()) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dtb", required=True) + parser.add_argument("--sddf", required=True) + parser.add_argument("--board", required=True, choices=[b.name for b in BOARDS]) + parser.add_argument("--output", required=True) + parser.add_argument("--sdf", required=True) + + args = parser.parse_args() + + board = next(filter(lambda b: b.name == args.board, BOARDS)) + + sdf = SystemDescription(board.arch, board.paddr_top) + sddf = Sddf(args.sddf) + + with open(args.dtb, "rb") as f: + dtb = DeviceTree(f.read()) + + generate(args.sdf, args.output, dtb) diff --git a/include/sddf-rust/Cargo.toml b/include/sddf-rust/Cargo.toml new file mode 100644 index 000000000..4e509dce6 --- /dev/null +++ b/include/sddf-rust/Cargo.toml @@ -0,0 +1,15 @@ +# Copyright 2025, UNSW +# SPDX-License-Identifier: BSD-2-Clause + +[package] +name = "sddf-rust" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" + +[dependencies] +sddf-ipc-types = { git = "https://github.com/seL4/rust-sel4.git", tag = "v3.0.0" } +sel4-microkit = { git = "https://github.com/seL4/rust-sel4.git", tag = "v3.0.0" } +num_enum = { version = "0.7.5", default-features = false } \ No newline at end of file diff --git a/include/sddf-rust/dvfs/dvfs.rs b/include/sddf-rust/dvfs/dvfs.rs new file mode 100644 index 000000000..22228fa89 --- /dev/null +++ b/include/sddf-rust/dvfs/dvfs.rs @@ -0,0 +1,94 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +use core::convert::Infallible; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use sddf_ipc_types::{ + MessagParseError, MessageBuilder, MessageParser, MessageWriter, ReadFromMessage, +}; + +#[derive(Debug)] +pub enum DvfsReq { + GetFreq { core_ident: u64 }, + SetFreq { core_ident: u64, freq_hz: u64 }, +} + +#[derive(IntoPrimitive, TryFromPrimitive)] +#[cfg_attr(target_pointer_width = "32", repr(u32))] +#[cfg_attr(target_pointer_width = "64", repr(u64))] +#[derive(Debug)] +enum DvfsMessageLabel { + GetFreq = 0, + SetFreq = 1, +} + +#[derive(Debug)] +pub enum DvfsResp { + Error, + GetFreq { freq_hz: u64 }, + SetFreq, +} + +#[derive(IntoPrimitive, TryFromPrimitive)] +#[cfg_attr(target_pointer_width = "32", repr(u32))] +#[cfg_attr(target_pointer_width = "64", repr(u64))] +#[derive(Debug)] +enum DvfsRespLabel { + Success = 0, + Error = 1, +} + +impl ReadFromMessage for DvfsReq { + type Error = MessagParseError; + + fn read_from_message( + label: sddf_ipc_types::MessageLabel, + buf: &[sddf_ipc_types::MessageRegisterValue], + ) -> Result { + let parser = MessageParser::new(label, buf); + Ok(match parser.label_try_into()? { + DvfsMessageLabel::GetFreq => { + let mut i = 0..; + let core_ident = parser.get_mr(i.next().unwrap())?; + DvfsReq::GetFreq { core_ident } + } + DvfsMessageLabel::SetFreq => { + let mut i = 0..; + let core_ident = parser.get_mr(i.next().unwrap())?; + let freq_hz = parser.get_mr(i.next().unwrap())?; + DvfsReq::SetFreq { + core_ident, + freq_hz, + } + } + }) + } +} + +impl MessageWriter for DvfsResp { + type Error = Infallible; + + fn write_message( + &self, + buf: &mut [sddf_ipc_types::MessageRegisterValue], + ) -> Result<(sddf_ipc_types::MessageLabel, usize), ::Error> { + let mut builder = MessageBuilder::new(buf); + + let mut i = 0..; + match self { + DvfsResp::Error => { + builder.set_mr(i.next().unwrap(), DvfsRespLabel::Error as u64); + } + DvfsResp::GetFreq { freq_hz } => { + builder.set_mr(i.next().unwrap(), DvfsRespLabel::Success as u64); + builder.set_mr(i.next().unwrap(), *freq_hz); + } + DvfsResp::SetFreq => { + builder.set_mr(i.next().unwrap(), DvfsRespLabel::Success as u64); + } + } + + Ok(builder.build()) + } +} diff --git a/include/sddf-rust/dvfs/mod.rs b/include/sddf-rust/dvfs/mod.rs new file mode 100644 index 000000000..c1eadc1a6 --- /dev/null +++ b/include/sddf-rust/dvfs/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +pub mod dvfs; diff --git a/include/sddf-rust/lib.rs b/include/sddf-rust/lib.rs new file mode 100644 index 000000000..b1f2b84a4 --- /dev/null +++ b/include/sddf-rust/lib.rs @@ -0,0 +1,6 @@ +// Copyright 2025, UNSW +// SPDX-License-Identifier: BSD-2-Clause + +#![no_std] // Don't link the standard library + +pub mod dvfs; diff --git a/include/sddf/dvfs/client.h b/include/sddf/dvfs/client.h new file mode 100644 index 000000000..a4e331440 --- /dev/null +++ b/include/sddf/dvfs/client.h @@ -0,0 +1,106 @@ +/* + * Copyright 2025, UNSW + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +/** + * The struct that describes an operating performance point table entry. + */ +typedef struct { + // Target Frequency in Hertz + uint64_t freq_hz; + // Target Voltage in Microvolts + uint64_t voltage_uv; + // Transition latency in Nanoseconds + uint64_t latency_ns; +} OppEntry; + +/** + * The struct that describes one CPU core. + * It includes the identifier of the core, + * the identifier of the clock that controls the core, + * and the operating performance point table that describe the + * frequency and the voltage that the core can be operating under. + */ +typedef struct { + // Logical Core ID + uint64_t core_ident; + // ID of the clock source + uint64_t clock_source_ident; + // Pointer to valid OPPs for this core + const OppEntry *opptable; + // Number of the OPPs + size_t opptable_len; +} CoreInfo; + +#ifdef CONFIG_PLAT_ZYNQMP_ZCU102 +const OppEntry OPP_TABLE[] = { + { .freq_hz = 1199999988, .voltage_uv = 1000000, .latency_ns = 500000 }, + { .freq_hz = 599999994, .voltage_uv = 1000000, .latency_ns = 500000 }, + { .freq_hz = 399999996, .voltage_uv = 1000000, .latency_ns = 500000 }, + { .freq_hz = 299999997, .voltage_uv = 1000000, .latency_ns = 500000 }, +}; + +const size_t OPP_TABLE_LEN = sizeof(OPP_TABLE) / sizeof(OPP_TABLE[0]); + +const CoreInfo CPU_INFO[] = { + { .core_ident = 0, .clock_source_ident = 0, .opptable = OPP_TABLE, .opptable_len = OPP_TABLE_LEN }, + { .core_ident = 1, .clock_source_ident = 0, .opptable = OPP_TABLE, .opptable_len = OPP_TABLE_LEN }, + { .core_ident = 2, .clock_source_ident = 0, .opptable = OPP_TABLE, .opptable_len = OPP_TABLE_LEN }, + { .core_ident = 3, .clock_source_ident = 0, .opptable = OPP_TABLE, .opptable_len = OPP_TABLE_LEN }, +}; + +const size_t CPU_INFO_LEN = sizeof(CPU_INFO) / sizeof(CPU_INFO[0]); +#endif + +#define SDDF_DVFS_GET_FREQ 0 +#define SDDF_DVFS_SET_FREQ 1 + +#define SDDF_DVFS_SUCCESS 0 +#define SDDF_DVFS_RESPONSE_ERROR 1 + +/** + * Get the frequency of a specific CPU core via PPC into the DVFS driver. + * Use the label to indicate this request. + * A return value of zero means the request is completed successfully. + * @param channel ID of the DVFS driver. + * @param core_ident The unique identifier of the CPU core. + * @param freq A pointer to a unsighed integer to pass back the returned frequency. + */ +static inline int32_t sddf_dvfs_get_freq(unsigned int channel, uint64_t core_ident, uint64_t *freq) +{ + sddf_set_mr(0, core_ident); + + sddf_ppcall(channel, seL4_MessageInfo_new(SDDF_DVFS_GET_FREQ, 0, 0, 1)); + + uint32_t error = sddf_get_mr(0); + if (error == SDDF_DVFS_SUCCESS) { + *freq = sddf_get_mr(1); + } + return error; +} + +/** + * Set the frequency of a specific CPU core via PPC into the DVFS driver. + * Use the label to indicate this request. + * A return value of zero means the request is completed successfully. + * @param channel ID of the DVFS driver. + * @param core_ident The unique identifier of the CPU core. + * @param freq The desired core frequency. + */ +static inline int32_t sddf_dvfs_set_freq(unsigned int channel, uint64_t core_ident, uint64_t freq) +{ + sddf_set_mr(0, core_ident); + sddf_set_mr(1, freq); + + sddf_ppcall(channel, seL4_MessageInfo_new(SDDF_DVFS_SET_FREQ, 0, 0, 2)); + + return sddf_get_mr(0); +} \ No newline at end of file diff --git a/support/targets/aarch64-sel4-microkit-minimal.json b/support/targets/aarch64-sel4-microkit-minimal.json new file mode 100644 index 000000000..5f35f8c45 --- /dev/null +++ b/support/targets/aarch64-sel4-microkit-minimal.json @@ -0,0 +1,39 @@ +{ + "arch": "aarch64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32", + "disable-redzone": true, + "exe-suffix": ".elf", + "features": "+v8a,+strict-align,+neon,+fp-armv8", + "link-script": "__sel4_ipc_buffer_obj = (__ehdr_start & ~(4096 - 1)) - 4096;", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-target": "aarch64-unknown-none", + "max-atomic-width": 128, + "metadata": { + "description": null, + "host_tools": null, + "std": null, + "tier": null + }, + "panic-strategy": "abort", + "pre-link-args": { + "gnu": [ + "--fix-cortex-a53-843419" + ], + "gnu-lld": [ + "--fix-cortex-a53-843419", + "-z", + "max-page-size=4096" + ] + }, + "relocation-model": "static", + "stack-probes": { + "kind": "inline" + }, + "supported-sanitizers": [ + "kcfi", + "kernel-address" + ], + "target-pointer-width": 64 +} \ No newline at end of file