diff --git a/README.md b/README.md index 207f8f21d..8dfc36702 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ 'icestudio' workflow status

-Visual editor for Verilog designs, built on top of [Icestorm](http://www.clifford.at/icestorm/) and -[Apio](https://github.com/FPGAwars/apio). +Visual editor for Verilog designs, built on top of [OSS CAD Suite](https://github.com/YosysHQ/oss-cad-suite-build). Find installation guidelines, user guide and further information at [juanmard.github.io/icestudio](https://juanmard.github.io/icestudio). @@ -23,4 +22,44 @@ Find installation guidelines, user guide and further information at

-**IMPORTANT: Since June 2021, several enhancements available in this variant are being applied [upstream](https://github.com/FPGAwars/icestudio). Therefore, the development of this fork is on hold until the dependencies are stabilized. Find further details in the [WIKI](https://github.com/juanmard/icestudio/wiki).** +**IMPORTANT: Since June 2021, several enhancements available in this variant are being applied [upstream](https://github.com/FPGAwars/icestudio). Find further details in the [WIKI](https://github.com/juanmard/icestudio/wiki).** + +--- + +Unlike the [upstream](https://github.com/FPGAwars/icestudio), _Icestudio Nightly_ is agnostic to the toolchain +installation solution and it does not require admin/sudo permissions. +Users are free to choose between [OSS CAD Suite](https://github.com/YosysHQ/oss-cad-suite-build), system packages, Conda environments, [apio](https://github.com/FPGAwars/apio), [containers](https://hdl.github.io/containers/), etc. as their +preferred solution for getting the required tools and making them available in the PATH. +See [hdl/packages](https://github.com/hdl/packages). +By the same token, the usage of virtual environments is optional, although recommended when using Python based packaging +systems such as Conda or apio. + +Furthermore, _Icestudio Nightly_ uses `ICETool` by default, instead of `apio`. +[ICETool](tools/ICETool) is a Python script that allows translating `verify`, `build` and `upload` commands from +Icestudio into the entrypoints provided by [FuseSoC](https://github.com/olofk/FuseSoC)/[Edalize](https://github.com/olofk/edalize/), +apio, or any other EDA workflow provider. +See [Electronic Design Automation Abstraction (EDA²)](https://edaa-org.github.io/). + +Currently, ICETool is in an early development stage and it is not published through PyPI. +Therefore, the location of the script needs to be made available before starting Icestudio. + +On GNU/Linux or MSYS2, add subdir `tools` to the PATH: + +```sh +PATH=$(pwd)/tools:$PATH yarn start +``` + +On the Windows CMD, use PYTHONPATH: + +```sh +PYTHONPATH=$(pwd)/tools:$PATH yarn start +``` + +Moreover, environment variable `ICETOOL_CMD` allows overriding the backend. + +```sh +# Use apio +ICETOOL_CMD=apio PATH=$(pwd)/tools:$PATH yarn start +# Use edalize +ICETOOL_CMD=edalize PATH=$(pwd)/tools:$PATH yarn start +``` diff --git a/app.js b/app.js index 397020d3c..83815e867 100644 --- a/app.js +++ b/app.js @@ -139,7 +139,6 @@ angular utils.selectBoard(_board); } - tools.checkToolchain(); setTimeout(utils.endWait, 1500); }); diff --git a/controllers/preferences.js b/controllers/preferences.js index 49f776dc8..14247caab 100644 --- a/controllers/preferences.js +++ b/controllers/preferences.js @@ -39,10 +39,6 @@ angular icon: 'snowflake-o', title: 'UI Theme', }, - toolchain: { - icon: 'gear', - title: 'Toolchain', - }, }; $scope.done = () => { diff --git a/docs/index.rst b/docs/index.rst index 9bb6f5336..fa6ff6e0a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,7 +19,6 @@ Icestudio v1 (nightly) quickstart/installation quickstart/selectboard - quickstart/toolchain quickstart/drivers quickstart/test quickstart/project diff --git a/docs/quickstart/drivers.rst b/docs/quickstart/drivers.rst index 89ee9a7b2..6c106e930 100644 --- a/docs/quickstart/drivers.rst +++ b/docs/quickstart/drivers.rst @@ -3,17 +3,11 @@ Install/configure the drivers ----------------------------- -Then, for configuring drivers, connect your board and select **Enable** in *toolchain* section. This operation requires **administrator privileges**. +On Windows, an external application (`Zadig `_) is used for boards with FTDI devices. +It replaces the existing FTDI driver of the **Interface 0** with **libusbK**. -.. attention:: +.. image:: ../_static/img/zadig.png + :width: 500 px + :align: center - On Windows, an external application (`Zadig `_) is used for boards with FTDI devices. - It replaces the existing FTDI driver of the **Interface 0** with **libusbK**. - - .. image:: ../_static/img/zadig.png - :width: 500 px - :align: center - - | - - On macOS, this operation requires internet connection to allow *Homebrew* to install ``libffi`` and ``libftdi`` packages. +On macOS, this operation requires internet connection to allow *Homebrew* to install ``libffi`` and ``libftdi`` packages. diff --git a/docs/quickstart/toolchain.rst b/docs/quickstart/toolchain.rst deleted file mode 100644 index 0802e50e1..000000000 --- a/docs/quickstart/toolchain.rst +++ /dev/null @@ -1,21 +0,0 @@ -.. _quickstart:toolchain: - -Install/configure the toolchain -------------------------------- - -For configuring the built-in toolchain of custom statically built binaries, go to **Edit > Preferences** and select *install*: - -.. image:: ../_static/img/start_toolchain.png - :width: 500 px - :align: center - -The latest stable release of `Apio `_ and all its built-in packages will be installed. - -.. IMPORTANT:: Some boards are not supported in the stable releases of Apio, but are available in the development branch. - You might need to install/update Apio from the git repository. - - NOTE: on GNU/Linux, first ``source ~/.icestudio/venv/bin/activate``. - - .. code-block:: shell - - pip install -U git+https://github.com/FPGAwars/apio.git@develop#egg=apio diff --git a/index.html b/index.html index e87a51760..16af5a388 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,6 @@ - diff --git a/package.json b/package.json index a33aa49f4..de2d05048 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "icestudio", - "version": "0.9.0dev", + "version": "0.12.0dev", "sha": "00000000", "description": "Visual editor for Verilog designs", "repository": "https://github.com/juanmard/icestudio", diff --git a/services/common.js b/services/common.js index 00e318fb9..e5834c2ca 100644 --- a/services/common.js +++ b/services/common.js @@ -73,25 +73,12 @@ angular this.ICESTUDIO_DIR, 'collections' ); - this.APIO_HOME_DIR = nodePath.join(this.ICESTUDIO_DIR, 'apio'); const _PROFILE_PATH = nodePath.join(this.ICESTUDIO_DIR, 'profile.json'); this.PROFILE_PATH = _PROFILE_PATH; this.APP_DIR = nodePath.dirname(process.execPath); - //-- Folder name for the virtual environment - this.ENV_DIR = nodePath.join(this.ICESTUDIO_DIR, 'venv'); - this.ENV_BIN_DIR = nodePath.join( - this.ENV_DIR, - this.WIN32 && !this.MSYSTEM ? 'Scripts' : 'bin' - ); - this.ENV_APIO = nodePath.join( - this.ENV_BIN_DIR, - this.WIN32 ? 'apio.exe' : 'apio' - ); - this.APIO_CMD = this.WIN32 - ? `set APIO_HOME_DIR="${this.APIO_HOME_DIR}"& "${this.ENV_APIO}"` - : `export APIO_HOME_DIR="${this.APIO_HOME_DIR}"; "${this.ENV_APIO}"`; + this.ICETOOL = 'ICETool'; const nodeTmp = require('tmp'); @@ -198,8 +185,6 @@ angular // Profile const _data = { - apioRepo: 'juanmard/icestudio', - apioRef: 'apio-dev', board: null, prog: null, boardRules: true, diff --git a/services/compiler.js b/services/compiler.js index 3a5db9d7e..ca7ffd445 100644 --- a/services/compiler.js +++ b/services/compiler.js @@ -698,13 +698,15 @@ angular code += module(data); } + code += '\n'; + // Dependencies modules if (typeof project.package !== 'undefined') { - code += '\n/*-------------------------------------------------*/\n'; - code += '/*-- ' + project.package.name + ' */\n'; - code += '/*-- - - - - - - - - - - - - - - - - - - - - - - --*/\n'; - code += '/*-- ' + project.package.description + '\n'; - code += '/*-------------------------------------------------*/\n'; + code += '//---------------------------------------------------\n'; + code += '//-- ' + project.package.name + '\n'; + code += '//-- - - - - - - - - - - - - - - - - - - - - - - - --\n'; + code += '//-- ' + project.package.description + '\n'; + code += '//---------------------------------------------------\n'; } for (var d in dependencies) { code += verilogCompiler(utils.digestId(d), dependencies[d]); diff --git a/services/drivers.js b/services/drivers.js deleted file mode 100644 index bb6a34ad2..000000000 --- a/services/drivers.js +++ /dev/null @@ -1,472 +0,0 @@ -angular - .module('icestudio') - .service( - 'drivers', - function ( - $rootScope, - alerts, - common, - gettextCatalog, - gui, - nodeChildProcess, - utils - ) { - 'use strict'; - - function _tcStr(str, args) { - return gettextCatalog.getString(str, args); - } - - this.enable = function () { - switch (common.selectedProgrammer) { - case 'FTDI': - enableDriversFTDI(); - break; - case 'Serial': - enableDriversSerial(); - break; - default: - console.warn('No valid selected board programmer'); - } - }; - - this.disable = function () { - switch (common.selectedProgrammer) { - case 'FTDI': - disableDriversFTDI(); - break; - case 'Serial': - disableDriversSerial(); - break; - default: - console.warn('No valid selected board programmer'); - } - }; - - function enableDriversFTDI() { - if (common.WIN32) { - enableWindowsDriversFTDI(); - } else if (common.DARWIN) { - enableDarwinDriversFTDI(); - } else { - enableLinuxDriversFTDI(); - } - } - - function disableDriversFTDI() { - if (common.WIN32) { - disableWindowsDriversFTDI(); - } else if (common.DARWIN) { - disableDarwinDriversFTDI(); - } else { - disableLinuxDriversFTDI(); - } - } - - function enableDriversSerial() { - if (common.WIN32) { - enableWindowsDriversSerial(); - } else if (common.DARWIN) { - enableDarwinDriversSerial(); - } else { - enableLinuxDriversSerial(); - } - } - - function disableDriversSerial() { - if (common.WIN32) { - disableWindowsDriversSerial(); - } else if (common.DARWIN) { - disableDarwinDriversSerial(); - } else { - disableLinuxDriversSerial(); - } - } - - this.preUpload = function (callback) { - if (common.DARWIN) { - preUploadDarwin(callback); - } else if (callback) { - callback(); - } - }; - - this.postUpload = function () { - if (common.DARWIN) { - postUploadDarwin(); - } - }; - - /* - * Linux drivers - */ - - function enableLinuxDriversFTDI() { - var rules = ''; - rules += 'ATTRS{idVendor}==\\"0403\\", ATTRS{idProduct}==\\"6010\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - rules += 'ATTRS{idVendor}==\\"0403\\", ATTRS{idProduct}==\\"6014\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - rules += 'ATTRS{idVendor}==\\"0403\\", ATTRS{idProduct}==\\"6015\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - rules += 'ATTRS{idVendor}==\\"1209\\", ATTRS{idProduct}==\\"5af0\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - rules += 'ATTRS{idVendor}==\\"1209\\", ATTRS{idProduct}==\\"5bf0\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - //-- ulx3s board - rules += 'ATTRS{idVendor}==\\"0403\\", ATTRS{idProduct}==\\"6015\\", '; - rules += 'MODE=\\"666\\", GROUP=\\"dialout\\"\n'; - - rules += 'ATTRS{idVendor}==\\"0403\\", ATTRS{idProduct}==\\"6015\\", '; - rules += 'MODE=\\"664\\", GROUP=\\"dialout\\", SUBSYSTEM=="tty"\n'; - - //-- Icesugar board - rules += 'ATTRS{idVendor}==\\"1d50\\", ATTRS{idProduct}==\\"602b\\", '; - rules += 'MODE=\\"0660\\", GROUP=\\"plugdev\\", TAG+=\\"uaccess\\"\n'; - - configureLinuxDrivers( - [ - "echo '" + rules + "' > /etc/udev/rules.d/80-fpga-ftdi.rules", - ].concat(reloadRules()), - true - ); - } - - function disableLinuxDriversFTDI() { - configureLinuxDrivers( - [ - 'rm -f /etc/udev/rules.d/80-icestick.rules', - 'rm -f /etc/udev/rules.d/80-fpga-ftdi.rules', - ].concat(reloadRules()), - false - ); - } - - function enableLinuxDriversSerial() { - var rules = ''; - rules += '# Disable ModemManager for BlackIce\n'; - rules += - 'ATTRS{idVendor}==\\"0483\\", ATTRS{idProduct}==\\"5740\\", ENV{ID_MM_DEVICE_IGNORE}=\\"1\\"\n'; - rules += '# Disable ModemManager for TinyFPGA B2\n'; - rules += - 'ATTRS{idVendor}==\\"1209\\", ATTRS{idProduct}==\\"2100\\", ENV{ID_MM_DEVICE_IGNORE}=\\"1\\"'; - rules += '# Disable ModemManager for TinyFPGA BX\n'; - rules += - 'ATTRS{idVendor}==\\"1d50\\", ATTRS{idProduct}==\\"6130\\", ENV{ID_MM_DEVICE_IGNORE}=\\"1\\"'; - rules += '# Disable ModemManager for iceFUN\n'; - rules += - 'ATTRS{idVendor}==\\"04d8\\", ATTRS{idProduct}==\\"ffee\\", ENV{ID_MM_DEVICE_IGNORE}=\\"1\\"'; - configureLinuxDrivers( - [ - "echo '" + rules + "' > /etc/udev/rules.d/80-fpga-serial.rules", - ].concat(reloadRules()), - true - ); - } - - function disableLinuxDriversSerial() { - configureLinuxDrivers( - ['rm -f /etc/udev/rules.d/80-fpga-serial.rules'].concat( - reloadRules() - ), - false - ); - } - - function reloadRules() { - return [ - 'udevadm control --reload-rules', - 'udevadm trigger', - 'service udev restart', - ]; - } - - const nodeSudo = require('sudo-prompt'); - - function configureLinuxDrivers(commands, enable) { - utils.startWait(); - nodeSudo.exec( - `sh -c "${commands.join('; ')}"`, - {name: 'Icestudio'}, - function (err) { - utils.endWait(); - if (err) { - alertify.error(_tcStr('Command failed!')); - return; - } - if (enable) { - alertify.success(_tcStr('Drivers enabled')); - alertify.message( - _tcStr('Unplug and reconnect the board'), - 5 - ); - } else { - alertify.warning(_tcStr('Drivers disabled')); - } - } - ); - } - - /* - * Darwin drivers - */ - - function enableDarwinDriversFTDI() { - enableDarwinDrivers(['libftdi', 'libffi'], 'macosFTDIDrivers'); - } - - function disableDarwinDriversFTDI() { - disableDarwinDrivers('macosFTDIDrivers'); - } - - function enableDarwinDriversSerial() { - enableDarwinDrivers(['libusb', 'libffi']); - } - - function disableDarwinDriversSerial() { - disableDarwinDrivers(); - } - - const fs = require('fs'); - - function enableDarwinDrivers(brewPackages, profileSetting) { - var brewCommands = ['/usr/local/bin/brew update']; - for (var i in brewPackages) { - brewCommands = brewCommands.concat(brewInstall(brewPackages[i])); - } - utils.startWait(); - if (typeof common.DEBUGMODE !== 'undefined' && common.DEBUGMODE === 1) { - fs.appendFileSync( - common.LOGFILE, - 'drivers.enableDarwinDrivers' + '\n' - ); - } - nodeChildProcess.exec( - brewCommands.join('; '), - function (error, stdout, stderr) { - if ( - typeof common.DEBUGMODE !== 'undefined' && - common.DEBUGMODE === 1 - ) { - fs.appendFileSync(common.LOGFILE, 'STDERR ' + stderr + '\n'); - fs.appendFileSync(common.LOGFILE, 'STDOUT ' + stdout + '\n'); - } - if (error) { - if ( - stderr.indexOf('brew: command not found') !== -1 || - stderr.indexOf('brew: No such file or directory') !== -1 - ) { - alertify.warning( - _tcStr('{{app}} is required.', { - app: 'Homebrew', - }) + - '
' + - '' + - _tcStr('Click here to install it') + - '', - 30 - ).callback = function (isClicked) { - if (isClicked) { - gui.Shell.openExternal('https://brew.sh'); - } - }; - } else if (stderr.indexOf('Error: Failed to download') !== -1) { - alertify.error(_tcStr('Internet connection required'), 30); - } else { - alertify.error(stderr, 30); - } - } else { - if (profileSetting) { - common.set(profileSetting, true); - } - alertify.success(_tcStr('Drivers enabled')); - } - utils.endWait(); - } - ); - if (typeof common.DEBUGMODE !== 'undefined' && common.DEBUGMODE === 1) { - fs.appendFileSync( - common.LOGFILE, - '/drivers.enableDarwinDrivers' + '\n' - ); - } - } - - function disableDarwinDrivers(profileSetting) { - if (profileSetting) { - common.set(profileSetting, false); - } - alertify.warning(_tcStr('Drivers disabled')); - } - - function brewInstall(brewPackage) { - return [ - '/usr/local/bin/brew install --force ' + brewPackage, - '/usr/local/bin/brew unlink ' + brewPackage, - '/usr/local/bin/brew link --force ' + brewPackage, - ]; - } - - var driverC = ''; - - function preUploadDarwin(callback) { - if (common.get('macosFTDIDrivers')) { - // Check and unload the Drivers - var driverA = 'com.FTDI.driver.FTDIUSBSerialDriver'; - var driverB = 'com.apple.driver.AppleUSBFTDI'; - if (checkDriverDarwin(driverA)) { - driverC = driverA; - processDriverDarwin(driverA, false, callback); - } else if (checkDriverDarwin(driverB)) { - driverC = driverB; - processDriverDarwin(driverB, false, callback); - } else { - driverC = ''; - if (callback) { - callback(); - } - } - } else if (callback) { - callback(); - } - } - - function postUploadDarwin() { - if (common.get('macosFTDIDrivers')) { - processDriverDarwin(driverC, true); - } - } - - function checkDriverDarwin(driver) { - var output = nodeChildProcess.execSync('kextstat').toString(); - return output.indexOf(driver) > -1; - } - - function processDriverDarwin(driver, load, callback) { - if (driver) { - var command = (load ? 'kextload' : 'kextunload') + ' -b ' + driver; - nodeSudo.exec( - command, - {name: 'Icestudio'}, - function (/*error, stdout, stderr*/) { - if (callback) { - callback(); - } - } - ); - } else if (callback) { - callback(); - } - } - - /* - * Windows drivers - */ - - function enableWindowsDriversFTDI() { - alerts.confirm({ - title: _tcStr('FTDI driver installation instructions'), - body: - _tcStr( - '
  1. Connect the FPGA board to the USB and wait until Windows finishes the default installation of the driver
  2. When the OK button is clicked, the FTDI driver installer will be launched in a new window
  3. In the installer, replace the (Interface 0) driver of the board by libusbK
  4. Unplug and reconnect the board
' - ) + _tcStr('It is recommended to use USB 2.0 ports'), - onok: () => { - enableWindowsDrivers('ftdi'); - }, - }); - } - - function disableWindowsDriversFTDI() { - alerts.confirm({ - title: _tcStr('FTDI driver uninstallation instructions'), - body: _tcStr( - '
  1. Find the FPGA USB Device
  2. Select the board programmer and uninstall the driver
' - ), - onok: () => { - disableWindowsDrivers('ftdi'); - }, - }); - } - - function enableWindowsDriversSerial() { - alerts.confirm({ - title: _tcStr('Serial driver installation instructions'), - body: _tcStr( - '
  1. Connect the FPGA board to the USB and wait until Windows finishes the default installation of the driver
  2. When the OK button is clicked, the Serial driver installer will be launched in a new window
  3. In the installer, follow the steps to install the driver
  4. Unplug and reconnect the board
' - ), - onok: () => { - enableWindowsDrivers('serial'); - }, - }); - } - - function disableWindowsDriversSerial() { - alerts.confirm({ - title: _tcStr('Serial driver uninstallation instructions'), - body: _tcStr( - '
  1. Find the FPGA USB Device
  2. Select the board programmer and uninstall the driver
' - ), - onok: () => { - disableWindowsDrivers('serial'); - }, - }); - } - - function enableWindowsDrivers(type) { - var option = '--' + type + '-enable'; - utils.startWait(); - nodeSudo.exec( - [common.APIO_CMD, 'drivers', option].join(' '), - {name: 'Icestudio'}, - function (error, stdout, stderr) { - utils.endWait(); - if (stderr) { - alertify.error( - _tcStr('Toolchain not installed') + - '.
' + - _tcStr('Click here to install it'), - 30 - ).callback = function (isClicked) { - if (isClicked) { - $rootScope.$broadcast('installToolchain'); - } - }; - } else if (!error) { - alertify.message( - _tcStr('Unplug and reconnect the board'), - 5 - ); - } - } - ); - } - - function disableWindowsDrivers(type) { - var option = '--' + type + '-disable'; - utils.startWait(); - nodeChildProcess.exec( - [common.APIO_CMD, 'drivers', option].join(' '), - function (error, stdout, stderr) { - utils.endWait(); - if (stderr) { - alertify.error( - _tcStr('Toolchain not installed') + - '.
' + - _tcStr('Click here to install it'), - 30 - ).callback = function (isClicked) { - if (isClicked) { - $rootScope.$broadcast('installToolchain'); - } - }; - } - } - ); - } - } - ); diff --git a/services/tools.js b/services/tools.js index aeb0727bc..3e0828a31 100644 --- a/services/tools.js +++ b/services/tools.js @@ -7,7 +7,6 @@ angular project, compiler, collections, - drivers, graph, utils, common, @@ -16,8 +15,7 @@ angular nodeFse, nodePath, nodeChildProcess, - _package, - $rootScope + _package ) { 'use strict'; @@ -30,16 +28,9 @@ angular var startAlert = null; var infoAlert = null; var resultAlert = null; - var toolchain = { - apio: '-', - installed: false, - disabled: false, - }; - - this.toolchain = toolchain; this.verifyCode = function (startMessage, endMessage) { - return apioRun( + return ICEToolRun( ['verify', '--board', common.selectedBoard.name], startMessage, endMessage @@ -47,7 +38,7 @@ angular }; this.buildCode = function (startMessage, endMessage) { - return apioRun( + return ICEToolRun( ['build', '--board', common.selectedBoard.name], startMessage, endMessage @@ -55,14 +46,14 @@ angular }; this.uploadCode = function (startMessage, endMessage) { - return apioRun( + return ICEToolRun( ['upload', '--board', common.selectedBoard.name], startMessage, endMessage ); }; - function apioRun(commands, startMessage, endMessage) { + function ICEToolRun(commands, startMessage, endMessage) { return new Promise(function (resolve) { var sourceCode = ''; @@ -80,12 +71,7 @@ angular .resetCodeErrors() .then(function () { return new Promise(function (resolve, reject) { - if (toolchain.apio != '-') { - resolve(); - } else { - _toolchainAlert(true); - reject(); - } + resolve(); }); }) .then(function () { @@ -106,7 +92,6 @@ angular if (command === 'build' || command === 'upload') { commands = commands.concat('--verbose-pnr'); } - console.log('APIO', commands); return executeLocal(commands); }) .then(function (result) { @@ -279,55 +264,38 @@ angular function executeLocal(commands) { return new Promise(function (resolve) { - if (commands[0] === 'upload') { - // Upload command requires drivers setup (Mac OS) - drivers.preUpload(function () { - _executeLocal(); - }); - } else { - // Other !upload commands - _executeLocal(); - } - - function _executeLocal() { - var apio = getApioExecutable(); - var command = [apio] - .concat(commands) - .concat(['-p', `"${common.BUILD_DIR}"`]) - .join(' '); - console.log('APIO COMMAND', command); - if ( - typeof common.DEBUGMODE !== 'undefined' && - common.DEBUGMODE === 1 - ) { - const fs = require('fs'); - fs.appendFileSync( - common.LOGFILE, - 'tools._executeLocal>' + command + '\n' - ); - } - nodeChildProcess.exec( - command, - { - maxBuffer: 5000 * 1024, - }, // To avoid buffer overflow - function (error, stdout, stderr) { - if (commands[0] === 'upload') { - // Upload command requires to restore the drivers (Mac OS) - drivers.postUpload(); - } - common.commandOutput = command + '\n\n' + stdout + stderr; - $(document).trigger('commandOutputChanged', [ - common.commandOutput, - ]); - resolve({ - error: error, - stdout: stdout, - stderr: stderr, - }); - } + var command = [common.ICETOOL] + .concat(commands) + .concat(['-p', `"${common.BUILD_DIR}"`]) + .join(' '); + if ( + typeof common.DEBUGMODE !== 'undefined' && + common.DEBUGMODE === 1 + ) { + const fs = require('fs'); + fs.appendFileSync( + common.LOGFILE, + 'tools._executeLocal>' + command + '\n' ); } + nodeChildProcess.exec( + command, + { + shell: 'bash', + maxBuffer: 5000 * 1024, + }, // To avoid buffer overflow + function (error, stdout, stderr) { + common.commandOutput = command + '\n\n' + stdout + stderr; + $(document).trigger('commandOutputChanged', [ + common.commandOutput, + ]); + resolve({ + error: error, + stdout: stdout, + stderr: stderr, + }); + } + ); }); } @@ -344,7 +312,6 @@ angular if (stdout) { var boardName = common.selectedBoard.name; var boardLabel = common.selectedBoard.info.label; - // - Apio errors if ( stdout.indexOf( 'Error: board ' + boardName + ' not connected' @@ -879,327 +846,6 @@ angular } } - this.checkToolchain = _checkToolchain; - - function getApioExecutable() { - const apio = process.env.ICESTUDIO_APIO; - if (nodeFs.existsSync(apio)) { - alertify.message('Using external apio: ' + apio, 5); - return `"${apio}"`; - } - return common.APIO_CMD; - } - - function _checkToolchain(callback) { - var apio = getApioExecutable(); - console.log('[srv.tools.checkToolchain] apio:', apio); - nodeChildProcess.exec(`${apio} --version`, function (error, stdout) { - console.log('[srv.tools.checkToolchain] version:', stdout); - if (error) { - console.log('[srv.tools.checkToolchain] version error:', error); - toolchain.apio = '-'; - _toolchainAlert(true); - if (callback) { - callback(); - } - return; - } - toolchain.apio = stdout.match(/apio,\sversion\s(.+)/i)[1]; - console.log( - '[srv.tools.checkToolchain] toolchain.apio:', - toolchain.apio - ); - if (toolchain.apio && toolchain.apio != '') { - _toolchainAlert( - false, - `${_tcStr('Apio version')} v${toolchain.apio}` - ); - // TODO: We should run some minimal test for ensuring that apio was correctly installed. - // nodeChildProcess.exec( - // `${apio} clean -p`, - // (error, stdout, stderr) => { - // console.log('[srv.tools.checkToolchain] clean sample:', error, stdout, stderr); - // if (error) { - // toolchain.apio = '-'; - // _toolchainAlert(false, _tcStr('Toolchain failed executing sample project!')); - // } - // if (callback) { - // callback(); - // } - // } - // ); - if (callback) { - callback(); - } - return; - } - _toolchainAlert(false, _tcStr('Could not retrieve apio version!')); - if (callback) { - callback(); - } - }); - } - - function executeCommand(command, callback) { - var cmd = command.join(' '); - nodeChildProcess.exec(cmd, (error, stdout, stderr) => { - console.log( - `[srv.tools.executeCommand] cmd: ${cmd}\nstdout: ${stdout}\nstderr: ${stderr}\nerror: ${error}` - ); - common.commandOutput = `${cmd}\n\n${stdout}${stderr}`; - $(document).trigger('commandOutputChanged', [common.commandOutput]); - if (error) { - alertify.error(error.message, 30); - callback(true); - } else { - callback(false); - } - }); - } - - function _removeToolchain() { - deleteFolderRecursive(common.ENV_DIR); - deleteFolderRecursive(common.APIO_HOME_DIR); - } - - $rootScope.$on('installToolchain', () => { - _installToolchain(); - }); - - this.installToolchain = _installToolchain; - - const nodeOnline = require('is-online'); - - function _installToolchain() { - if (resultAlert) { - resultAlert.dismiss(false); - } - alerts.confirm({ - title: _tcStr('Toolchain installation'), - body: _tcStr( - 'The toolchain will be downloaded. This operation requires Internet connection.' - ), - onok: () => { - _removeToolchain(); - alerts.alert({ - title: _tcStr('Installing toolchain'), - body: `
-
-
-
-
-
-
`, - }); - toolchain.apio = '-'; - - const _py = getPythonExecutable(); - var _epip = null; - - async.series( - [ - // checkInternetConnection - (callback) => { - updateProgress(_tcStr('Check Internet connection...'), 0); - nodeOnline({timeout: 5000}, function (err, online) { - if (online) { - callback(false); - return; - } - resultAlert = alertify.error( - _tcStr('Internet connection required'), - 30 - ); - callback(true); - }); - }, - // ensurePythonIsAvailable - (callback) => { - updateProgress(_tcStr('Check Python...'), 10); - if (_py) { - callback(false); - return; - } - resultAlert = alertify.error( - _tcStr('At least Python 3.5 is required'), - 30 - ); - callback(true); - }, - // createVirtualenv - (callback) => { - updateProgress(_tcStr('Create virtualenv...'), 20); - if (nodeFs.existsSync(common.ENV_DIR)) { - callback(false); - return; - } - if (!nodeFs.existsSync(common.ICESTUDIO_DIR)) { - nodeFs.mkdirSync(common.ICESTUDIO_DIR); - } - executeCommand( - [_py, '-m', 'venv', `"${common.ENV_DIR}"`], - callback - ); - }, - // setupVirtualenv - // see: https://bugs.python.org/issue30628 and https://stackoverflow.com/a/61553959 - (callback) => { - updateProgress(_tcStr('Setup virtualenv...'), 25); - if (!nodeFs.existsSync(common.ENV_DIR)) { - callback(true); - return; - } - if (!nodeFs.existsSync(common.ENV_BIN_DIR)) { - callback(true); - return; - } - _epip = [ - getPythonExecutable(common.ENV_BIN_DIR), - '-m', - 'pip', - ]; - - if (common.MSYSTEM) { - callback(false); - return; - } - executeCommand( - _epip.concat([ - 'install', - '-U', - 'pip', - 'setuptools', - 'wheel', - ]), - callback - ); - }, - // installOnlineApio - (callback) => { - updateProgress('Install apio', 50); - const pkgs = '[blackiceprog,tinyfpgab,tinyprog,icefunprog]'; //icesprog,fujprog - executeCommand( - _epip.concat([ - 'install', - '-U', - `apio${pkgs}@https://github.com/${common.get( - 'apioRepo' - )}/archive/${common.get('apioRef')}.zip`, - ]), - callback - ); - }, - // apioinstallPackages - (callback) => { - const pkgs = - 'oss-cad-suite yosys ice40 ecp5 fujprog icesprog dfu iverilog drivers scons'; - updateProgress(`apio install ${pkgs}`, 75); - apioInstall(pkgs, callback); - }, - ], - // installationCompleted - (err, results) => { - console.log( - `[srv.tools.installationCompleted] err: ${err}\nresults: ${results}` - ); - _checkToolchain(() => { - if (toolchain.apio != '-') { - updateProgress(_tcStr('Installation completed'), 100); - alertify.success(_tcStr('Toolchain installed')); - } else { - alertify.failure(_tcStr('Toolchain installation failed')); - } - }); - } - ); - }, - }); - } - - function _toolchainAlert(install, message) { - if (resultAlert) { - resultAlert.dismiss(false); - } - resultAlert = alertify.warning( - !message - ? `${_tcStr('Toolchain not found')}.
${_tcStr( - 'Click here to install it' - )}` - : message, - 10, - function (isClicked) { - if (install && isClicked) { - _installToolchain(); - } - } - ); - } - - this.removeToolchain = function () { - if (resultAlert) { - resultAlert.dismiss(false); - } - alerts.confirm({ - title: _tcStr('The toolchain will be removed'), - body: _tcStr('Do you want to continue?'), - onok: () => { - _removeToolchain(); - toolchain.apio = '-'; - alertify.success(_tcStr('Toolchain removed')); - }, - }); - }; - - this.enableDrivers = () => { - _checkToolchain(() => { - if (toolchain.apio != '-') { - drivers.enable(); - } - }); - }; - - this.disableDrivers = () => { - _checkToolchain(() => { - if (toolchain.apio != '-') { - drivers.disable(); - } - }); - }; - - function apioInstall(pkg, callback) { - executeCommand([common.APIO_CMD, 'install', pkg], callback); - } - - function setupDriversAlert() { - if (!infoAlert) { - infoAlert = alertify.message( - _tcStr('Click here to setup the drivers'), - 30 - ); - infoAlert.callback = function (isClicked) { - infoAlert = null; - if (isClicked) { - if (resultAlert) { - resultAlert.dismiss(false); - } - $rootScope.$broadcast('enableDrivers'); - } - }; - } - } - - function updateProgress(message, value) { - var bar = $('#progress-bar'); - if (value === 100) { - bar.removeClass('progress-bar-striped active'); - } - bar.text(value + '%'); - bar.attr('aria-valuenow', value); - bar.css('width', value + '%'); - $('#progress-message').text(message); - } - // Collections management this.saveCollections = () => { diff --git a/tools/ICETool b/tools/ICETool new file mode 100755 index 000000000..252395a56 --- /dev/null +++ b/tools/ICETool @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 + +from sys import ( + argv as sys_argv, + exit as sys_exit, + stdout as sys_stdout, + stderr as sys_stderr +) +from os import getenv +from pathlib import Path +from subprocess import check_call +import click + + +@click.command("verify") +@click.pass_context +@click.option( + "-p", + "--project-dir", + type=str, + metavar="path", + help="Set the target directory for the project.", +) +@click.option( + "-b", + "--board", + type=str, + metavar="board", + help="Set the board." +) +@click.option( + "-v", + "--verbose", + is_flag=True, + help="Show the entire output of the command.", +) +def VerifyCommand(ctx, board, project_dir, verbose): + """ + Verify verilog sources through Icarus Verilog. + """ + + device = 'ice40' if board in ['TinyFPGA-B2'] else 'ecp5' + + IVER_PATH = 'apath' + TARGET_SIM = 'atarget' + YOSYS_PATH = 'path' + + sources = " ".join([item.name for item in Path(project_dir).glob("*.v")]) + + opts = '-D NO_ICE40_DEFAULT_ASSIGNMENTS' if device == 'ice40' else '-D NO_INCLUDES' + + #cmd = f'iverilog {IVER_PATH} -o $TARGET -D VCD_OUTPUT={TARGET_SIM} {opts} {YOSYS_PATH}/{device}/cells_sim.v {sources}' + cmd = f'iverilog {opts} {sources}' + + print(cmd) + check_call(cmd, cwd=project_dir) + + print("verbose:", verbose) + + ctx.exit(0) + + +@click.command("build") +@click.pass_context +@click.option( + "-b", + "--board", + type=str, + metavar="board", + help="Set the board." +) +@click.option( + "--device", + type=str, + metavar="device", + help="Set the FPGA device." +) +@click.option( + "--package", + type=str, + metavar="package", + help="Set the FPGA package." +) +@click.option( + "-p", + "--project-dir", + type=str, + metavar="path", + help="Set the target directory for the project.", +) +@click.option( + "-v", + "--verbose", + is_flag=True, + help="Show the entire output of the command.", +) +@click.option( + "--verbose-yosys", + is_flag=True, + help="Show the yosys output of the command.", +) +@click.option( + "--verbose-pnr", + is_flag=True, + help="Show the pnr output of the command." +) +def BuildCommand( + ctx, + board, + device, + package, + project_dir, + verbose, + verbose_yosys, + verbose_pnr, +): + """ + Generate bitstream through Yosys and Nextpnr. + """ + + sources = " ".join([item.name for item in Path(project_dir).glob("*.v")]) + opts = '' if verbose or verbose_yosys else '-q' + cmd = f'yosys -p "proc; synth_ice40 -top main -json synth.json" {opts} {sources}' + + print(cmd) + check_call(cmd, cwd=project_dir) + sys_stdout.flush() + sys_stderr.flush() + + boards = { + 'icezum': { + 'device': 'ICE40-HX1K', + 'package': 'TQ144' + } + } + + device = boards[board]['device'].split('-')[1].lower() if device is None else device + package = boards[board]['package'].lower() if package is None else package + + pcf = [item.name for item in Path(project_dir).glob("*.pcf")][0] + opts = '' if verbose or verbose_pnr else '-q' + cmd = f'nextpnr-ice40 --{device} --package {package} --pcf {pcf} --json synth.json --asc pnr.asc {opts}' + + print(cmd) + check_call(cmd, cwd=project_dir) + sys_stdout.flush() + sys_stderr.flush() + + cmd = 'icepack pnr.asc design.bin' + + print(cmd) + check_call(cmd, cwd=project_dir) + sys_stdout.flush() + sys_stderr.flush() + + print('board:', board) + + ctx.exit(0) + + +@click.command("upload") +@click.pass_context +@click.option( + "-b", + "--board", + type=str, + metavar="board", + help="Set the board." +) +@click.option( + "--serial-port", + type=str, + metavar="serial-port", + help="Set the serial port.", +) +@click.option( + "--ftdi-id", + type=str, + metavar="ftdi-id", + help="Set the FTDI id." +) +@click.option( + "-s", + "--sram", + is_flag=True, + help="Perform SRAM programming." +) +@click.option( + "-f", + "--flash", + is_flag=True, + help="Perform FLASH programming." +) +@click.option( + "-p", + "--project-dir", + type=str, + metavar="path", + help="Set the target directory for the project.", +) +@click.option( + "-v", + "--verbose", + is_flag=True, + help="Show the entire output of the command.", +) +@click.option( + "--verbose-yosys", + is_flag=True, + help="Show the yosys output of the command.", +) +@click.option( + "--verbose-pnr", + is_flag=True, + help="Show the pnr output of the command." +) +def UploadCommand( + ctx, + board, + serial_port, + ftdi_id, + sram, + flash, + project_dir, + verbose, + verbose_yosys, + verbose_pnr, +): + """ + Upload bitstream to the board through openFPGALoader. + """ + + print("board:", board) + print("serial_port:", serial_port) + print("ftdi_id:", ftdi_id) + print("sram:", sram) + print("flash:", flash) + print("verbose:", verbose) + print("verbose_yosys:", verbose_yosys) + print("verbose_pnr:", verbose_pnr) + + ctx.exit(0) + + +@click.group() +def cli(): + pass + +cli.add_command(VerifyCommand) +cli.add_command(BuildCommand) +cli.add_command(UploadCommand) + + +if __name__ == '__main__': + toolenv = getenv('ICETOOL_CMD', 'ICETool') + if toolenv != 'ICETool': + check_call([toolenv] + sys_argv[1:]) + sys_exit() + cli() diff --git a/views/preferences.html b/views/preferences.html index c4429ca23..a4c8364c5 100644 --- a/views/preferences.html +++ b/views/preferences.html @@ -228,134 +228,3 @@