diff --git a/README.md b/README.md index 30aa140..3d9c122 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ There are three methods to install Parvaj: Use the AppImage bundle, use the Phar - GHDL - GTKWave -Having a GNU/Linux distribution, installing these should be easy. On Fedora 35, for example, you could simply do: +Having a GNU/Linux distribution, installing these should be easy. On Fedora 37, for example, you could simply do: ```bash sudo dnf install ghdl gtkwave @@ -78,7 +78,7 @@ Throughout this document, it is supposed you installed Parvaj using this method. The primary Parvaj command is `simulate`. It simulates a test-bench for you, given its name. Yes, it is really that simple! -For example, to simulate a test-bench named `test_multiplexer_2_to_1`, run: +For example, to simulate a test-bench named `test_multiplexer_2_to_1` (note that it's the name of the test-bench, not its file path), run: ```bash parvaj simulate test_multiplexer_2_to_1 @@ -92,7 +92,7 @@ Note that, for the `simulate` command to work, you must be in the project root, ### Options -You may also want to use some of the GHDL's simulation options, or the options provided by Parvaj. You can use the command `help` to see the list of available options: +You may also want to use some of the [GHDL's simulation options](https://ghdl.github.io/ghdl/using/Simulation.html#simulation-options), or the options provided by Parvaj. You can use the command `help` to see the list of available options: ```bash parvaj help simulate @@ -116,9 +116,11 @@ parvaj simulate --help parvaj simulate test_clock_generator -o stop-time=3ns -o vcd-nodate ``` + **Hint:** `stop-time` option is useful when your test-bench doesn't end in a finite period of time and could be run infinitely. In this case, you must inform GHDL to limit the simulation time to a specific period, e.g. 3ns; otherwise, the simulation (i.e. elab-running phase) will never stop. + ### Other Commands -Although Parvaj is designed to work mostly config-free, you can configure a few things: +Although Parvaj is designed to work mostly config-free, you can configure a few things using the `config` command: - `gtkwave.cmdline`: If set, this command is used to run GTKWave. This is useful if you want to use a different application for viewing waveforms, or having problems with the default invocation command. @@ -126,6 +128,29 @@ Although Parvaj is designed to work mostly config-free, you can configure a few - `ghdl.version`: GHDL version should be auto-detected, but this sets its major version. +#### Example + +Some MacOS users cannot invoke GTKWave directly from the command-line using `gtkwave` command. In this case, the fix is to use `open` command. + +You can set it like the following: + +```bash +parvaj config gtkwave.cmdline open +``` + +Want to make sure it was set? + +```bash +parvaj config gtkwave.cmdline +# Output: open +``` + +Want to unset it (i.e. reset it to the default value)? + +```bash +parvaj config gtkwave.cmdline "" +``` + ## Platform Support Tested platforms include: diff --git a/src/Command/ConfigCommand.php b/src/Command/ConfigCommand.php index 6df6096..41ffdfd 100644 --- a/src/Command/ConfigCommand.php +++ b/src/Command/ConfigCommand.php @@ -61,7 +61,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!isset($value)) { $output->writeln($config->get($name)); } else { - if (!empty($value)) { + if ($value !== "") { $config->set($name, $value); } else { $config->remove($name); diff --git a/src/Command/SimulateCommand.php b/src/Command/SimulateCommand.php index 51a477c..f4f5dd8 100644 --- a/src/Command/SimulateCommand.php +++ b/src/Command/SimulateCommand.php @@ -2,24 +2,19 @@ namespace MAChitgarha\Parvaj\Command; -use InvalidArgumentException; - use MAChitgarha\Component\Pusheh; use MAChitgarha\Parvaj\Config; -use MAChitgarha\Parvaj\Console\Application; use MAChitgarha\Parvaj\PathFinder; use MAChitgarha\Parvaj\DependencyResolver; use MAChitgarha\Parvaj\Runner\Ghdl\GhdlRunner; -use MAChitgarha\Parvaj\Runner\Ghdl\GhdlVersion; use MAChitgarha\Parvaj\Runner\Ghdl\ElabRunUserOptions; use MAChitgarha\Parvaj\Runner\Ghdl\GhdlRunnerFactory; use MAChitgarha\Parvaj\Runner\Gtkwave\GtkwaveRunnerFactory; use MAChitgarha\Parvaj\Util\ExecutableFinder; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -156,18 +151,10 @@ private function buildConfig( if (!$config->has(Config::KEY_GHDL_VERSION)) { $output->writeln("GHDL version not set, auto-detecting..."); - [$versionString, $majorVersion] = GhdlRunner::detectVersion($executableFinder); - $output->writeln("Detected GHDL version: $versionString"); - - try { - $config->setGhdlVersion(GhdlVersion::fromMajorVersion($majorVersion)); - } catch (InvalidArgumentException) { - throw new RuntimeException( - "The GHDL version is not supported yet" . PHP_EOL . - "Feel free to open an issue here: " . Application::ISSUES_PAGE_LINK - ); - } + $version = GhdlRunner::detectVersion($executableFinder); + $output->writeln("Detected GHDL version: {$version->getFull()}"); + $config->setGhdlVersion($version->getType()); $output->writeln(""); } diff --git a/src/Config.php b/src/Config.php index 49beff3..cf46db3 100644 --- a/src/Config.php +++ b/src/Config.php @@ -70,12 +70,12 @@ public static function isKeyValid(string $key): bool return \in_array($key, self::VALID_KEYS, true); } - public function getGhdlVersion(): int + public function getGhdlVersion(): string { - return (int)($this->get(self::KEY_GHDL_VERSION)); + return $this->get(self::KEY_GHDL_VERSION); } - public function setGhdlVersion(int $ghdlVersion): void + public function setGhdlVersion(string $ghdlVersion): void { $this->set(self::KEY_GHDL_VERSION, $ghdlVersion); } diff --git a/src/Console/Application.php b/src/Console/Application.php index 99f28db..c24a561 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -8,7 +8,7 @@ class Application extends \Symfony\Component\Console\Application { public const NAME = "Parvaj"; - public const VERSION = "0.5.0"; + public const VERSION = "0.5.1"; public const ISSUES_PAGE_LINK = "https://github.com/machitgarha/parvaj/issues"; diff --git a/src/Runner/Ghdl/GhdlRunner.php b/src/Runner/Ghdl/GhdlRunner.php index 47d95e6..8bbde51 100644 --- a/src/Runner/Ghdl/GhdlRunner.php +++ b/src/Runner/Ghdl/GhdlRunner.php @@ -2,6 +2,10 @@ namespace MAChitgarha\Parvaj\Runner\Ghdl; +use OutOfBoundsException; + +use MAChitgarha\Parvaj\Console\Application; + use MAChitgarha\Parvaj\Runner\OptionBuilder; use MAChitgarha\Parvaj\Util\Process; @@ -94,11 +98,7 @@ protected function getElabRunSimulationOptions(string $waveformFilePath, ElabRun ); } - /** - * @return array{0:string,1:int} Pair of GHDL raw version string, and the major - * version. - */ - public static function detectVersion(ExecutableFinder $executableFinder): array + public static function detectVersion(ExecutableFinder $executableFinder): GhdlVersion { $ghdlExecutable = $executableFinder->find("ghdl"); @@ -106,8 +106,22 @@ public static function detectVersion(ExecutableFinder $executableFinder): array $ghdlVersionProcess->run(); $output = $ghdlVersionProcess->getCompleteOutput(); - if (\preg_match("/GHDL +((\d+)\.\d+(\.\d+)?([\-_]dev)?)/i", $output, $match)) { - return [$match[1], (int)($match[2])]; + if (\preg_match("/GHDL +((\d+)\.(\d+)(\.(\d+))?([\-_](dev))?)/i", $output, $match)) { + $emptyToNull = fn($x) => $x !== "" ? $x : null; + try { + return new GhdlVersion( + $match[1], + (int)$match[2], + (int)$match[3], + (int)$emptyToNull($match[5]), + $emptyToNull($match[7]), + ); + } catch (OutOfBoundsException) { + throw new RuntimeException( + "The GHDL version is not supported yet" . PHP_EOL . + "Feel free to open an issue here: " . Application::ISSUES_PAGE_LINK + ); + } } throw new RuntimeException("Cannot detect GHDL version"); } diff --git a/src/Runner/Ghdl/GhdlRunnerFactory.php b/src/Runner/Ghdl/GhdlRunnerFactory.php index 4868ed8..d01a451 100644 --- a/src/Runner/Ghdl/GhdlRunnerFactory.php +++ b/src/Runner/Ghdl/GhdlRunnerFactory.php @@ -6,18 +6,22 @@ use MAChitgarha\Parvaj\Util\ExecutableFinder; +use Symfony\Component\Console\Exception\InvalidArgumentException; + class GhdlRunnerFactory { private const VERSION_TO_CLASS_MAPPER = [ - GhdlVersion::V0 => VersionSpecific\GhdlRunnerV0::class, - GhdlVersion::V1 => VersionSpecific\GhdlRunnerV1::class, - GhdlVersion::V2 => VersionSpecific\GhdlRunnerV2::class, - GhdlVersion::V3 => VersionSpecific\GhdlRunnerV3::class, + GhdlVersion::TYPE_0 => VersionSpecific\GhdlRunnerV0::class, + GhdlVersion::TYPE_1 => VersionSpecific\GhdlRunnerV1::class, ]; public static function create(ExecutableFinder $executableFinder, Config $config): GhdlRunner { - $ghdlRunnerClass = self::VERSION_TO_CLASS_MAPPER[$config->getGhdlVersion()]; + // @phan-suppress-next-line PhanTypeMismatchDimFetch + $ghdlRunnerClass = self::VERSION_TO_CLASS_MAPPER[$config->getGhdlVersion()] + ?? throw new InvalidArgumentException( + "GHDL version invalid or unsupported (config value set to '{$config->getGhdlVersion()}')" + ); return new $ghdlRunnerClass( $executableFinder->find("ghdl") diff --git a/src/Runner/Ghdl/GhdlVersion.php b/src/Runner/Ghdl/GhdlVersion.php index 6ff560c..e61bff3 100644 --- a/src/Runner/Ghdl/GhdlVersion.php +++ b/src/Runner/Ghdl/GhdlVersion.php @@ -2,28 +2,36 @@ namespace MAChitgarha\Parvaj\Runner\Ghdl; +use OutOfBoundsException; + class GhdlVersion { - public const V0 = 0; - public const V1 = 1; - public const V2 = 2; - public const V3 = 3; + public const TYPE_0 = "0"; + public const TYPE_1 = "1"; + + public function __construct( + private string $full, + private int $major, + private int $minor, + private ?int $patch, + private ?string $preRelease, + ) { + if (!(0 <= $major && $major <= 1)) { + throw new OutOfBoundsException("GHDL version not supported"); + } + } - private const LIST = [ - self::V0, - self::V1, - self::V2, - self::V3, - ]; + public function getFull(): string + { + return $this->full; + } - public static function fromMajorVersion(int $majorVersion): int + public function getType(): string { - return match ($majorVersion) { - 0 => self::V0, - 1 => self::V1, - 2 => self::V2, - 3 => self::V3, - default => throw new \InvalidArgumentException(), - }; + if ($this->major === 1 && $this->preRelease === "dev") { + return self::TYPE_0; + } else { + return (string)$this->major; + } } } diff --git a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV0.php b/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV0.php index 2b38874..d4c77b2 100644 --- a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV0.php +++ b/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV0.php @@ -4,6 +4,14 @@ use MAChitgarha\Parvaj\Runner\Ghdl\GhdlRunner; +use Symfony\Component\Filesystem\Path; + class GhdlRunnerV0 extends GhdlRunner { + protected function getElabRunGeneralOptions(string $testEntityName): array + { + return parent::getElabRunGeneralOptions($testEntityName) + [ + "o" => Path::join($this->workdir, $testEntityName) + ]; + } } diff --git a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV1.php b/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV1.php index 86c12ac..7c5f98d 100644 --- a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV1.php +++ b/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV1.php @@ -4,14 +4,6 @@ use MAChitgarha\Parvaj\Runner\Ghdl\GhdlRunner; -use Symfony\Component\Filesystem\Path; - class GhdlRunnerV1 extends GhdlRunner { - protected function getElabRunGeneralOptions(string $testEntityName): array - { - return parent::getElabRunGeneralOptions($testEntityName) + [ - "o" => Path::join($this->workdir, $testEntityName) - ]; - } } diff --git a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV2.php b/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV2.php deleted file mode 100644 index d8907d8..0000000 --- a/src/Runner/Ghdl/VersionSpecific/GhdlRunnerV2.php +++ /dev/null @@ -1,9 +0,0 @@ -