diff --git a/app/scripts/controllers/design.js b/app/scripts/controllers/design.js
index c96fcc2b9..f32245ef4 100644
--- a/app/scripts/controllers/design.js
+++ b/app/scripts/controllers/design.js
@@ -140,9 +140,14 @@ angular
}
};
+ function loadSelectedGraph() {
+ utils.beginBlockingTask();
+ setTimeout(function(){
+ _decoupledLoadSelectedGraph();
+ },500);
+ }
+
function _decoupledLoadSelectedGraph() {
-
-
var n = graph.breadcrumbs.length;
var opt = { disabled: true };
var design = false;
@@ -166,8 +171,7 @@ angular
graph.resetView();
graph.loadDesign(design, opt, function () {
$scope.isNavigating = false;
- graph.fitContent();
-
+ utils.endBlockingTask();
});
$scope.topModule = true;
}
@@ -191,24 +195,13 @@ angular
graph.fitContent();
graph.resetView();
graph.loadDesign(dependency.design, opt, function () {
- graph.fitContent();
$scope.isNavigating = false;
-
+ utils.endBlockingTask();
});
$scope.information = dependency.package;
}
}
-
-
- function loadSelectedGraph() {
-
- utils.beginBlockingTask();
- setTimeout(function(){
- _decoupledLoadSelectedGraph();
- },500);
- }
-
$rootScope.$on('navigateProject', function (event, args) {
var opt = { disabled: true };
if (typeof args.submodule !== 'undefined') {
@@ -230,9 +223,7 @@ angular
graph.resetView();
project.update({ deps: false }, function () {
graph.loadDesign(args.project.design, opt, function () {
-
utils.endBlockingTask();
-
});
});
diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js
index bd993a80e..44de30bbe 100644
--- a/app/scripts/controllers/menu.js
+++ b/app/scripts/controllers/menu.js
@@ -1027,6 +1027,17 @@ angular
}
};
+ $scope.toggleInoutPorts = function () {
+ const newState = !profile.get("allowInoutPorts");
+ profile.set("allowInoutPorts", newState);
+ if (newState) {
+ alertify.success(gettextCatalog.getString("Tri-state connections (inout ports) enabled"));
+ } else {
+ common.allowProjectInoutPorts = true; // if tri-state in current design, keep behaviour unchanged
+ alertify.success(gettextCatalog.getString("Tri-state connections (inout ports) disabled"));
+ }
+ };
+
$(document).on("langChanged", function (evt, lang) {
$scope.selectLanguage(lang);
});
diff --git a/app/scripts/services/blockforms.js b/app/scripts/services/blockforms.js
index add76e966..cd57be133 100644
--- a/app/scripts/services/blockforms.js
+++ b/app/scripts/services/blockforms.js
@@ -54,16 +54,18 @@ angular.module('icestudio')
//-- the OK button and all the data is ok.
//-- -cells: Array of blocks passed as arguments
//-------------------------------------------------------------------------
- function newBasic(type, callback) {
+ function newBasic(type, allowInoutPorts, callback) {
+ let name = ''; //-- Port name by default
+ let clock = false; //-- Clock checkbox not checked by default
+ let inoutDefault;
let form;
//-- If inside a module, the FPGA-pin option is disabled
- //-- The pins are always virtual
let disabled = common.isEditingSubmodule;
+
+ //-- The pins are always virtual
let virtual = disabled;
- let clock = false; //-- Clock checkbox no checked by default
- let name = ''; //-- Port name by default
//-- Create the block by calling the corresponding function
//-- according to the given type
@@ -72,14 +74,24 @@ angular.module('icestudio')
//-- Input port
case blocks.BASIC_INPUT:
- form = new forms.FormBasicInput(name, virtual, clock, disabled);
+ //-- InOut-pin option is present or not, and if present, it defaults to false
+ if (allowInoutPorts) {
+ inoutDefault = false;
+ }
+
+ form = new forms.FormBasicInput(name, virtual, clock, disabled, inoutDefault);
newBasicPort(form, callback);
break;
//-- Output port
case blocks.BASIC_OUTPUT:
- form = new forms.FormBasicOutput(name, virtual, disabled);
+ //-- InOut-pin option is present or not, and if present, it defaults to false
+ if (allowInoutPorts) {
+ inoutDefault = false;
+ }
+
+ form = new forms.FormBasicOutput(name, virtual, disabled, inoutDefault);
newBasicPort(form, callback);
break;
@@ -115,7 +127,12 @@ angular.module('icestudio')
//-- Code block
case blocks.BASIC_CODE:
- form = new forms.FormBasicCode();
+ //-- Inout ports are present or not, and if present, the value is just initialized to empty string
+ if (allowInoutPorts) {
+ inoutDefault = '';
+ }
+
+ form = new forms.FormBasicCode('', '', '', inoutDefault, inoutDefault);
newBasicCode(form, callback);
break;
@@ -720,6 +737,7 @@ angular.module('icestudio')
}
for (o in instance.data.ports.inoutRight) {
port = instance.data.ports.inoutRight[o];
+ // (there's no port default rule for the right side, output)
rightPorts.push({
id: port.name,
name: port.name,
@@ -912,7 +930,7 @@ angular.module('icestudio')
//-- * cellView: Access to the graphics library
//-- * callback: Function to call when the block is Edited
//-----------------------------------------------------------------------
- function editBasic(type, cellView, callback) {
+ function editBasic(type, allowInoutPorts, cellView, callback) {
//-- Get information from the joint graphics library
let block = cellView.model.attributes;
@@ -921,7 +939,7 @@ angular.module('icestudio')
let name = block.data.name + (block.data.range || '');
let virtual = block.data.virtual;
let clock = block.data.clock;
- let inout = (typeof block.data.inout === 'undefined') ? false : block.data.inout;
+ let inoutValue;
let form;
let color = block.data.blockColor;
@@ -933,6 +951,11 @@ angular.module('icestudio')
virtual = true;
}
+ //-- InOut-pin option is present or not
+ if (allowInoutPorts) {
+ inoutValue = (typeof block.data.inout === 'undefined') ? false : !!block.data.inout;
+ }
+
//-- Call the corresponding function depending on the type of block
switch (type) {
@@ -940,7 +963,7 @@ angular.module('icestudio')
case blocks.BASIC_INPUT:
//-- Build the form, and pass the actual block data
- form = new forms.FormBasicInput(name, virtual, clock, disabled, inout);
+ form = new forms.FormBasicInput(name, virtual, clock, disabled, inoutValue);
editBasicPort(form, cellView, callback);
break;
@@ -948,7 +971,7 @@ angular.module('icestudio')
case blocks.BASIC_OUTPUT:
//-- Build the form, and pass the actual block data
- form = new forms.FormBasicOutput(name, virtual, disabled, inout);
+ form = new forms.FormBasicOutput(name, virtual, disabled, inoutValue);
editBasicPort(form, cellView, callback);
break;
@@ -977,7 +1000,7 @@ angular.module('icestudio')
break;
case blocks.BASIC_CODE:
- editBasicCode(cellView, callback);
+ editBasicCode(allowInoutPorts, cellView, callback);
break;
case blocks.BASIC_INFO:
@@ -1380,33 +1403,43 @@ angular.module('icestudio')
}
- function editBasicCode(cellView, callback) {
+ function editBasicCode(allowInoutPorts, cellView, callback) {
+
+ let inPortNames = '';
+ let outPortNames = '';
+ let inoutLeftPortNames;
+ let inoutRightPortNames;
//-- Get information from the joint graphics library
let block = cellView.model.attributes;
- // Backward compatibility
+ // Compatibility between tri-state/non-tri-state project formats
if (typeof block.data.ports.inoutLeft === 'undefined') {
block.data.ports.inoutLeft = [];
block.data.ports.inoutRight = [];
}
- //-- Get the input port names as a string
- let inPortNames = blocks.portsInfo2Str(block.data.ports.in);
+ //-- Get the input port names as a string
+ if (block.data.ports.in) {
+ inPortNames = blocks.portsInfo2Str(block.data.ports.in);
+ }
//-- Get the output port names as a string
- let outPortNames = blocks.portsInfo2Str(block.data.ports.out);
+ if (block.data.ports.out) {
+ outPortNames = blocks.portsInfo2Str(block.data.ports.out);
+ }
//-- Get the input param names as a string
let inParamNames = blocks.portsInfo2Str(block.data.params);
+ //-- Get the optional left/right InputOutput port names as strings
+ //-- InputOutput port name fields are present or not, and if present, are initialized to strings
+ if (allowInoutPorts) {
+ //-- Get the left side InputOutput port names as a string
+ inoutLeftPortNames = blocks.portsInfo2Str(block.data.ports.inoutLeft);
-
- //-- Get the input/output port names at left side of block as a string
- let inoutLeftPortNames = blocks.portsInfo2Str(block.data.ports.inoutLeft);
-
- //-- Get the input/output port names at right side of block as a string
- let inoutRightPortNames = blocks.portsInfo2Str(block.data.ports.inoutRight);
-
+ //-- Get the right side InputOutput port names as a string
+ inoutRightPortNames = blocks.portsInfo2Str(block.data.ports.inoutRight);
+ }
//-- Create the form
let form = new forms.FormBasicCode(
@@ -1467,7 +1500,7 @@ angular.module('icestudio')
//-- Get all the wires of the current block
let connectedWires = graph.getConnectedLinks(cellView.model);
- //---------- Chage the block
+ //---------- Change the block
graph.startBatch('change');
//-- Remove the current block
diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js
index edac33263..7f62e9b1a 100644
--- a/app/scripts/services/blocks.js
+++ b/app/scripts/services/blocks.js
@@ -107,7 +107,10 @@ angular.module('icestudio')
//-- * Particular information:
//-- -clock: (bool). If the port is a clock or not
//-- * true: It is a clock signal
- //-- * False: Normal signal
+ //-- * false: Normal signal
+ //-- -inout: (bool). If the port is inout or normal
+ //-- * true: It is tri-state
+ //-- * false: It is normal two-state
//-------------------------------------------------------------------------
class InputPortBlock extends PortBlock {
@@ -118,7 +121,6 @@ angular.module('icestudio')
//-- Particular information
this.data.clock = clock; //-- Optional. Is the port a clock input?
-
this.data.inout = inout;
}
}
@@ -128,7 +130,10 @@ angular.module('icestudio')
//-- Class: Output port. The information goes from the FPGA to the
//-- outside. Or from one block to another the upper level
//--
- //-- NO particular information
+ //-- * Particular information:
+ //-- -inout: (bool). If the port is inout or normal
+ //-- * true: It is tri-state
+ //-- * false: It is normal two-state
//-------------------------------------------------------------------------
class OutputPortBlock extends PortBlock {
@@ -230,6 +235,8 @@ angular.module('icestudio')
//-- * inPortsInfo: Array of PortInfos
//-- * outPortsInfo: Array of PortInfos
//-- * inParamsInfo: Array of PortInfos
+ //-- * inoutLeftPortsInfo: Optional Array of PortInfos
+ //-- * inoutRightPortsInfo: Optional Array of PortInfos
//--
//-- PortInfos:
//-- * name: String
@@ -297,28 +304,33 @@ angular.module('icestudio')
this.data.params.push(info);
});
- //-- Insert the InputOutput portInfo
- inoutLeftPortsInfo.forEach(portInfo => {
- let info = {
- name: portInfo.name,
- range: portInfo.rangestr,
- size: portInfo.size > 1 ? portInfo.size : undefined
- };
+ //-- Insert the InputOutput portInfo, left and/or right
+ if (inoutLeftPortsInfo) {
+ inoutLeftPortsInfo.forEach(portInfo => {
- this.data.ports.inoutLeft.push(info);
- });
- //-- Insert the InputOutput portInfo
- inoutRightPortsInfo.forEach(portInfo => {
+ let info = {
+ name: portInfo.name,
+ range: portInfo.rangestr,
+ size: portInfo.size > 1 ? portInfo.size : undefined
+ };
- let info = {
- name: portInfo.name,
- range: portInfo.rangestr,
- size: portInfo.size > 1 ? portInfo.size : undefined
- };
+ this.data.ports.inoutLeft.push(info);
+ });
+ }
- this.data.ports.inoutRight.push(info);
- });
+ if (inoutRightPortsInfo) {
+ inoutRightPortsInfo.forEach(portInfo => {
+
+ let info = {
+ name: portInfo.name,
+ range: portInfo.rangestr,
+ size: portInfo.size > 1 ? portInfo.size : undefined
+ };
+
+ this.data.ports.inoutRight.push(info);
+ });
+ }
}
}
diff --git a/app/scripts/services/common.js b/app/scripts/services/common.js
index 9cc4087f2..5d828fd5d 100644
--- a/app/scripts/services/common.js
+++ b/app/scripts/services/common.js
@@ -31,6 +31,9 @@ angular
// Project status: Has it change from the previous build or not?
this.hasChangesSinceBuild = false;
+ // Tri-state ports: Are they present in any opened designs or blocks, and is this approved?
+ // (User profile "allowInoutPorts" is false)
+ this.allowProjectInoutPorts = false;
// All project dependencies
this.allDependencies = {};
diff --git a/app/scripts/services/forms.js b/app/scripts/services/forms.js
index 051ebdb79..491395b6e 100644
--- a/app/scripts/services/forms.js
+++ b/app/scripts/services/forms.js
@@ -834,7 +834,7 @@ angular.module('icestudio')
//-- Field 0: Text input
let field0 = new TextField(
msg, //-- Top message
- name, //-- Default value
+ name, //-- Default value
0 //-- Field id
);
@@ -974,14 +974,16 @@ angular.module('icestudio')
//-- |
//-- [✅️] FPGA pin |
//-- [ ] Show clock |
- //-- [ ] InOut pin |
+ //-- [ ] InOut pin (*Optional) |
//----------------------------------------+
//-- INPUTS:
//-- * name: Default port name
//-- * virtual: Is this a virtual or real port?
- //-- * Clock: The input pin carries a clock signal
+ //-- * clock: The input pin carries a clock signal
//-- * disabled: FPGA-pin checkbox disabled
- constructor(name = '', virtual = false, clock = false, disabled = false, inout = false) {
+ //-- * inoutValue: If undefined, InOut-pin checkbox is hidden
+ // Otherwise, it is a boolean value to initialize the checkbox
+ constructor(name = '', virtual = false, clock = false, disabled = false, inoutValue = undefined) {
//-- Create a blank BasicPortForm (calling the upper Class)
super(gettextCatalog.getString('Input port name:'),
@@ -1005,17 +1007,17 @@ angular.module('icestudio')
//-- Add the fields to the form
this.addField(field2);
- //-- Field 3: Checkbox for configuring the pi as inout
-
- let field3 = new CheckboxField(
- gettextCatalog.getString('InOut pin'),
- inout, //-- Default value
- 3 //-- Field id
- );
-
- //-- Add the fields to the form
- this.addField(field3);
+ //-- Field 3: Checkbox for configuring the pin as inout
+ if (inoutValue !== undefined) {
+ let field3 = new CheckboxField(
+ gettextCatalog.getString('InOut pin'),
+ inoutValue, //-- Default value
+ 3 //-- Field id
+ );
+ //-- Add the fields to the form
+ this.addField(field3);
+ }
//-- Store the initial values
//-- (For comparing it later with the new ones and detect if
@@ -1023,7 +1025,9 @@ angular.module('icestudio')
this.nameIni = name;
this.virtualIni = virtual;
this.clockIni = clock;
- this.inoutIni = inout;
+ if (inoutValue !== undefined) {
+ this.inoutIni = inoutValue;
+ }
}
//------------------------------------------------
@@ -1040,11 +1044,10 @@ angular.module('icestudio')
//-- it indicates if this is a clock input
this.clock = this.values[2];
- //-- Input port have the inout property
- //-- it indicates if this pin is an inout pin
+ //-- There may be no inout field in Values (undefined)
+ //-- If inout field is present, it indicates if this pin is an inout type
this.inout = this.values[3];
-
//-- Check all ports again... There could be no data buses defined
//-- as clocks (it is only for 1-wire ports)
for (let portInfo of this.portInfos) {
@@ -1065,10 +1068,10 @@ angular.module('icestudio')
//-- There have been no errors. Detect if there have been some
//-- changes in the values
- this.changed = (this.nameIni !== this.values[0] ||
+ this.changed = this.nameIni !== this.values[0] ||
this.virtualIni !== this.virtual ||
- this.inoutIni !== this.inout ||
- this.clockIni !== this.clock);
+ this.clockIni !== this.clock ||
+ this.hasOwnProperty('inoutIni') && this.inoutIni !== this.inout;
}
//-------------------------------------------------------------
@@ -1118,13 +1121,15 @@ angular.module('icestudio')
//-- +--------------------------+ |
//-- |
//-- [✅️] FPGA pin |
- //-- [ ] InOut pin |
+ //-- [ ] InOut pin (*Optional) |
//----------------------------------------+
//-- INPUTS:
- //-- * msg: Message above the text box
//-- * name: Default port name
//-- * virtual: Is this a virtual or real port?
- constructor(name = '', virtual = false, disabled = false, inout = false) {
+ //-- * disabled: FPGA-pin checkbox disabled
+ //-- * inoutValue: If undefined, InOut-pin checkbox is hidden
+ // Otherwise, it is a boolean value to initialize the checkbox
+ constructor(name = '', virtual = false, disabled = false, inoutValue = undefined) {
//-- Create a blank BasicPortForm (calling the upper Class)
super(gettextCatalog.getString('Output port name'),
@@ -1135,20 +1140,28 @@ angular.module('icestudio')
//-- Store the type of block associated with the Form
this.type = blocks.BASIC_OUTPUT;
- let field2 = new CheckboxField(
- gettextCatalog.getString('InOut pin'),
- inout, //-- Default value
- 2 //-- Field id
- );
+ //-------- Add the particular fields
+
+ //-- Field 2: Checkbox for configuring the pin as inout
+ if (inoutValue !== undefined) {
+ let field2 = new CheckboxField(
+ gettextCatalog.getString('InOut pin'),
+ inoutValue, //-- Default value
+ 2 //-- Field id
+ );
+
+ //-- Add the field to the form
+ this.addField(field2);
+ }
- //-- Add the fields to the form
- this.addField(field2);
//-- Store the initial values
//-- (For comparing it later with the new ones and detect if
//-- there have been changes)
this.nameIni = name;
this.virtualIni = virtual;
- this.inoutIni = inout;
+ if (inoutValue !== undefined) {
+ this.inoutIni = inoutValue;
+ }
}
process(evt) {
@@ -1156,15 +1169,15 @@ angular.module('icestudio')
//-- Process the form as an BasicPort
super.process(evt);
- //-- Input port have the inout property
- //-- it indicates if this pin is an inout pin
+ //-- There may be no inout field in Values (undefined)
+ //-- If inout field is present, it indicates if this pin is an inout type
this.inout = this.values[2];
//-- There have been no errors. Detect if there have been some
//-- changes in the values
- this.changed = (this.nameIni !== this.values[0] ||
+ this.changed = this.nameIni !== this.values[0] ||
this.virtualIni !== this.virtual ||
- this.inoutIni !== this.inout);
+ this.hasOwnProperty('inoutIni') && this.inoutIni !== this.inout;
}
//-------------------------------------------------------------
@@ -1584,9 +1597,13 @@ angular.module('icestudio')
//-- INPUTS:
//-- * portsIn: Input port names (separated by commas)
//-- * portsOut: Output port names (separated by commas)
- //-- * paramsIn: Input parameters names (separataed by commas)
+ //-- * paramsIn: Input parameters names (separated by commas)
+ //-- * portsInOutLeft: If undefined, InputOutput port names field is hidden
+ // Otherwise, it is string of port names (separated by commas) to populate the field
+ //-- * portsInOutRight: If undefined, InputOutput port names field is hidden
+ // Otherwise, it is string of port names (separated by commas) to populate the field
//-----------------------------------------------------------------
- constructor(portsIn = '', portsOut = '', paramsIn = '', portsInOutLeft = '', portsInOutRight = '') {
+ constructor(portsIn = '', portsOut = '', paramsIn = '', portsInOutLeft = undefined, portsInOutRight = undefined) {
//-- Create a blank Form (calling the upper Class)
super();
@@ -1614,25 +1631,37 @@ angular.module('icestudio')
2
);
- //-- Field 3: InputOutput port names at the left
- let field3 = new TextField(
- gettextCatalog.getString('InOut Left ports'), //-- Top message
- portsInOutLeft, //-- Default InputOutput port names at the left
- 3 //-- Field id
- );
-
- //-- Field 4: InputOutput port names at the right
- let field4 = new TextField(
- gettextCatalog.getString('InOut Right ports'), //-- Top message
- portsInOutRight, //-- Default InputOutput port names at the right
- 4 //-- Field id
- );
- //-- Add the fields to the form
this.addField(field0);
this.addField(field1);
this.addField(field2);
- this.addField(field3);
- this.addField(field4);
+
+ //-- Optional fields
+ let field3;
+ let field4;
+
+ //-- Field 3: InputOutput port names at the left
+ if (portsInOutLeft !== undefined) {
+ field3 = new TextField(
+ gettextCatalog.getString('InOut Left ports'), //-- Top message
+ portsInOutLeft, //-- Default InputOutput port names at the left
+ 3 //-- Field id
+ );
+
+ //-- Add the field to the form
+ this.addField(field3);
+ }
+
+ //-- Field 4: InputOutput port names at the right
+ if (portsInOutRight !== undefined) {
+ field4 = new TextField(
+ gettextCatalog.getString('InOut Right ports'), //-- Top message
+ portsInOutRight, //-- Default InputOutput port names at the right
+ 4 //-- Field id
+ );
+
+ //-- Add the field to the form
+ this.addField(field4);
+ }
//-- Control the notifications generated by
//-- the errors when processing the form
@@ -1640,12 +1669,14 @@ angular.module('icestudio')
//-- Store the initial values used for creating the form
//-- They will be used later for detecting a change in
- //-- the vaues introduced by the user
+ //-- the values introduced by the user
this.iniPortsIn = portsIn;
this.iniPortsOut = portsOut;
this.iniParamsIn = paramsIn;
- this.iniPortsInOutLeft = portsInOutLeft;
- this.iniPortsInOutRight = portsInOutRight;
+ if (portsInOutLeft !== undefined && portsInOutRight !== undefined) {
+ this.iniPortsInOutLeft = portsInOutLeft;
+ this.iniPortsInOutRight = portsInOutRight;
+ }
}
//-----------------------------------------------------------------------
@@ -1714,14 +1745,17 @@ angular.module('icestudio')
//-- Parse the input parameters
this.inParams = Form.parseNames(this.values[2]);
- //-- Values[3]: Input port names at the right of the block
- //-- Parse the input/output port names
- this.inoutLeftPorts = Form.parseNames(this.values[3]);
-
- //-- Values[4]: input/Output port names at the right of the block
- //-- Parse the input/output port names
- this.inoutRightPorts = Form.parseNames(this.values[4]);
+ //-- Values[3]: InputOutput port names at the left of the block
+ //-- If field is present in Values, then parse the inout port names
+ if (this.values[3]) {
+ this.inoutLeftPorts = Form.parseNames(this.values[3]);
+ }
+ //-- Values[4]: InputOutput port names at the right of the block
+ //-- If field is present in Values, then parse the inout port names
+ if (this.values[4]) {
+ this.inoutRightPorts = Form.parseNames(this.values[4]);
+ }
}
@@ -1742,8 +1776,12 @@ angular.module('icestudio')
this.inPortsInfo = this.getPortInfo(this.inPorts, evt);
this.outPortsInfo = this.getPortInfo(this.outPorts, evt);
this.inParamsInfo = this.getPortInfo(this.inParams, evt);
- this.inoutLeftPortsInfo = this.getPortInfo(this.inoutLeftPorts, evt);
- this.inoutRightPortsInfo = this.getPortInfo(this.inoutRightPorts, evt);
+ if (this.hasOwnProperty('inoutLeftPorts')) {
+ this.inoutLeftPortsInfo = this.getPortInfo(this.inoutLeftPorts, evt);
+ }
+ if (this.hasOwnProperty('inoutRightPorts')) {
+ this.inoutRightPortsInfo = this.getPortInfo(this.inoutRightPorts, evt);
+ }
//-- Validate values entered by the user
//-- There cannot be inputs, outputs and params with the same name
@@ -1752,12 +1790,17 @@ angular.module('icestudio')
//-- Array for storing all the port names created
let allPortnames = [];
- //-- Array with the input/output given by the user
- let userPorts = this.inPortsInfo.concat(this.outPortsInfo, this.inoutLeftPortsInfo, this.inoutRightPortsInfo);
+ //-- Array with the inputs and outputs given by the user
+ let userPorts = this.inPortsInfo.concat(this.outPortsInfo);
//-- Add the array with the input parameters
userPorts = userPorts.concat(this.inParamsInfo);
+ //-- Add the optional arrays with the left/right InputOutput ports
+ if (this.inoutLeftPortsInfo && this.inoutRightPortsInfo) {
+ userPorts = userPorts.concat(this.inoutLeftPortsInfo, this.inoutRightPortsInfo);
+ }
+
//-- Analyze all the port names, one by one
for (let portInfo of userPorts) {
@@ -1804,21 +1847,24 @@ angular.module('icestudio')
//-- Get the input param names as a string
let inParamNames = blocks.portsInfo2Str(this.inParamsInfo);
- //-- Get the input/output port names as a string
- let inoutLeftPortNames = blocks.portsInfo2Str(this.inoutLeftPortsInfo);
+ //-- Get the optional left/right InputOutput port names as strings
+ let inoutLeftPortNames;
+ let inoutRightPortNames;
- //-- Get the input/output port names as a string
- let inoutRightPortNames = blocks.portsInfo2Str(this.inoutRightPortsInfo);
+ if (this.inoutLeftPortsInfo && this.inoutRightPortsInfo) {
+ inoutLeftPortNames = blocks.portsInfo2Str(this.inoutLeftPortsInfo);
+ inoutRightPortNames = blocks.portsInfo2Str(this.inoutRightPortsInfo);
+ }
//-- Compare these values with the initial ones
//-- to detec if there has been a change
//-- All the items compared are Strings
- let changed = inPortNames !== this.iniPortsIn ||
- outPortNames !== this.iniPortsOut ||
- inoutLeftPortNames !== this.iniPortsInOutLeft ||
- inoutRightPortNames !== this.iniPortsInOutRight ||
- inParamNames !== this.iniParamsIn;
+ let changed = this.iniPortsIn !== inPortNames ||
+ this.iniPortsOut !== outPortNames ||
+ this.iniParamsIn !== inParamNames ||
+ this.hasOwnProperty('iniPortsInOutLeft') && this.iniPortsInOutLeft !== inoutLeftPortNames ||
+ this.hasOwnProperty('iniPortsInOutRight') && this.iniPortsInOutRight !== inoutRightPortNames;
//-- Return a boolean value
return changed;
@@ -1839,8 +1885,8 @@ angular.module('icestudio')
//-- Field 0: Memory block names
let field0 = new TextField(
gettextCatalog.getString('Memory blocks'), //-- Top message
- names, //-- Default Input port names
- 0 //-- Field id
+ names, //-- Default names
+ 0 //-- Field id
);
//-------- Field 1: Combobox
@@ -2058,7 +2104,7 @@ angular.module('icestudio')
0 //-- Field id
);
- //-- Field 2: Checkbox for selecting if the constant is a
+ //-- Field 1: Checkbox for selecting if the constant is a
//-- local parameter or not
let field1 = new CheckboxField(
gettextCatalog.getString('Local parameter'),
diff --git a/app/scripts/services/graph.js b/app/scripts/services/graph.js
index a44b15155..bb0d58c4f 100644
--- a/app/scripts/services/graph.js
+++ b/app/scripts/services/graph.js
@@ -522,8 +522,8 @@ angular.module('icestudio')
if (type.indexOf('basic.') !== -1) {
// Edit basic blocks
if (paper.options.enabled) {
- blockforms.editBasic(type, cellView, addCell);
-
+ const allowInoutPorts = profile.get('allowInoutPorts') || common.allowProjectInoutPorts;
+ blockforms.editBasic(type, allowInoutPorts, cellView, addCell);
}
}
else if (common.allDependencies[type]) {
@@ -1041,7 +1041,8 @@ angular.module('icestudio')
};
this.createBasicBlock = function (type) {
- blockforms.newBasic(type, function (cells) {
+ const allowInoutPorts = profile.get('allowInoutPorts') || common.allowProjectInoutPorts;
+ blockforms.newBasic(type, allowInoutPorts, function (cells) {
self.addDraggableCells(cells);
});
};
@@ -1265,57 +1266,51 @@ angular.module('icestudio')
this.pasteSelected = function () {
if (document.activeElement.tagName === 'A' ||
document.activeElement.tagName === 'BODY') {
- utils.pasteFromClipboard(function (object) {
- if (object.version === common.VERSION) {
- self.appendDesign(object.design, object.dependencies);
- }
+ utils.pasteFromClipboard(profile, function (object) {
+ self.appendDesign(object.design, object.dependencies);
});
}
};
this.pasteAndCloneSelected = function () {
if (document.activeElement.tagName === 'A' ||
document.activeElement.tagName === 'BODY') {
- utils.pasteFromClipboard(function (object) {
- if (object.version === common.VERSION) {
-
- let hash = {};
- // We will clone all dependencies
- if (typeof object.dependencies !== false &&
- object.dependencies !== false &&
- object.dependencies !== null) {
-
- var dependencies = utils.clone(object.dependencies);
- object.dependencies = {};
- let hId = false;
- let dep = false;
- let dat = false;
- let seq = false;
- let oldversion = false;
-
- for (dep in dependencies) {
- dependencies[dep].package.name = dependencies[dep].package.name + ' CLONE';
- dat = new Date();
- seq = dat.getTime();
- oldversion = dependencies[dep].package.version.replace(/(.*)(-c\d*)/, '$1');
- dependencies[dep].package.version = oldversion + '-c' + seq;
-
- hId = utils.dependencyID(dependencies[dep]);
- object.dependencies[hId] = dependencies[dep];
- hash[dep] = hId;
- }
-
- //reassign dependencies
-
- object.design.graph.blocks = object.design.graph.blocks.map(function (e) {
- if (typeof e.type !== 'undefined' &&
- typeof hash[e.type] !== 'undefined') {
- e.type = hash[e.type];
- }
- return e;
- });
+ utils.pasteFromClipboard(profile, function (object) {
+ let hash = {};
+ // We will clone all dependencies
+ if (typeof object.dependencies !== false &&
+ object.dependencies !== false &&
+ object.dependencies !== null) {
+
+ var dependencies = utils.clone(object.dependencies);
+ object.dependencies = {};
+ let hId = false;
+ let dep = false;
+ let dat = false;
+ let seq = false;
+ let oldversion = false;
+
+ for (dep in dependencies) {
+ dependencies[dep].package.name = dependencies[dep].package.name + ' CLONE';
+ dat = new Date();
+ seq = dat.getTime();
+ oldversion = dependencies[dep].package.version.replace(/(.*)(-c\d*)/, '$1');
+ dependencies[dep].package.version = oldversion + '-c' + seq;
+
+ hId = utils.dependencyID(dependencies[dep]);
+ object.dependencies[hId] = dependencies[dep];
+ hash[dep] = hId;
}
- self.appendDesign(object.design, object.dependencies);
+
+ //reassign dependencies
+ object.design.graph.blocks = object.design.graph.blocks.map(function (e) {
+ if (typeof e.type !== 'undefined' &&
+ typeof hash[e.type] !== 'undefined') {
+ e.type = hash[e.type];
+ }
+ return e;
+ });
}
+ self.appendDesign(object.design, object.dependencies);
});
}
};
@@ -1323,9 +1318,7 @@ angular.module('icestudio')
this.duplicateSelected = function() {
if (hasSelection()) {
utils.duplicateSelected(selection, graph, function (object) {
- if (object.version === common.VERSION) {
- self.appendDesign(object.design, object.dependencies);
- }
+ self.appendDesign(object.design, object.dependencies);
});
}
};
@@ -1458,7 +1451,7 @@ angular.module('icestudio')
let cells = graphToCells(design.graph, opt);
- self.fitContent();
+ self.fitContent();
graph.addCells(cells);
@@ -1470,23 +1463,14 @@ angular.module('icestudio')
}
if (callback) {
-
-
callback();
-
- utils.endBlockingTask();
-
- self.fitContent();
- } else {
-
- utils.endBlockingTask();
-
- self.fitContent();
-
}
-
+
+ self.fitContent();
return true;
}
+
+ return false;
};
function graphToCells(_graph, opt) {
@@ -1656,7 +1640,7 @@ angular.module('icestudio')
});
if (isMigrated) {
- alertify.warning(gettextCatalog.getString("If you see blank IN/OUT pins, it is because equivalent pins do not exist on this board"));
+ alertify.warning(gettextCatalog.getString('If you see blank IN/OUT pins, it is because equivalent pins do not exist on this board'));
}
diff --git a/app/scripts/services/profile.js b/app/scripts/services/profile.js
index aad728e72..ef32eecd9 100644
--- a/app/scripts/services/profile.js
+++ b/app/scripts/services/profile.js
@@ -20,6 +20,7 @@ angular.module('icestudio')
'board': '', //-- Selected board
'boardRules': true, //-- Boardrules (active by default)
+ 'allowInoutPorts': false, //-- Tri-state (inout ports) available (not included by default)
'collection': '', //-- Selected collection
'externalCollections': '', //-- Path for the external collections
'externalPlugins': '', //-- Path for the external paths
@@ -54,6 +55,7 @@ angular.module('icestudio')
self.data = {
'board': data.board || '',
'boardRules': data.boardRules !== false,
+ 'allowInoutPorts': data.allowInoutPorts === true,
'collection': data.collection || '',
'language': data.language || 'en',
'uiTheme': data.uiTheme || 'dark',
diff --git a/app/scripts/services/project.js b/app/scripts/services/project.js
index b0b857ba3..327c3f08c 100644
--- a/app/scripts/services/project.js
+++ b/app/scripts/services/project.js
@@ -55,22 +55,17 @@ angular.module('icestudio')
project[key] = obj;
}
};
- this.open = function (filepath, emptyPath) {
-
+ this.open = function (filepath, emptyPath) {
let _this=this;
utils.beginBlockingTask();
setTimeout(function(){
- _this._decoupledOpen(filepath,emptyPath);
+ _this._decoupledOpen(filepath,emptyPath);
},200);
- };
- this._decoupledOpen = function(filepath,emptyPath){
-
-
+ };
-
+ this._decoupledOpen = function(filepath,emptyPath){
var self = this;
-
self.path = emptyPath ? '' : filepath;
self.filepath = filepath;
utils.readFile(filepath)
@@ -84,42 +79,51 @@ angular.module('icestudio')
.catch(function () {
alertify.error(gettextCatalog.getString('Invalid project format'), 30);
});
- };
+ };
this.load = function (name, data) {
var self = this;
if (!checkVersion(data.version)) {
+ utils.endBlockingTask();
return;
}
- project = _safeLoad(data, name);
- if (project.design.board !== common.selectedBoard.name) {
- var projectBoard = boards.boardLabel(project.design.board);
- alertify.set('confirm', 'labels', {
- 'ok': gettextCatalog.getString('Load'),
- 'cancel': gettextCatalog.getString('Convert')
- });
- alertify.confirm(
- gettextCatalog.getString('This project is designed for the {{name}} board.', { name: utils.bold(projectBoard) }) + '
' +
- gettextCatalog.getString('You can load it as it is or convert it to use the {{name}} board.', { name: utils.bold(common.selectedBoard.info.label) }),
- function () {
- // Load
- setTimeout(function(){
- _load();
- },100);
- },
- function () {
-
- setTimeout(function(){
- // Convert
- project.design.board = common.selectedBoard.name;
-
- _load(true, boardMigration(projectBoard, common.selectedBoard.name));
- },100);
+
+ project = _safeUpgradeVersion(data, name);
+ utils.approveProjectBlock(profile, project, true).then((result) => {
+ if (result === 'cancel') {
+ console.log('cancelLoad');
+ utils.endBlockingTask();
+ return;
+ }
+
+ if (project.design.board !== common.selectedBoard.name) {
+ var projectBoard = boards.boardLabel(project.design.board);
+ alertify.set('confirm', 'labels', {
+ 'ok': gettextCatalog.getString('Load'),
+ 'cancel': gettextCatalog.getString('Convert')
});
- }
- else {
- _load();
- }
+ alertify.confirm(
+ gettextCatalog.getString('This project is designed for the {{name}} board.', { name: utils.bold(projectBoard) }) + '
' +
+ gettextCatalog.getString('You can load it as it is or convert it to use the {{name}} board.', { name: utils.bold(common.selectedBoard.info.label) }),
+ function () {
+ // Load
+ setTimeout(function(){
+ _load();
+ },100);
+ },
+ function () {
+ // Convert
+ setTimeout(function(){
+ project.design.board = common.selectedBoard.name;
+
+ _load(true, boardMigration(projectBoard, common.selectedBoard.name));
+ },100);
+ });
+ }
+ else {
+ _load();
+ }
+ });
function _load(reset, originalBoard) {
common.allDependencies = project.dependencies;
@@ -137,11 +141,10 @@ angular.module('icestudio')
}
var ret = graph.loadDesign(project.design, opt, function () {
-
graph.resetCommandStack();
- graph.fitContent();
alertify.success(gettextCatalog.getString('Project {{name}} loaded', { name: utils.bold(name) }));
common.hasChangesSinceBuild = true;
+ utils.endBlockingTask();
});
if (ret) {
@@ -191,7 +194,7 @@ angular.module('icestudio')
return true;
}
- function _safeLoad(data, name) {
+ function _safeUpgradeVersion(data, name) {
// Backwards compatibility
var project = {};
switch (data.version) {
@@ -241,6 +244,7 @@ angular.module('icestudio')
}
// Add current dependency
block = pruneBlock(block);
+
delete block.design.deps;
block.package.name = block.package.name || key;
block.package.description = block.package.description || key;
@@ -451,10 +455,10 @@ angular.module('icestudio')
if (!checkVersion(data.version)) {
return;
}
+
var name = utils.basename(filepath);
-
- var block = _safeLoad(data, name);
- if (block) {
+ var block = _safeUpgradeVersion(data, name);
+ if (block) {
var origPath = utils.dirname(filepath);
var destPath = utils.dirname(self.path);
// 1. Parse and find included files
@@ -467,43 +471,31 @@ angular.module('icestudio')
if (files.length > 0) {
// 2. Check project's directory
if (self.path) {
- // 3. Copy the included files
- copyIncludedFiles(files, origPath, destPath, function (success) {
- if (success) {
- // 4. Success: import block
- doImportBlock();
- }
- });
+ // 3. Block will be imported if copying the included files is successful
+ doImportBlock(files, origPath, destPath);
}
else {
alertify.confirm(gettextCatalog.getString('This import operation requires a project path. You need to save the current project. Do you want to continue?'),
function () {
$rootScope.$emit('saveProjectAs', function () {
-
- // 3. Copy the included files
-
- copyIncludedFiles(files, origPath, destPath, function (success) {
- if (success) {
- // 4. Success: import block
- doImportBlock();
- }
- });
+ // 3. Block will be imported if copying the included files is successful
+ doImportBlock(files, origPath, destPath);
});
});
}
}
else {
// No included files to copy
- // 4. Import block
doImportBlock();
}
}
- function doImportBlock() {
- self.addBlock(block);
- if (notification) {
- alertify.success(gettextCatalog.getString('Block {{name}} imported', { name: utils.bold(block.package.name) }));
- }
+ function doImportBlock(files, origPath, destPath) {
+ self.addBlock(block, files, origPath, destPath).then(() => {
+ if (notification) {
+ alertify.success(gettextCatalog.getString('Block {{name}} imported', { name: utils.bold(block.package.name) }));
+ }
+ });
}
})
.catch(function () {
@@ -571,7 +563,15 @@ angular.module('icestudio')
var block = _project.design.graph.blocks[i];
switch (block.type) {
case blocks.BASIC_INPUT:
+ if (block.data.inout === false) {
+ delete block.data.inout;
+ }
+ break;
case blocks.BASIC_OUTPUT:
+ if (block.data.inout === false) {
+ delete block.data.inout;
+ }
+ break;
case blocks.BASIC_OUTPUT_LABEL:
case blocks.BASIC_INPUT_LABEL:
case blocks.BASIC_CONSTANT:
@@ -581,6 +581,12 @@ angular.module('icestudio')
for (var j in block.data.ports.in) {
delete block.data.ports.in[j].default;
}
+ if (block.data.ports.inoutLeft && !block.data.ports.inoutLeft.length) {
+ delete block.data.ports.inoutLeft;
+ }
+ if (block.data.ports.inoutRight && !block.data.ports.inoutRight.length) {
+ delete block.data.ports.inoutRight;
+ }
break;
case blocks.BASIC_INFO:
delete block.data.text;
@@ -650,19 +656,40 @@ angular.module('icestudio')
graph.createBasicBlock(type);
};
- this.addBlock = function (block) {
- if (block) {
- block = _safeLoad(block);
- block = pruneBlock(block);
- if (block.package.name.toLowerCase().indexOf('generic-') === 0) {
- var dat = new Date();
- var seq = dat.getTime();
- block.package.otid = seq;
- }
- var type = utils.dependencyID(block);
- utils.mergeDependencies(type, block);
- graph.createBlock(type, block);
- }
+ this.addBlock = function (block, files, origPath, destPath) {
+ return new Promise((resolve, reject) => {
+ return utils.approveProjectBlock(profile, block).then((result) => {
+ if (result === 'cancel') {
+ reject('cancelImport');
+ return;
+ }
+
+ if (files) {
+ copyIncludedFiles(files, origPath, destPath, function (success) {
+ if (success) {
+ _createBlock();
+ } else {
+ reject('copyIncludedFiles');
+ }
+ });
+ } else {
+ _createBlock();
+ }
+
+ function _createBlock() {
+ block = pruneBlock(block);
+ if (block.package.name.toLowerCase().indexOf('generic-') === 0) {
+ var dat = new Date();
+ var seq = dat.getTime();
+ block.package.otid = seq;
+ }
+ var type = utils.dependencyID(block);
+ utils.mergeDependencies(type, block);
+ graph.createBlock(type, block);
+ resolve();
+ }
+ });
+ });
};
function pruneBlock(block) {
diff --git a/app/scripts/services/utils.js b/app/scripts/services/utils.js
index c7bf0e448..a28926fc1 100644
--- a/app/scripts/services/utils.js
+++ b/app/scripts/services/utils.js
@@ -4,6 +4,7 @@ angular.module('icestudio')
.service('utils', function ($rootScope,
gettextCatalog,
common,
+ blocks,
forms,
_package,
window,
@@ -1146,7 +1147,8 @@ angular.module('icestudio')
});
};
- this.pasteFromClipboard = function (callback) {
+ this.pasteFromClipboard = function (profile, callback) {
+ var _this = this;
nodeCP.paste(function (err, text) {
if (err) {
if (common.LINUX) {
@@ -1184,7 +1186,19 @@ angular.module('icestudio')
// Parse the global clipboard
var clipboard = JSON.parse(text);
if (callback && clipboard && clipboard.icestudio) {
- callback(clipboard.icestudio);
+ const block = clipboard.icestudio;
+ if (block.version === common.VERSION) {
+ _this.approveProjectBlock(profile, block).then((result) => {
+ if (result === 'cancel') {
+ console.log('cancelPaste');
+ return;
+ }
+
+ callback(block);
+ });
+ } else {
+ alertify.error(gettextCatalog.getString('Cannot paste from a different project format ({{version}})', { version: block.version }), 5);
+ }
}
}
});
@@ -1232,7 +1246,7 @@ angular.module('icestudio')
// - design.graph
// - dependencies
- var blocks = [];
+ var _blocks = [];
var wires = [];
var p = {
version: common.VERSION,
@@ -1263,7 +1277,7 @@ angular.module('icestudio')
cell.type === 'ice.Memory') {
block.size = cell.size;
}
- blocks.push(block);
+ _blocks.push(block);
} else if (cell.type === 'ice.Wire') {
var wire = {};
wire.source = {
@@ -1282,7 +1296,7 @@ angular.module('icestudio')
p.design.board = common.selectedBoard.name;
p.design.graph = {
- blocks: blocks,
+ blocks: _blocks,
wires: wires
};
@@ -1300,9 +1314,8 @@ angular.module('icestudio')
this.findSubDependencies = function (dependency) {
var subDependencies = [];
if (dependency) {
- var blocks = dependency.design.graph.blocks;
- for (var i in blocks) {
- var type = blocks[i].type;
+ for (var i in dependency.design.graph.blocks) {
+ var type = dependency.design.graph.blocks[i].type;
if (type.indexOf('basic.') === -1) {
subDependencies.push(type);
var newSubDependencies = this.findSubDependencies(common.allDependencies[type]);
@@ -1314,6 +1327,103 @@ angular.module('icestudio')
return subDependencies;
};
+ // Check for Advanced block being opened or imported: If it has tri-state, and user does not have
+ // Advanced profile setting for tri-state, user needs to approve or cancel
+ //
+ // Return 'cancel' or return 'ok' or a variant of 'ok'
+ this.approveProjectBlock = function (profile, block, isLoad) {
+ if (profile.get('allowInoutPorts') || common.allowProjectInoutPorts) {
+ return Promise.resolve('ok');
+ }
+
+ const hasInoutPorts = checkIsAnyInout(block);
+ if (!hasInoutPorts) {
+ return Promise.resolve('ok');
+ }
+
+ // user can approve by either updating profile 'allowInoutPorts' or setting
+ // flag common.allowProjectInoutPorts
+ const prompt = (isLoad ?
+ gettextCatalog.getString('You are loading a design that uses "tri-state".') :
+ gettextCatalog.getString('You are importing a block that uses "tri-state".')) +
+ ' ' +
+ gettextCatalog.getString('Tri-state (aka high-Z, bidirectional, or inout) ports are not recommended in standard designs.
You will be asked to update your Preferences (Advanced user setting) or you can just open this design on a preview basis.
Continue?');
+ return new Promise((resolve) => {
+ alertify.confirm(prompt, () => {
+ resolve('ok');
+ }, () => {
+ resolve('cancel');
+ });
+ })
+ .then((result) => {
+ if (result === 'cancel') {
+ return result;
+ }
+
+ return new Promise((resolve) => {
+ alertify.set('confirm', 'defaultFocus', 'cancel');
+ alertify.confirm(gettextCatalog.getString('Click "Yes" to allow tri-state and update Preferences:
Advanced features -> Allow tri-state connections
Click "This time" to view tri-state for this design only.'), () => {
+ profile.set('allowInoutPorts', true);
+ alertify.warning(gettextCatalog.getString('Changed Preferences: Allow tri-state connections'));
+ resolve('ok_advanced');
+ }, () => {
+ common.allowProjectInoutPorts = true;
+ alertify.warning(gettextCatalog.getString('Viewing tri-state'));
+ resolve('ok_this_time');
+ }).set('labels', {
+ ok: gettextCatalog.getString('Yes'),
+ cancel: gettextCatalog.getString('This time')
+ });
+ })
+ .then((result) => {
+ alertify.set('confirm', 'defaultFocus', 'ok');
+ alertify.set('confirm', 'labels', {
+ ok: gettextCatalog.getString('OK'),
+ cancel: gettextCatalog.getString('Cancel')
+ });
+ return result;
+ });
+ });
+ };
+
+ function checkIsAnyInout(project) {
+ if (_checkIsAnyInout(project)) {
+ return true;
+ }
+ for (var d in project.dependencies) {
+ if (_checkIsAnyInout(project.dependencies[d])) {
+ return true;
+ }
+ }
+
+ function _checkIsAnyInout(_project) {
+ for (var i in _project.design.graph.blocks) {
+ var block = _project.design.graph.blocks[i];
+ switch (block.type) {
+ case blocks.BASIC_INPUT:
+ case blocks.BASIC_OUTPUT:
+ if (block.data.inout) {
+ return true;
+ }
+ break;
+ case blocks.BASIC_CODE:
+ if (block.data.ports.inoutLeft && block.data.ports.inoutLeft.length) {
+ return true;
+ }
+ if (block.data.ports.inoutRight && block.data.ports.inoutRight.length) {
+ return true;
+ }
+ break;
+ default:
+ // Generic block
+ break;
+ }
+ }
+ return false;
+ }
+ return false;
+ }
+
this.hasInputRule = function (port, apply) {
apply = (apply === undefined) ? true : apply;
var _default;
diff --git a/app/views/advanced.html b/app/views/advanced.html
new file mode 100644
index 000000000..130ed4f5d
--- /dev/null
+++ b/app/views/advanced.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+