From c28050e812216032f2b5635cd22d2d222a06f36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Sat, 14 Apr 2018 01:33:17 +0200 Subject: [PATCH 01/27] Add Memory block in the menu --- app/scripts/services/blocks.js | 34 ++++++++++++++++++++++++++++++++++ app/views/menu.html | 3 +++ 2 files changed, 37 insertions(+) diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index 212144883..e59864094 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -31,6 +31,9 @@ angular.module('icestudio') case 'basic.constant': newBasicConstant(callback); break; + case 'basic.memory': + newBasicMemory(callback); + break; case 'basic.code': newBasicCode(callback); break; @@ -239,6 +242,19 @@ angular.module('icestudio') }); } + function newBasicMemory(callback) { + var blockInstance = { + id: null, + data: { list: '' }, + type: 'basic.memory', + position: { x: 40 * gridsize, y: 36 * gridsize }, + size: { width: 192, height: 128 } + }; + if (callback) { + callback([loadBasicMemory(blockInstance)]); + } + } + function newBasicCode(callback, block) { var blockInstance = { id: null, @@ -505,6 +521,24 @@ angular.module('icestudio') return cell; } + function loadBasicMemory(instance, disabled) { + var bottomPorts = [{ + id: 'constant-out', + name: '', + label: '' + }]; + var cell = new joint.shapes.ice.Constant({ + id: instance.id, + blockType: instance.type, + data: instance.data, + position: instance.position, + size: instance.size, + disabled: disabled, + bottomPorts: bottomPorts + }); + return cell; + } + function loadBasicCode(instance, disabled) { var port; var leftPorts = []; diff --git a/app/views/menu.html b/app/views/menu.html index 970050714..264bf27ee 100644 --- a/app/views/menu.html +++ b/app/views/menu.html @@ -349,6 +349,9 @@
{{ 'Constant' | translate }}
+ +
{{ 'Memory' | translate }} +
{{ 'Code' | translate }}
From 313c0a07583cc9337b3d943c7f22d4095369fc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Sat, 14 Apr 2018 01:47:30 +0200 Subject: [PATCH 02/27] Remove unused initial blocks position --- app/scripts/services/blocks.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index e59864094..e1198fa1c 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -50,7 +50,7 @@ angular.module('icestudio') id: null, data: {}, type: 'basic.input', - position: { x: 4 * gridsize, y: 4 * gridsize } + position: { x: 0, y: 0 } }; utils.inputcheckbox2prompt([ gettextCatalog.getString('Enter the input blocks'), @@ -115,7 +115,7 @@ angular.module('icestudio') id: null, data: {}, type: 'basic.output', - position: { x: 95 * gridsize, y: 4 * gridsize } + position: { x: 0, y: 0 } }; utils.inputcheckboxprompt([ gettextCatalog.getString('Enter the output blocks'), @@ -189,7 +189,7 @@ angular.module('icestudio') id: null, data: {}, type: 'basic.constant', - position: { x: 20 * gridsize, y: 4 * gridsize } + position: { x: 0, y: 0 } }; utils.inputcheckboxprompt([ gettextCatalog.getString('Enter the constant blocks'), @@ -247,7 +247,7 @@ angular.module('icestudio') id: null, data: { list: '' }, type: 'basic.memory', - position: { x: 40 * gridsize, y: 36 * gridsize }, + position: { x: 0, y: 0 }, size: { width: 192, height: 128 } }; if (callback) { @@ -264,7 +264,7 @@ angular.module('icestudio') ports: { in: [], out: [] } }, type: 'basic.code', - position: { x: 40 * gridsize, y: 16 * gridsize }, + position: { x: 0, y: 0 }, size: { width: 192, height: 128 } }; var defaultValues = [ @@ -412,7 +412,7 @@ angular.module('icestudio') id: null, data: { info: '', readonly: false }, type: 'basic.info', - position: { x: 40 * gridsize, y: 36 * gridsize }, + position: { x: 0, y: 0 }, size: { width: 192, height: 128 } }; if (callback) { @@ -424,7 +424,7 @@ angular.module('icestudio') var blockInstance = { id: null, type: type, - position: { x: 10 * gridsize, y: 16 * gridsize } + position: { x: 0, y: 0 } }; if (resultAlert) { resultAlert.dismiss(false); From b8acb7fc44b20aa16b9f6ea6667f07145a2e3d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Sat, 14 Apr 2018 02:10:14 +0200 Subject: [PATCH 03/27] Add Memory block --- app/scripts/graphics/joint.shapes.js | 282 +++++++++++++++++++++++++++ app/scripts/services/blocks.js | 4 +- app/scripts/services/utils.js | 6 +- app/styles/design.css | 24 +++ 4 files changed, 312 insertions(+), 4 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index c1a79c989..47e7a7f70 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -865,6 +865,288 @@ joint.shapes.ice.ConstantView = joint.shapes.ice.ModelView.extend({ }); +// Memory block + +joint.shapes.ice.Memory = joint.shapes.ice.Model.extend({ + defaults: joint.util.deepSupplement({ + type: 'ice.Memory', + size: { + width: 384, + height: 256 + } + }, joint.shapes.ice.Model.prototype.defaults) +}); + +joint.shapes.ice.MemoryView = joint.shapes.ice.ModelView.extend({ + + initialize: function() { + _.bindAll(this, 'updateBox'); + joint.dia.ElementView.prototype.initialize.apply(this, arguments); + + var id = sha1(this.model.get('id')).toString().substring(0, 6); + var blockLabel = 'block' + id; + var editorLabel = 'editor' + id; + this.$box = $(joint.util.template( + '\ +
\ +
\ + \ +
\ +
\ + ' + )()); + + this.model.on('change', this.updateBox, this); + this.model.on('remove', this.removeBox, this); + + this.updateBox(); + this.updating = false; + this.prevZoom = 0; + + this.listenTo(this.model, 'process:ports', this.update); + joint.dia.ElementView.prototype.initialize.apply(this, arguments); + + this.selector = this.$box.find('#' + editorLabel); + + // Prevent paper from handling pointerdown. + this.selector.on('mousedown click', function(event) { event.stopPropagation(); }); + + this.deltas = []; + this.counter = 0; + this.timer = null; + var undoGroupingInterval = 200; + + var self = this; + this.editor = ace.edit(this.selector[0]); + this.editor.$blockScrolling = Infinity; + this.editor.commands.removeCommand('undo'); + this.editor.commands.removeCommand('redo'); + this.editor.commands.removeCommand('touppercase'); + this.editor.session.on('change', function(delta) { + if (!self.updating) { + // Check consecutive-change interval + if (Date.now() - self.counter < undoGroupingInterval) { + clearTimeout(self.timer); + } + // Update deltas + self.deltas = self.deltas.concat([delta]); + // Launch timer + self.timer = setTimeout(function() { + var deltas = JSON.parse(JSON.stringify(self.deltas)); + // Set deltas + self.model.set('deltas', deltas); + // Reset deltas + self.deltas = []; + // Set data.list + self.model.attributes.data.list = self.editor.session.getValue(); + }, undoGroupingInterval); + // Reset counter + self.counter = Date.now(); + } + }); + this.editor.on('focus', function() { + $(document).trigger('disableSelected'); + self.editor.setHighlightActiveLine(true); + self.editor.setHighlightGutterLine(true); + // Show cursor + self.editor.renderer.$cursorLayer.element.style.opacity = 1; + }); + this.editor.on('blur', function() { + var selection = self.editor.session.selection; + if (selection) { + selection.clearSelection(); + } + self.editor.setHighlightActiveLine(false); + self.editor.setHighlightGutterLine(false); + // Hide cursor + self.editor.renderer.$cursorLayer.element.style.opacity = 0; + }); + this.editor.on('paste', function(e) { + if (e.text.startsWith('{"icestudio":')) { + // Prevent paste blocks + e.text = ''; + } + }); + this.editor.on('mousewheel', function(event) { + // Stop mousewheel event propagation when target is active + if (document.activeElement.parentNode.id === self.selector.attr('id')) { + // Enable only scroll + event.stopPropagation(); + } + else { + // Enable only zoom + event.preventDefault(); + } + }); + + this.setupResizer(); + + // Apply data + this.apply({ ini: true }); + }, + + applyValue: function(opt) { + this.updating = true; + + var dontselect = false; + var data = this.model.get('data'); + var deltas = this.model.get('deltas'); + + opt = opt || {}; + + switch (opt.attribute) { + case 'deltas': + if (deltas) { + var changes = [{ + group: 'doc', + deltas: deltas + }]; + if (opt.undo) { + this.editor.session.undoChanges(changes, dontselect); + } + else { + this.editor.session.redoChanges(changes, dontselect); + } + } + break; + case 'data': + + break; + default: + break; + } + if (opt.ini) { + this.editor.session.setValue(data.list); + } + else { + // Set data.list + this.model.attributes.data.list = this.editor.session.getValue(); + } + setTimeout(function(self) { + self.updating = false; + }, 10, this); + }, + + apply: function(opt) { + this.applyValue(opt); + }, + + update: function() { + this.renderPorts(); + this.editor.setReadOnly(this.model.get('disabled')); + joint.dia.ElementView.prototype.update.apply(this, arguments); + }, + + updateBox: function() { + var i, port; + var bbox = this.model.getBBox(); + var data = this.model.get('data'); + var state = this.model.get('state'); + var rules = this.model.get('rules'); + var leftPorts = this.model.get('leftPorts'); + var rightPorts = this.model.get('rightPorts'); + + // Set font size + if (this.editor) { + if (this.prevZoom !== state.zoom) { + this.prevZoom = state.zoom; + // Scale border + this.$box.find('.memory-editor').css({ + margin: 8 * state.zoom, + 'border-radius': 5 * state.zoom + }); + // Scale padding + this.$box.find('.ace_text-layer').css('padding', '0px ' + Math.round(4 * state.zoom) + 'px'); + // Scale gutters + var rule = getCSSRule('.ace_folding-enabled > .ace_gutter-cell'); + if (rule) { + rule.style.paddingLeft = Math.round(19 * state.zoom) + 'px'; + rule.style.paddingRight = Math.round(13 * state.zoom) + 'px'; + } + // Scale font size + this.editor.setFontSize(Math.round(aceFontSize * state.zoom)); + // Scale cursor + this.editor.renderer.$cursorLayer.$padding = Math.round(4 * state.zoom); + } + this.editor.resize(); + } + + function getCSSRule(ruleName) { + if (document.styleSheets) { + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + var ii = 0; + var cssRule = false; + do { + if (styleSheet.cssRules) { + cssRule = styleSheet.cssRules[ii]; + } else { + cssRule = styleSheet.rules[ii]; + } + if (cssRule) { + if (cssRule.selectorText === ruleName) { + return cssRule; + } + } + ii++; + } while (cssRule); + } + } + return false; + } + + // Set ports width + var width = WIRE_WIDTH * state.zoom; + this.$('.port-wire').css('stroke-width', width); + // Set buses + for (i in leftPorts) { + port = leftPorts[i]; + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); + } + } + for (i in rightPorts) { + port = rightPorts[i]; + if (port.size > 1) { + this.$('#port-wire-' + port.id).css('stroke-width', width * 3); + } + } + // Render rules + if (data && data.ports && data.ports.in) { + for (i in data.ports.in) { + port = data.ports.in[i]; + if (rules && port.default && port.default.apply) { + this.$('#port-default-' + port.name).css('display', 'inline'); + this.$('#port-default-wire-' + port.name).css('stroke-width', width); + this.$('#port-default-rect-' + port.name).css('stroke-width', state.zoom); + } + else { + this.$('#port-default-' + port.name).css('display', 'none'); + } + } + } + + this.$box.css({ + 'border-radius': 5 * state.zoom + }); + this.$box.css({ width: bbox.width * state.zoom, + height: bbox.height * state.zoom, + left: bbox.x * state.zoom + state.pan.x, + top: bbox.y * state.zoom + state.pan.y }); + // 'border-width': 2 * state.zoom: problem int instead of float + } +}); + + // Code block joint.shapes.ice.Code = joint.shapes.ice.Model.extend({ diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index e1198fa1c..5c1d53049 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -248,7 +248,7 @@ angular.module('icestudio') data: { list: '' }, type: 'basic.memory', position: { x: 0, y: 0 }, - size: { width: 192, height: 128 } + size: { width: 128, height: 144 } }; if (callback) { callback([loadBasicMemory(blockInstance)]); @@ -527,7 +527,7 @@ angular.module('icestudio') name: '', label: '' }]; - var cell = new joint.shapes.ice.Constant({ + var cell = new joint.shapes.ice.Memory({ id: instance.id, blockType: instance.type, data: instance.data, diff --git a/app/scripts/services/utils.js b/app/scripts/services/utils.js index ee85b0283..89d45fb73 100644 --- a/app/scripts/services/utils.js +++ b/app/scripts/services/utils.js @@ -968,7 +968,8 @@ angular.module('icestudio') cell.type === 'ice.Output' || cell.type === 'ice.Code' || cell.type === 'ice.Info' || - cell.type === 'ice.Constant') { + cell.type === 'ice.Constant' || + cell.type === 'ice.Memory') { var block = {}; block.id = cell.id; block.type = cell.blockType; @@ -976,7 +977,8 @@ angular.module('icestudio') block.position = cell.position; if (cell.type === 'ice.Generic' || cell.type === 'ice.Code' || - cell.type === 'ice.Info') { + cell.type === 'ice.Info' || + cell.type === 'ice.Memory') { block.size = cell.size; } blocks.push(block); diff --git a/app/styles/design.css b/app/styles/design.css index 6badec6d0..7bc60c87c 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -361,6 +361,30 @@ g { outline: none; } +.memory-block { + position: absolute; + background: #FBF0C9; + border-radius: 5px; + border: 1px solid #777; + pointer-events: none; + -webkit-user-select: none; + user-select: none; + z-index: 0; + transition: opacity 0.2s; +} + +.memory-block .memory-editor { + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + margin: 8px; + border-radius: 5px; + border: 1px solid #BBB; + pointer-events: auto; +} + .code-block { position: absolute; background: #C0DFEB; From 85f94b3f2641c5c20f5cee16572f0c7df7f261cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Sat, 14 Apr 2018 02:17:05 +0200 Subject: [PATCH 04/27] Save/Load Memory block data --- app/scripts/services/blocks.js | 2 ++ app/scripts/services/project.js | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index 5c1d53049..9c8ad0567 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -454,6 +454,8 @@ angular.module('icestudio') return loadBasicOutput(instance, disabled); case 'basic.constant': return loadBasicConstant(instance, disabled); + case 'basic.memory': + return loadBasicMemory(instance, disabled); case 'basic.code': return loadBasicCode(instance, disabled); case 'basic.info': diff --git a/app/scripts/services/project.js b/app/scripts/services/project.js index 7f5727b3e..436864551 100644 --- a/app/scripts/services/project.js +++ b/app/scripts/services/project.js @@ -503,15 +503,16 @@ angular.module('icestudio') case 'basic.input': case 'basic.output': case 'basic.constant': - break; - case 'basic.info': - delete block.data.text; + case 'basic.memory': break; case 'basic.code': for (var j in block.data.ports.in) { delete block.data.ports.in[j].default; } break; + case 'basic.info': + delete block.data.text; + break; default: // Generic block delete block.data; From d56665c27e492c075724dcf35fdad0cb05f5b7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Sat, 14 Apr 2018 03:08:44 +0200 Subject: [PATCH 05/27] Improve Memory block style --- app/scripts/graphics/joint.shapes.js | 1 + app/styles/design.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index 47e7a7f70..7a451ba7b 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -899,6 +899,7 @@ joint.shapes.ice.MemoryView = joint.shapes.ice.ModelView.extend({ ' + editorLabel + '.setOption("firstLineNumber", 0);\ ' + editorLabel + '.setAutoScrollEditorIntoView(true);\ ' + editorLabel + '.renderer.$cursorLayer.element.style.opacity = 0;\ + ' + editorLabel + '.renderer.$gutter.style.background = "#F0F0F0";\ \
\
\ diff --git a/app/styles/design.css b/app/styles/design.css index 7bc60c87c..7c7d5be34 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -381,7 +381,7 @@ g { bottom: -2px; margin: 8px; border-radius: 5px; - border: 1px solid #BBB; + border: 1px solid #D3D3D3; pointer-events: auto; } From 35b545241776f63d281ff63d33b2946b1d023994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 16 Apr 2018 09:58:18 +0200 Subject: [PATCH 06/27] Include Memory block in the compiler --- app/scripts/services/blocks.js | 2 +- app/scripts/services/compiler.js | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index 9c8ad0567..f2dd368e7 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -525,7 +525,7 @@ angular.module('icestudio') function loadBasicMemory(instance, disabled) { var bottomPorts = [{ - id: 'constant-out', + id: 'memory-out', name: '', label: '' }]; diff --git a/app/scripts/services/compiler.js b/app/scripts/services/compiler.js index 5bc53b5aa..71a26aa12 100644 --- a/app/scripts/services/compiler.js +++ b/app/scripts/services/compiler.js @@ -126,6 +126,13 @@ angular.module('icestudio') value: block.data.value }); } + else if (block.type === 'basic.memory') { + var name = utils.digestId(block.id); + params.push({ + name: name, + value: '"' + name + '.list"' + }); + } } return params; @@ -169,7 +176,8 @@ angular.module('icestudio') for (w in graph.wires) { var wire = graph.wires[w]; - if (wire.source.port === 'constant-out') { + if (wire.source.port === 'constant-out' || + wire.source.port === 'memory-out') { // Local Parameters var constantBlock = findBlock(wire.source.block, graph); var paramValue = utils.digestId(constantBlock.id); @@ -192,7 +200,8 @@ angular.module('icestudio') } else if (block.type === 'basic.output') { if (wire.target.block === block.id) { - if (wire.source.port === 'constant-out') { + if (wire.source.port === 'constant-out' || + wire.source.port === 'memory-out') { // connections.assign.push('assign ' + digestId(block.id) + ' = p' + w + ';'); } else { @@ -216,7 +225,8 @@ angular.module('icestudio') var wj = graph.wires[j]; if (wi.source.block === wj.source.block && wi.source.port === wj.source.port && - wi.source.port !== 'constant-out') { + wi.source.port !== 'constant-out' && + wi.source.port !== 'memory-out') { content.push('assign w' + i + ' = w' + j + ';'); } } @@ -240,6 +250,7 @@ angular.module('icestudio') if (block.type !== 'basic.input' && block.type !== 'basic.output' && block.type !== 'basic.constant' && + block.type !== 'basic.memory' && block.type !== 'basic.info') { // Header @@ -258,7 +269,8 @@ angular.module('icestudio') for (w in graph.wires) { wire = graph.wires[w]; if ((block.id === wire.target.block) && - (wire.source.port === 'constant-out')) { + (wire.source.port === 'constant-out' || + wire.source.port === 'memory-out')) { var paramName = wire.target.port; if (block.type !== 'basic.code') { paramName = utils.digestId(paramName); @@ -288,7 +300,8 @@ angular.module('icestudio') connectPort(wire.source.port, portsNames, ports, block); } if (block.id === wire.target.block) { - if (wire.source.port !== 'constant-out') { + if (wire.source.port !== 'constant-out' && + wire.source.port !== 'memory-out') { connectPort(wire.target.port, portsNames, ports, block); } } @@ -474,8 +487,6 @@ angular.module('icestudio') if (name === 'main' && opt.boardRules) { - // Initialize output pins - var initPins = opt.initPins || getInitPins(project); var n = initPins.length; From 1236b9e51d9e354b1ede824e1dfa0c36d5ddb508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 16 Apr 2018 10:24:07 +0200 Subject: [PATCH 07/27] Refactor compiler.generate function --- app/scripts/controllers/menu.js | 3 ++- app/scripts/services/compiler.js | 43 +++++++++++++++++++++++--------- app/scripts/services/project.js | 4 +-- app/scripts/services/tools.js | 21 ++++++++++++---- 4 files changed, 51 insertions(+), 20 deletions(-) diff --git a/app/scripts/controllers/menu.js b/app/scripts/controllers/menu.js index 059c3cdf3..8bae3ab8e 100644 --- a/app/scripts/controllers/menu.js +++ b/app/scripts/controllers/menu.js @@ -196,9 +196,10 @@ angular.module('icestudio') function exportFromCompiler(id, name, ext) { checkGraph() .then(function() { + // TODO: export list files utils.saveDialog('#input-export-' + id, ext, function(filepath) { // Save the compiler result - var data = project.compile(id); + var data = project.compile(id)[0].content; utils.saveFile(filepath, data) .then(function() { alertify.success(gettextCatalog.getString('{{name}} exported', { name: name })); diff --git a/app/scripts/services/compiler.js b/app/scripts/services/compiler.js index 71a26aa12..23bef6ae8 100644 --- a/app/scripts/services/compiler.js +++ b/app/scripts/services/compiler.js @@ -7,29 +7,48 @@ angular.module('icestudio') _package) { this.generate = function(target, project, opt) { - var code = ''; + var content = ''; + var files = []; switch(target) { case 'verilog': - code += header('//', opt); - code += '`default_nettype none\n'; - code += verilogCompiler('main', project, opt); + content += header('//', opt); + content += '`default_nettype none\n'; + content += verilogCompiler('main', project, opt); + files.push({ + name: 'main.v', + content: content + }); break; case 'pcf': - code += header('#', opt); - code += pcfCompiler(project, opt); + content += header('#', opt); + content += pcfCompiler(project, opt); + files.push({ + name: 'main.pcf', + content: content + }); break; + case 'list': + files = listCompiler(project); case 'testbench': - code += header('//', opt); - code += testbenchCompiler(project); + content += header('//', opt); + content += testbenchCompiler(project); + files.push({ + name: 'main_tb.v', + content: content + }); break; case 'gtkwave': - code += header('[*]', opt); - code += gtkwaveCompiler(project); + content += header('[*]', opt); + content += gtkwaveCompiler(project); + files.push({ + name: 'main_tb.gtkw', + content: content + }); break; default: - code += ''; + break; } - return code; + return files; }; function header(comment, opt) { diff --git a/app/scripts/services/project.js b/app/scripts/services/project.js index 436864551..86ebba1a2 100644 --- a/app/scripts/services/project.js +++ b/app/scripts/services/project.js @@ -314,7 +314,7 @@ angular.module('icestudio') var origPath = utils.dirname(this.filepath); var destPath = utils.dirname(filepath); // 1. Parse and find included files - var code = compiler.generate('verilog', project); + var code = compiler.generate('verilog', project)[0].content; var files = utils.findIncludedFiles(code); // Are there included files? if (files.length > 0) { @@ -390,7 +390,7 @@ angular.module('icestudio') var origPath = utils.dirname(filepath); var destPath = utils.dirname(self.path); // 1. Parse and find included files - var code = compiler.generate('verilog', block); + var code = compiler.generate('verilog', block)[0].content; var files = utils.findIncludedFiles(code); // Are there included files? if (files.length > 0) { diff --git a/app/scripts/services/tools.js b/app/scripts/services/tools.js index 2a33b2a6f..d49d621c2 100644 --- a/app/scripts/services/tools.js +++ b/app/scripts/services/tools.js @@ -158,11 +158,22 @@ angular.module('icestudio') opt.initPorts = compiler.getInitPorts(project.get()); opt.initPins = compiler.getInitPins(project.get()); } - var verilog = compiler.generate('verilog', project.get(), opt); - var pcf = compiler.generate('pcf', project.get(), opt); - nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, 'main.v'), verilog, 'utf8'); - nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, 'main.pcf'), pcf, 'utf8'); - resolve(verilog); + + // Verilog file + var verilogFile = compiler.generate('verilog', project.get(), opt)[0]; + nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, verilogFile.name), verilogFile.content, 'utf8'); + + // PCF file + var pcfFile = compiler.generate('pcf', project.get(), opt)[0]; + nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, pcfFile.name), pcfFile.content, 'utf8'); + + // List files + var listFiles = compiler.generate('list', project.get()); + for (var i in listFiles) { + var listFile = listFiles[i]; + nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, listFile.name), listFile.content, 'utf8'); + } + resolve(verilogFile.content); }); } From 2a81cfedfa5dc8dc9cec921efe574f20dda4484b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 16 Apr 2018 10:50:16 +0200 Subject: [PATCH 08/27] Add listCompiler function --- app/scripts/services/compiler.js | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/app/scripts/services/compiler.js b/app/scripts/services/compiler.js index 23bef6ae8..70b4f50cc 100644 --- a/app/scripts/services/compiler.js +++ b/app/scripts/services/compiler.js @@ -29,6 +29,7 @@ angular.module('icestudio') break; case 'list': files = listCompiler(project); + break; case 'testbench': content += header('//', opt); content += testbenchCompiler(project); @@ -649,6 +650,40 @@ angular.module('icestudio') return code; } + function listCompiler(project) { + var i; + var listFiles = []; + + if (project && + project.design && + project.design.graph) { + + var blocks = project.design.graph.blocks; + var dependencies = project.dependencies; + + // Find in blocks + + for (i in blocks) { + var block = blocks[i]; + if (block.type === 'basic.memory') { + listFiles.push({ + name: utils.digestId(block.id) + '.list', + content: block.data.list + }); + } + } + + // Find in dependencies + + for (i in dependencies) { + var dependency = dependencies[i]; + listFiles = listFiles.concat(listCompiler(dependency)); + } + } + + return listFiles; + } + function testbenchCompiler(project) { var i, o, p; var code = ''; From 51a18bd33e40723b1147e4b48edfc7317a69d984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 16 Apr 2018 11:03:55 +0200 Subject: [PATCH 09/27] Load only external resources. Skip loading internal resources --- app/scripts/services/tools.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/scripts/services/tools.js b/app/scripts/services/tools.js index d49d621c2..14a70a9c7 100644 --- a/app/scripts/services/tools.js +++ b/app/scripts/services/tools.js @@ -76,9 +76,9 @@ angular.module('icestudio') } return generateCode(); }) - .then(function(code) { - sourceCode = code; - return syncResources(code); + .then(function(output) { + sourceCode = output.code; + return syncResources(output.code, output.internalResources); }) .then(function() { var hostname = profile.get('remoteHostname'); @@ -173,11 +173,14 @@ angular.module('icestudio') var listFile = listFiles[i]; nodeFs.writeFileSync(nodePath.join(common.BUILD_DIR, listFile.name), listFile.content, 'utf8'); } - resolve(verilogFile.content); + resolve({ + code: verilogFile.content, + internalResources: listFiles.map(function (res) { return res.name; }) + }); }); } - function syncResources(code) { + function syncResources(code, internalResources) { return new Promise(function(resolve, reject) { // Remove resources removeFiles(resources); @@ -188,6 +191,8 @@ angular.module('icestudio') resources = resources.concat(findInlineFiles(code, reject)); // Sync resources resources = _.uniq(resources); + // Remove internal files + resources = _.difference(resources, internalResources); syncFiles(resources, reject); resolve(); }); From 71229deee98e6dbe88b7e2e13b886b80268e30ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Mon, 16 Apr 2018 19:52:49 +0200 Subject: [PATCH 10/27] Reconnect memory-block connections when code-block interface changes --- app/scripts/services/blocks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/services/blocks.js b/app/scripts/services/blocks.js index f2dd368e7..17a7cc7e6 100644 --- a/app/scripts/services/blocks.js +++ b/app/scripts/services/blocks.js @@ -984,8 +984,8 @@ angular.module('icestudio') var source = wire.get('source'); var target = wire.get('target'); if ((source.id === cell.id && containsPort(source.port, size, cell.get('rightPorts'))) || - (target.id === cell.id && containsPort(target.port, size, cell.get('leftPorts')) && source.port !== 'constant-out') || - (target.id === cell.id && containsPort(target.port, size, cell.get('topPorts')) && source.port === 'constant-out')) + (target.id === cell.id && containsPort(target.port, size, cell.get('leftPorts')) && (source.port !== 'constant-out' && source.port !== 'memory-out')) || + (target.id === cell.id && containsPort(target.port, size, cell.get('topPorts')) && (source.port === 'constant-out' || source.port === 'memory-out'))) { graph.addCell(wire); } From 3d130d0448c875494ec45d943dc500b62dfdbaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 17 Apr 2018 23:30:36 +0200 Subject: [PATCH 11/27] Improve blocks replacement. Add Constant <-> Memory blocks replacement --- app/scripts/services/graph.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/scripts/services/graph.js b/app/scripts/services/graph.js index 7c29cae4c..16bbc94b0 100644 --- a/app/scripts/services/graph.js +++ b/app/scripts/services/graph.js @@ -478,21 +478,26 @@ angular.module('icestudio') } function findLowerBlock(upperBlock) { - if (upperBlock.get('type') !== 'ice.Generic' && - upperBlock.get('type') !== 'ice.Code' && - upperBlock.get('type') !== 'ice.Input') { + if (upperBlock.get('type') === 'ice.Info') { return; } var blocks = graph.findModelsUnderElement(upperBlock); - // There is at least one model ice.Generic under the upperModel - if (blocks.length <= 0) { + // There is at least one model under the upper block + if (blocks.length === 0) { return; } // Get the first model found var lowerBlock = blocks[0]; - if (lowerBlock.get('type') !== 'ice.Generic' && - lowerBlock.get('type') !== 'ice.Code' && - lowerBlock.get('type') !== 'ice.Input') { + var validReplacements = { + 'ice.Generic': ['ice.Code', 'ice.Input', 'ice.Output'], + 'ice.Code': ['ice.Generic', 'ice.Input', 'ice.Output'], + 'ice.Input': ['ice.Generic', 'ice.Code'], + 'ice.Output': ['ice.Generic', 'ice.Code'], + 'ice.Constant': ['ice.Memory'], + 'ice.Memory': ['ice.Constant'] + }[lowerBlock.get('type')]; + // Check if the upper block is a valid replacement + if (validReplacements.indexOf(upperBlock.get('type')) === -1) { return; } return lowerBlock; From 00233468aa4abf2994296cf2d39100a6093eae52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 17 Apr 2018 23:43:40 +0200 Subject: [PATCH 12/27] Add name and local to the Memory block --- app/scripts/graphics/joint.shapes.js | 30 ++++++++- app/scripts/services/blocks.js | 96 ++++++++++++++++++++++++++-- app/scripts/services/project.js | 6 ++ app/scripts/services/tools.js | 1 + app/styles/design.css | 25 +++++++- 5 files changed, 149 insertions(+), 9 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index 7a451ba7b..5b3cf2cba 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -854,8 +854,8 @@ joint.shapes.ice.ConstantView = joint.shapes.ice.ModelView.extend({ apply: function() { this.applyName(); - this.applyValue(); this.applyLocal(); + this.applyValue(); }, update: function() { @@ -889,6 +889,8 @@ joint.shapes.ice.MemoryView = joint.shapes.ice.ModelView.extend({ this.$box = $(joint.util.template( '\
\ +

\ + \
\ \ +
\ +
\ +
\
\
\ ' @@ -1572,8 +1571,9 @@ joint.shapes.ice.InfoView = joint.shapes.ice.ModelView.extend({ this.updateBox(); this.updating = false; - this.textSelector = this.$box.find('#' + textLabel); - this.editorSelector = this.$box.find('#' + editorLabel); + this.textSelector = this.$box.find('.info-text'); + this.editorSelector = this.$box.find('.info-editor'); + this.contentSelector = this.$box.find('.info-content'); // Prevent paper from handling pointerdown. this.editorSelector.on('mousedown click', function(event) { event.stopPropagation(); }); @@ -1697,6 +1697,7 @@ joint.shapes.ice.InfoView = joint.shapes.ice.ModelView.extend({ this.$box.addClass('info-block-readonly'); this.textSelector.removeClass('hidden'); this.editorSelector.addClass('hidden'); + this.contentSelector.addClass('hidden'); this.disableResizer(); // Clear selection var selection = this.editor.session.selection; @@ -1708,6 +1709,7 @@ joint.shapes.ice.InfoView = joint.shapes.ice.ModelView.extend({ this.$box.removeClass('info-block-readonly'); this.textSelector.addClass('hidden'); this.editorSelector.removeClass('hidden'); + this.contentSelector.removeClass('hidden'); this.enableResizer(); } }, @@ -1769,11 +1771,15 @@ joint.shapes.ice.InfoView = joint.shapes.ice.ModelView.extend({ }); } else if (this.editor) { - // Scale border - // This is required because this.editorSelector may be not available + // Scale editor this.$box.find('.info-editor').css({ + top: -2 * state.zoom, + left: -2 * state.zoom, + right: -2 * state.zoom, + bottom: -2 * state.zoom, margin: 8 * state.zoom, - 'border-radius': 5 * state.zoom + 'border-radius': 5 * state.zoom, + 'border-width': state.zoom + 0.5 }); // Scale padding this.$box.find('.ace_text-layer').css('padding', '0px ' + Math.round(4 * state.zoom) + 'px'); @@ -1784,12 +1790,21 @@ joint.shapes.ice.InfoView = joint.shapes.ice.ModelView.extend({ this.editor.resize(); } + // Render content + this.$box.find('.info-content').css({ + left: bbox.width / 2.0 * (state.zoom - 1), + top: bbox.height / 2.0 * (state.zoom - 1), + width: bbox.width, + height: bbox.height, + transform: 'scale(' + state.zoom + ')' + }); + + // Render block this.$box.css({ left: bbox.x * state.zoom + state.pan.x, top: bbox.y * state.zoom + state.pan.y, width: bbox.width * state.zoom, - height: bbox.height * state.zoom, - 'border-radius': 5 * state.zoom + height: bbox.height * state.zoom }); }, diff --git a/app/styles/design.css b/app/styles/design.css index e20accf5d..04e8bd2f3 100644 --- a/app/styles/design.css +++ b/app/styles/design.css @@ -436,45 +436,47 @@ g { .info-block { position: absolute; - background: #DDD; - border-radius: 5px; - border: 1px solid #777; pointer-events: none; - -webkit-user-select: none; - user-select: none; z-index: 0; transition: opacity 0.2s; } -.info-block-readonly { - background: transparent; - border: 1px solid transparent; +.info-block .info-content { + position: absolute; + background: #DDD; + border-radius: 5px; + border: 1px solid #777; } -.info-block .info-text { +.info-block .info-editor { position: absolute; - overflow: visible; - background: transparent; top: -2px; left: -2px; right: -2px; bottom: -2px; margin: 8px; border-radius: 5px; + border: 1px solid #AAA; + pointer-events: auto; +} + +.info-block-readonly { + background: transparent; border: 1px solid transparent; - pointer-events: none; } -.info-block .info-editor { +.info-block .info-text { position: absolute; + overflow: visible; + background: transparent; top: -2px; left: -2px; right: -2px; bottom: -2px; margin: 8px; border-radius: 5px; - border: 1px solid #BBB; - pointer-events: auto; + border: 1px solid transparent; + pointer-events: none; } .joint-highlight-stroke.joint-theme-default { From f4c3be3a0e1514581caa0487b7d29827bfbc9202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Arroyo=20Torrens?= Date: Tue, 15 May 2018 19:43:15 +0200 Subject: [PATCH 27/27] Unify getCSSRule function --- app/scripts/graphics/joint.shapes.js | 72 ++++++++++------------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/app/scripts/graphics/joint.shapes.js b/app/scripts/graphics/joint.shapes.js index b42b6ee40..3391101cd 100644 --- a/app/scripts/graphics/joint.shapes.js +++ b/app/scripts/graphics/joint.shapes.js @@ -1120,30 +1120,6 @@ joint.shapes.ice.MemoryView = joint.shapes.ice.ModelView.extend({ this.editor.resize(); } - function getCSSRule(ruleName) { - if (document.styleSheets) { - for (var i = 0; i < document.styleSheets.length; i++) { - var styleSheet = document.styleSheets[i]; - var ii = 0; - var cssRule = false; - do { - if (styleSheet.cssRules) { - cssRule = styleSheet.cssRules[ii]; - } else { - cssRule = styleSheet.rules[ii]; - } - if (cssRule) { - if (cssRule.selectorText === ruleName) { - return cssRule; - } - } - ii++; - } while (cssRule); - } - } - return false; - } - // Set ports width var width = WIRE_WIDTH * state.zoom; this.$('.port-wire').css('stroke-width', width); @@ -1446,30 +1422,6 @@ joint.shapes.ice.CodeView = joint.shapes.ice.ModelView.extend({ this.editor.resize(); } - function getCSSRule(ruleName) { - if (document.styleSheets) { - for (var i = 0; i < document.styleSheets.length; i++) { - var styleSheet = document.styleSheets[i]; - var ii = 0; - var cssRule = false; - do { - if (styleSheet.cssRules) { - cssRule = styleSheet.cssRules[ii]; - } else { - cssRule = styleSheet.rules[ii]; - } - if (cssRule) { - if (cssRule.selectorText === ruleName) { - return cssRule; - } - } - ii++; - } while (cssRule); - } - } - return false; - } - // Set ports width var width = WIRE_WIDTH * state.zoom; this.$('.port-wire').css('stroke-width', width); @@ -2173,3 +2125,27 @@ joint.shapes.ice.WireView = joint.dia.LinkView.extend({ } }); + +function getCSSRule(ruleName) { + if (document.styleSheets) { + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + var ii = 0; + var cssRule = false; + do { + if (styleSheet.cssRules) { + cssRule = styleSheet.cssRules[ii]; + } else { + cssRule = styleSheet.rules[ii]; + } + if (cssRule) { + if (cssRule.selectorText === ruleName) { + return cssRule; + } + } + ii++; + } while (cssRule); + } + } + return false; +}