diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst
index 846696e14e..6e2da317e8 100644
--- a/doc/guides/tools/dts.rst
+++ b/doc/guides/tools/dts.rst
@@ -215,12 +215,16 @@ DTS is run with ``main.py`` located in the ``dts`` directory after entering Poet
 .. code-block:: console
 
    (dts-py3.10) $ ./main.py --help
-   usage: main.py [-h] [--config-file CONFIG_FILE] [--output-dir OUTPUT_DIR] [-t TIMEOUT] [-v] [-s] [--tarball TARBALL] [--compile-timeout COMPILE_TIMEOUT] [--test-cases TEST_CASES] [--re-run RE_RUN]
+   usage: main.py [-h] (--tarball FILEPATH | --revision ID) [--config-file CONFIG_FILE] [--output-dir OUTPUT_DIR] [-t TIMEOUT] [-v] [-s] [--compile-timeout COMPILE_TIMEOUT] [--test-cases TEST_CASES] [--re-run RE_RUN]
 
    Run DPDK test suites. All options may be specified with the environment variables provided in brackets. Command line arguments have higher priority.
 
    options:
    -h, --help            show this help message and exit
+   --tarball FILEPATH, --snapshot FILEPATH
+                         Path to DPDK source code tarball to test. (default: None)
+   --revision ID, --rev ID, --git-ref ID
+                         Git revision ID to test. Could be commit, tag, tree ID and vice versa. To test local changes, first commit them, then use their commit ID (default: None)
    --config-file CONFIG_FILE
                          [DTS_CFG_FILE] configuration file that describes the test cases, SUTs and targets. (default: ./conf.yaml)
    --output-dir OUTPUT_DIR, --output OUTPUT_DIR
@@ -229,8 +233,6 @@ DTS is run with ``main.py`` located in the ``dts`` directory after entering Poet
                          [DTS_TIMEOUT] The default timeout for all DTS operations except for compiling DPDK. (default: 15)
    -v, --verbose         [DTS_VERBOSE] Specify to enable verbose output, logging all messages to the console. (default: False)
    -s, --skip-setup      [DTS_SKIP_SETUP] Specify to skip all setup steps on SUT and TG nodes. (default: None)
-   --tarball TARBALL, --snapshot TARBALL, --git-ref TARBALL
-                         [DTS_DPDK_TARBALL] Path to DPDK source code tarball or a git commit ID, tag ID or tree ID to test. To test local changes, first commit them, then use the commit ID with this option. (default: dpdk.tar.xz)
    --compile-timeout COMPILE_TIMEOUT
                          [DTS_COMPILE_TIMEOUT] The timeout for compiling DPDK. (default: 1200)
    --test-cases TEST_CASES
diff --git a/dts/framework/settings.py b/dts/framework/settings.py
index 609c8d0e62..2d0365e763 100644
--- a/dts/framework/settings.py
+++ b/dts/framework/settings.py
@@ -76,7 +76,8 @@
 from pathlib import Path
 from typing import Any, TypeVar
 
-from .utils import DPDKGitTarball
+from .exception import ConfigurationError
+from .utils import DPDKGitTarball, get_commit_id
 
 _T = TypeVar("_T")
 
@@ -149,6 +150,26 @@ def __call__(
     return _EnvironmentArgument
 
 
+def _parse_tarball(filepath: str) -> Path:
+    """Validate if the filepath is valid and return a Path object."""
+    path = Path(filepath)
+    if not path.exists() or not path.is_file():
+        raise argparse.ArgumentTypeError(
+            "the file path provided is not a valid file")
+    return path
+
+
+def _parse_revision_id(rev_id: str) -> str:
+    """Retrieve effective commit ID from a revision ID. While validating it."""
+
+    try:
+        return get_commit_id(rev_id)
+    except ConfigurationError:
+        raise argparse.ArgumentTypeError(
+            "the Git revision ID supplied is invalid or ambiguous"
+        )
+
+
 @dataclass(slots=True)
 class Settings:
     """Default framework-wide user settings.
@@ -167,7 +188,7 @@ class Settings:
     #:
     skip_setup: bool = False
     #:
-    dpdk_tarball_path: Path | str = "dpdk.tar.xz"
+    dpdk_tarball_path: Path | str = ""
     #:
     compile_timeout: float = 1200
     #:
@@ -186,6 +207,28 @@ def _get_parser() -> argparse.ArgumentParser:
         formatter_class=argparse.ArgumentDefaultsHelpFormatter,
     )
 
+    dpdk_source = parser.add_mutually_exclusive_group(required=True)
+
+    dpdk_source.add_argument(
+        "--tarball",
+        "--snapshot",
+        action='store',
+        type=_parse_tarball,
+        help="Path to DPDK source code tarball to test.",
+        metavar="FILEPATH",
+    )
+
+    dpdk_source.add_argument(
+        "--revision",
+        "--rev",
+        "--git-ref",
+        type=_parse_revision_id,
+        metavar="ID",
+        help="Git revision ID to test. Could be commit, tag, tree ID and "
+        "vice versa. To test local changes, first commit them, then use their "
+        "commit ID",
+    )
+
     parser.add_argument(
         "--config-file",
         action=_env_arg("DTS_CFG_FILE"),
@@ -229,18 +272,6 @@ def _get_parser() -> argparse.ArgumentParser:
         help="[DTS_SKIP_SETUP] Specify to skip all setup steps on SUT and TG nodes.",
     )
 
-    parser.add_argument(
-        "--tarball",
-        "--snapshot",
-        "--git-ref",
-        action=_env_arg("DTS_DPDK_TARBALL"),
-        default=SETTINGS.dpdk_tarball_path,
-        type=Path,
-        help="[DTS_DPDK_TARBALL] Path to DPDK source code tarball or a git commit ID, "
-        "tag ID or tree ID to test. To test local changes, first commit them, "
-        "then use the commit ID with this option.",
-    )
-
     parser.add_argument(
         "--compile-timeout",
         action=_env_arg("DTS_COMPILE_TIMEOUT"),
@@ -283,9 +314,8 @@ def get_settings() -> Settings:
         verbose=parsed_args.verbose,
         skip_setup=parsed_args.skip_setup,
         dpdk_tarball_path=Path(
-            Path(DPDKGitTarball(parsed_args.tarball, parsed_args.output_dir))
-            if not os.path.exists(parsed_args.tarball)
-            else Path(parsed_args.tarball)
+            parsed_args.tarball or
+            DPDKGitTarball(parsed_args.revision, parsed_args.output_dir)
         ),
         compile_timeout=parsed_args.compile_timeout,
         test_cases=(parsed_args.test_cases.split(",") if parsed_args.test_cases else []),
diff --git a/dts/framework/utils.py b/dts/framework/utils.py
index cc5e458cc8..dbbec8b00a 100644
--- a/dts/framework/utils.py
+++ b/dts/framework/utils.py
@@ -70,6 +70,32 @@ def get_packet_summaries(packets: list[Packet]) -> str:
     return f"Packet contents: \n{packet_summaries}"
 
 
+def get_commit_id(rev_id: str) -> str:
+    """Given a Git revision ID, return the corresponding commit ID.
+
+    Args:
+        rev_id: The Git revision ID.
+
+    Raises:
+        ConfigurationError: The ``git rev-parse`` command failed. Suggesting
+            an invalid or ambiguous revision ID was supplied.
+    """
+    result = subprocess.run(
+        ["git", "rev-parse", "--verify", rev_id],
+        text=True,
+        capture_output=True,
+    )
+    if result.returncode != 0:
+        raise ConfigurationError(
+            f"{rev_id} is neither a path to an existing DPDK "
+            "archive nor a valid git reference.\n"
+            f"Command: {result.args}\n"
+            f"Stdout: {result.stdout}\n"
+            f"Stderr: {result.stderr}"
+        )
+    return result.stdout.strip()
+
+
 class StrEnum(Enum):
     """Enum with members stored as strings."""
 
@@ -170,7 +196,6 @@ def __init__(
 
         self._tarball_dir = Path(output_dir, "tarball")
 
-        self._get_commit_id()
         self._create_tarball_dir()
 
         self._tarball_name = (
@@ -180,21 +205,7 @@ def __init__(
         if not self._tarball_path:
             self._create_tarball()
 
-    def _get_commit_id(self) -> None:
-        result = subprocess.run(
-            ["git", "rev-parse", "--verify", self._git_ref],
-            text=True,
-            capture_output=True,
-        )
-        if result.returncode != 0:
-            raise ConfigurationError(
-                f"{self._git_ref} is neither a path to an existing DPDK "
-                "archive nor a valid git reference.\n"
-                f"Command: {result.args}\n"
-                f"Stdout: {result.stdout}\n"
-                f"Stderr: {result.stderr}"
-            )
-        self._git_ref = result.stdout.strip()
+
 
     def _create_tarball_dir(self) -> None:
         os.makedirs(self._tarball_dir, exist_ok=True)
