From 0d61b68909e6e055c59a422429a174a3175f5588 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Mon, 8 Feb 2021 14:04:02 +0100 Subject: [PATCH 01/38] Bump dependencies and version number. --- package-lock.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9506b95..f262e85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "yosys2digitaljs", - "version": "0.2.3", + "version": "0.2.4", "lockfileVersion": 1, "requires": true, "dependencies": { "3vl": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/3vl/-/3vl-0.3.4.tgz", - "integrity": "sha512-q4ctIYwgAUFV8sdFBuVx6yMhoQo/kS9sYoi3CacvWLEcynvseDMWMm0NjlSsnZDXiHQcP3kJNKYTjJGaLrrtMg==" + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/3vl/-/3vl-0.3.5.tgz", + "integrity": "sha512-xyBvuoClECIQHqKDEhcDnpaAVTDvcWV3BcX0farUKBjaoxAXta/4zyJr5l3utnuf90kbQPvXWCFAK71O4u5N1g==" }, "balanced-match": { "version": "1.0.0", diff --git a/package.json b/package.json index c93de52..8de9957 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.2.3", + "version": "0.2.4", "description": "Export Yosys netlists to a logic simulator", "main": "index.js", "scripts": { @@ -9,7 +9,7 @@ "author": "Marek Materzok", "license": "BSD-2-Clause", "dependencies": { - "3vl": "^0.3.4", + "3vl": "^0.3.5", "big-integer": "^1.6.48", "hashmap": "^2.4.0", "minimist": "^1.2.5", From 347e5cf37d5a03e08b094e427cac931e6f0f3c3c Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Tue, 20 Jul 2021 14:29:19 +0200 Subject: [PATCH 02/38] Remove graph transformations --- index.js | 25 ------------------------- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index ff3062c..9d9b180 100644 --- a/index.js +++ b/index.js @@ -723,31 +723,6 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { add_net_source(nbits, dname, 'out'); add_net_target(cconn, dname, 'in'); } - // Make some simplifications in the graph - // TODO multiple passes, less ad-hoc - for (const [nbits, net] of nets.entries()) { - if (net.source === undefined || net.targets.length != 1 || - net.source.id == net.targets[0].id) continue; - const srcdev = mout.devices[net.source.id]; - const tgtdev = mout.devices[net.targets[0].id]; - // eliminate repeaters, TODO nets with multiple targets - if (tgtdev.type == 'Repeater') { - delete mout.devices[net.targets[0].id]; - const t_nbits = devnets.get(net.targets[0].id).get('out'); - const t_net = nets.get(t_nbits); - t_net.source = net.source; - nets.delete(nbits); - } - // eliminate negations - if (tgtdev.type == 'Not' && gate_negations.has(srcdev.type)) { - srcdev.type = gate_negations.get(srcdev.type); - delete mout.devices[net.targets[0].id]; - const t_nbits = devnets.get(net.targets[0].id).get('out'); - const t_net = nets.get(t_nbits); - t_net.source = net.source; - nets.delete(nbits); - } - } // Generate connections between devices for (const [nbits, net] of nets.entries()) { if (net.source === undefined) { diff --git a/package-lock.json b/package-lock.json index f262e85..ce2ee7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.2.4", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8de9957..9dc56de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.2.4", + "version": "0.3.0", "description": "Export Yosys netlists to a logic simulator", "main": "index.js", "scripts": { From 00722fc88fe419876ee3a0774e50f6a06ca46060 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Tue, 17 Aug 2021 16:05:40 +0200 Subject: [PATCH 03/38] Fix examples --- tests/cycleadder.sv | 2 +- tests/cycleadder_arst.sv | 2 +- tests/dff_masterslave.sv | 2 +- tests/dlatch_gate.sv | 2 +- tests/fsm.sv | 6 +++--- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/cycleadder.sv b/tests/cycleadder.sv index 5d2b198..d51463d 100644 --- a/tests/cycleadder.sv +++ b/tests/cycleadder.sv @@ -5,7 +5,7 @@ module cycleadder input rst, input en, input [WIDTH-1:0] A, - output [WIDTH-1:0] O + output logic [WIDTH-1:0] O ); always_ff @(posedge clk) diff --git a/tests/cycleadder_arst.sv b/tests/cycleadder_arst.sv index bc83ae9..d00f1b7 100644 --- a/tests/cycleadder_arst.sv +++ b/tests/cycleadder_arst.sv @@ -5,7 +5,7 @@ module cycleadder input rst, input en, input [WIDTH-1:0] A, - output [WIDTH-1:0] O + output logic [WIDTH-1:0] O ); always_ff @(posedge clk or posedge rst) diff --git a/tests/dff_masterslave.sv b/tests/dff_masterslave.sv index 7ffa7e9..8c145d7 100644 --- a/tests/dff_masterslave.sv +++ b/tests/dff_masterslave.sv @@ -4,7 +4,7 @@ module d_latch( output q, nq ); - logic s, r; + logic s, r, nd; nor g1(q, r, nq); nor g2(nq, s, q); diff --git a/tests/dlatch_gate.sv b/tests/dlatch_gate.sv index 7355301..27781a2 100644 --- a/tests/dlatch_gate.sv +++ b/tests/dlatch_gate.sv @@ -4,7 +4,7 @@ module d_latch( output q, nq ); - logic s, r; + logic s, r, nd; nor g1(q, r, nq); nor g2(nq, s, q); diff --git a/tests/fsm.sv b/tests/fsm.sv index 6bbfb21..e570920 100644 --- a/tests/fsm.sv +++ b/tests/fsm.sv @@ -1,5 +1,5 @@ // Write your modules here! -module fsm(input clk, rst, a, output b); +module fsm(input clk, rst, a, output logic b); logic [1:0] state; @@ -10,7 +10,7 @@ module fsm(input clk, rst, a, output b); always_ff @(posedge clk or posedge rst) if (rst) state <= B; - else casex(state) + else unique case(state) A: state <= C; B: state <= D; C: if (a) state <= D; else state <= B; @@ -19,7 +19,7 @@ module fsm(input clk, rst, a, output b); always_comb begin b = 1'bx; - case(state) + unique case(state) A, D: b = 0; B: b = 1; C: if (a) b = 1; else b = 0; From 8e688f2ba467c732cca7929ba0dbfbbfd49203eb Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 19 Aug 2021 12:46:24 +0200 Subject: [PATCH 04/38] Towards porting yosys2digitaljs to TypeScript --- .gitignore | 3 + package-lock.json | 12 +++ package.json | 11 ++- process.js | 2 +- index.js => src/index.ts | 155 +++++++++++++++++++++++++++++---------- tsconfig.json | 15 ++++ 6 files changed, 157 insertions(+), 41 deletions(-) rename index.js => src/index.ts (90%) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 07e6e47..b652566 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /node_modules +/dist +*.swp +*.swo diff --git a/package-lock.json b/package-lock.json index ce2ee7c..efd1b40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,12 @@ "resolved": "https://registry.npmjs.org/3vl/-/3vl-0.3.5.tgz", "integrity": "sha512-xyBvuoClECIQHqKDEhcDnpaAVTDvcWV3BcX0farUKBjaoxAXta/4zyJr5l3utnuf90kbQPvXWCFAK71O4u5N1g==" }, + "@types/node": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.2.tgz", + "integrity": "sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA==", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -147,6 +153,12 @@ "utf8-byte-length": "^1.0.1" } }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, "utf8-byte-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", diff --git a/package.json b/package.json index 9dc56de..f7a3993 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,13 @@ "name": "yosys2digitaljs", "version": "0.3.0", "description": "Export Yosys netlists to a logic simulator", - "main": "index.js", + "main": "dist/index", + "types": "dist/index.d.ts", "scripts": { + "prepare": "npm run build; npm run doc", + "cjs": "tsc -m commonjs", + "build": "npm run cjs", + "doc": "typedoc --out dist/docs src/", "test": "./run_tests.sh" }, "author": "Marek Materzok", @@ -24,5 +29,9 @@ "repository": { "type": "git", "url": "https://github.com/tilk/yosys2digitaljs.git" + }, + "devDependencies": { + "@types/node": "^16.6.2", + "typescript": "^4.3.5" } } diff --git a/process.js b/process.js index d0ce4c6..f1cad2e 100755 --- a/process.js +++ b/process.js @@ -29,7 +29,7 @@ if (argv._.length === 0) { console.error('No Verilog files passed!'); process.exit(1); } -const yosys2digitaljs = require('./index.js'); +const yosys2digitaljs = require('./dist/index.js'); const opts = {}; if (argv.optimize) opts.optimize = true; if (argv.fsm) opts.fsm = argv.fsm; diff --git a/index.js b/src/index.ts similarity index 90% rename from index.js rename to src/index.ts index 9d9b180..531f355 100644 --- a/index.js +++ b/src/index.ts @@ -1,17 +1,18 @@ #!/usr/bin/env node "use strict"; -const tmp = require('tmp-promise'); -const child_process = require('child_process'); -const assert = require('assert'); -const topsort = require('topsort'); -const fs = require('fs'); -const sanitize = require("sanitize-filename"); -const path = require('path'); -const HashMap = require('hashmap'); -const bigInt = require('big-integer'); -const {promisify} = require('util'); -const {Vector3vl, Mem3vl} = require('3vl'); +import * as tmp from 'tmp-promise'; +import * as child_process from 'child_process'; +import * as assert from 'assert'; +import * as fs from 'fs'; +import sanitize from "sanitize-filename"; +import * as path from 'path'; +import * as HashMap from 'hashmap'; +import * as bigInt from 'big-integer'; +import {promisify} from 'util'; +import {Vector3vl, Mem3vl} from '3vl'; + +const topsort: (edges:T[][], options?:{continueOnCircularDependency: boolean}) => T[] = require('topsort'); const unary_gates = new Set([ '$not', '$neg', '$pos', '$reduce_and', '$reduce_or', '$reduce_xor', @@ -99,6 +100,87 @@ const gate_negations = new Map([ ['XorReduce', 'XnorReduce'], ['XnorReduce', 'XorReduce']]); +type DigitaljsMemReadPort = { + clock_polarity?: boolean, + enable_polarity?: boolean, + transparent?: boolean +}; + +type DigitaljsMemWritePort = { + clock_polarity?: boolean, + enable_polarity?: boolean +}; + +type DigitaljsDevice = { + type: string, + [key: string]: any +}; + +type DigitaljsPort = { + id: string, + port: string +}; + +type DigitaljsConnector = { + from: DigitaljsPort, + to: DigitaljsPort, + name?: string +}; + +type DigitaljsModule = { + devices: { [key: string]: DigitaljsDevice }, + connectors: DigitaljsConnector[] +}; + +type DigitaljsTopModule = DigitaljsModule & { + subcircuits: { [key: string]: DigitaljsModule } +} + +type YosysPort = { + direction: 'input' | 'output' | 'inout', + bits: any +}; + +type YosysCell = { + hide_name: 0 | 1, + type: string, + parameters: any, + attributes: { [key: string]: string }, + port_directions: any, + connections: any +}; + +type YosysNet = { + hide_name: 0 | 1, + bits: any, + attributes: { [key: string]: string } +}; + +type YosysModule = { + ports: { [key: string]: YosysPort }, + cells: { [key: string]: YosysCell }, + netnames: { [key: string]: YosysNet } +}; + +type YosysOutput = { + modules: { [key: string]: YosysModule } +}; + +type Yosys2DigitaljsOptions = { + propagation?: number, + optimize?: boolean, + fsmexpand?: boolean, + fsm?: boolean | "nomap", + timeout?: number +}; + +type Yosys2DigitaljsOutput = { + output?: DigitaljsTopModule, + yosys_output?: any, + yosys_stdout: string, + yosys_stderr: string +}; + function chunkArray(a, chunk_size){ let results = []; let ca = a.splice(); @@ -110,8 +192,8 @@ function chunkArray(a, chunk_size){ return results; } -function module_deps(data) { - const out = []; +function module_deps(data: YosysOutput) { + const out: [string, string | number][] = []; for (const [name, mod] of Object.entries(data.modules)) { out.push([name, 1/0]); for (const cname in mod.cells) { @@ -123,7 +205,7 @@ function module_deps(data) { return out; } -function order_ports(data) { +function order_ports(data: YosysOutput) { const unmap = {A: 'in', Y: 'out'}; const binmap = {A: 'in1', B: 'in2', Y: 'out'}; const out = { @@ -167,7 +249,7 @@ function decode_json_constant(param, bits) { return param; } -function yosys_to_digitaljs(data, portmaps, options = {}) { +function yosys_to_digitaljs(data: YosysOutput, portmaps, options = {}): {[key: string]: DigitaljsModule} { const out = {}; for (const [name, mod] of Object.entries(data.modules)) { out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options); @@ -175,7 +257,7 @@ function yosys_to_digitaljs(data, portmaps, options = {}) { return out } -function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { +function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, options: Yosys2DigitaljsOptions = {}): DigitaljsModule { function constbit(bit) { return bit == '0' || bit == '1' || bit == 'x'; } @@ -200,7 +282,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { } return nets.get(k); } - function add_net_source(k, d, p, primary) { + function add_net_source(k, d, p, primary: boolean = false) { if (k.length == 0) return; // for unconnected ports const net = get_net(k); if(net.source !== undefined) { @@ -225,7 +307,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { devices: {}, connectors: [] } - function add_device(dev) { + function add_device(dev : DigitaljsDevice): string { const dname = gen_name(); if (options.propagation !== undefined) dev.propagation = options.propagation; @@ -327,7 +409,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { } // Add gates for (const [cname, cell] of Object.entries(mod.cells)) { - const dev = { + const dev : DigitaljsDevice = { label: cname, type: gate_subst.get(cell.type) }; @@ -560,16 +642,17 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { dev.trans_table = []; for (let i = 0; i < cell.parameters.TRANS_NUM; i++) { let base = i * step; - const o = {}; const f = (sz) => { const ret = tt.slice(base, base + sz); base += sz; return ret; }; - o.state_in = parseInt(f(cell.parameters.STATE_NUM_LOG2), 2); - o.ctrl_in = f(cell.parameters.CTRL_IN_WIDTH).replace(/-/g, 'x'); - o.state_out = parseInt(f(cell.parameters.STATE_NUM_LOG2), 2); - o.ctrl_out = f(cell.parameters.CTRL_OUT_WIDTH); + const o = { + state_in: parseInt(f(cell.parameters.STATE_NUM_LOG2), 2), + ctrl_in: f(cell.parameters.CTRL_IN_WIDTH).replace(/-/g, 'x'), + state_out: parseInt(f(cell.parameters.STATE_NUM_LOG2), 2), + ctrl_out: f(cell.parameters.CTRL_OUT_WIDTH) + }; dev.trans_table.push(o); } break; @@ -608,7 +691,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { dev.memdata = memdata.toJSON(); } for (const k of Array(cell.parameters.RD_PORTS).keys()) { - const port = { + const port: DigitaljsMemReadPort = { }; if (rden[k]) { port.clock_polarity = Boolean(rdpol[k]); @@ -620,7 +703,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { dev.rdports.push(port); } for (const k of Array(cell.parameters.WR_PORTS).keys()) { - const port = { + const port: DigitaljsMemWritePort = { }; if (wren[k]) { port.clock_polarity = Boolean(wrpol[k]); @@ -731,7 +814,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { } let first = true; for (const target in net.targets) { - const conn = { + const conn: DigitaljsConnector = { to: net.targets[target], from: net.source }; @@ -751,7 +834,7 @@ function yosys_to_digitaljs_mod(name, mod, portmaps, options = {}) { return mout; } -async function process(filenames, dirname, options = {}) { +export async function process(filenames: string[], dirname?: string, options: Yosys2DigitaljsOptions = {}): Promise { const optimize_simp = options.optimize ? "; opt" : "; opt_clean"; const optimize = options.optimize ? "; opt -full" : "; opt_clean"; const fsmexpand = options.fsmexpand ? " -expand" : ""; @@ -760,7 +843,7 @@ async function process(filenames, dirname, options = {}) { : ""; const tmpjson = await tmp.tmpName({ postfix: '.json' }); let obj = undefined; - const yosys_result = await promisify(child_process.exec)( + const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; dff2dffe; wreduce -memx' + optimize + '" -o "' + tmpjson + '" ' + filenames.map(cmd => '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"').join(' '), @@ -783,8 +866,7 @@ async function process(filenames, dirname, options = {}) { const toporder = topsort(module_deps(obj)); toporder.pop(); const toplevel = toporder.pop(); - const output = out[toplevel]; - output.subcircuits = {}; + const output: DigitaljsTopModule = { subcircuits: {}, ... out[toplevel] }; for (const x of toporder) output.subcircuits[x] = out[x]; return { output: output, @@ -800,7 +882,7 @@ async function process(filenames, dirname, options = {}) { } } -function io_ui(output) { +export function io_ui(output: DigitaljsModule) { for (const [name, dev] of Object.entries(output.devices)) { if (dev.type == 'Input' || dev.type == 'Output') { dev.label = dev.net; @@ -817,7 +899,7 @@ function io_ui(output) { } } -async function process_files(data, options) { +export async function process_files(data: {[key: string]: string}, options: Yosys2DigitaljsOptions): Promise { const dir = await tmp.dir(); const names = []; try { @@ -835,7 +917,7 @@ async function process_files(data, options) { } } -async function process_sv(text) { +export async function process_sv(text: string): Promise { const tmpsv = await tmp.file({ postfix: '.sv' }); try { await promisify(fs.write)(tmpsv.fd, text); @@ -846,8 +928,3 @@ async function process_sv(text) { } } -exports.process = process; -exports.process_files = process_files; -exports.process_sv = process_sv; -exports.io_ui = io_ui; - diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..435b151 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "module": "es2015", + "target": "es2017", + "declaration": true, + "allowSyntheticDefaultImports": true, + "outDir": "./dist", + "lib": ["es2017"], + "types": ["node"] + }, + "include": [ + "src/**/*" + ] +} From 1162c1476a00ebe7bfa010eeb1d6326a7436de20 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 19 Aug 2021 18:32:23 +0200 Subject: [PATCH 05/38] More types added. --- src/index.ts | 256 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 160 insertions(+), 96 deletions(-) diff --git a/src/index.ts b/src/index.ts index 531f355..a029133 100644 --- a/src/index.ts +++ b/src/index.ts @@ -100,73 +100,119 @@ const gate_negations = new Map([ ['XorReduce', 'XnorReduce'], ['XnorReduce', 'XorReduce']]); -type DigitaljsMemReadPort = { - clock_polarity?: boolean, - enable_polarity?: boolean, - transparent?: boolean -}; - -type DigitaljsMemWritePort = { - clock_polarity?: boolean, - enable_polarity?: boolean -}; +namespace Digitaljs { -type DigitaljsDevice = { - type: string, - [key: string]: any -}; + export type MemReadPort = { + clock_polarity?: boolean, + enable_polarity?: boolean, + transparent?: boolean + }; + + export type MemWritePort = { + clock_polarity?: boolean, + enable_polarity?: boolean + }; + + export type Device = { + type: string, + [key: string]: any + }; + + export type Port = { + id: string, + port: string + }; + + export type Connector = { + from: Port, + to: Port, + name?: string + }; + + export type Module = { + devices: { [key: string]: Device }, + connectors: Connector[] + }; + + export type TopModule = Module & { + subcircuits: { [key: string]: Module } + }; -type DigitaljsPort = { - id: string, - port: string }; -type DigitaljsConnector = { - from: DigitaljsPort, - to: DigitaljsPort, - name?: string -}; +namespace Yosys { -type DigitaljsModule = { - devices: { [key: string]: DigitaljsDevice }, - connectors: DigitaljsConnector[] -}; + export type Bit = number | '0' | '1' | 'x'; -type DigitaljsTopModule = DigitaljsModule & { - subcircuits: { [key: string]: DigitaljsModule } -} + export type BitVector = Bit[]; -type YosysPort = { - direction: 'input' | 'output' | 'inout', - bits: any -}; + export type Port = { + direction: 'input' | 'output' | 'inout', + bits: any + }; -type YosysCell = { - hide_name: 0 | 1, - type: string, - parameters: any, - attributes: { [key: string]: string }, - port_directions: any, - connections: any -}; + export type Parameters = { + WIDTH?: number, + A_WIDTH?: number, + B_WIDTH?: number, + S_WIDTH?: number, + Y_WIDTH?: number, + A_SIGNED?: 0 | 1, + B_SIGNED?: 0 | 1, + CLK_POLARITY?: 0 | 1, + EN_POLARITY?: 0 | 1, + ARST_POLARITY?: 0 | 1, + ARST_VALUE: JsonConstant, + CTRL_IN_WIDTH?: number, + CTRL_OUT_WIDTH?: number, + TRANS_NUM?: number, + STATE_NUM_LOG2?: number, + STATE_RST?: number, + RD_PORTS?: number, + WR_PORTS?: number, + RD_CLK_POLARITY?: JsonConstant, + RD_CLK_ENABLE?: JsonConstant, + RD_CLK_TRANSPARENT?: JsonConstant, + WR_CLK_POLARITY?: JsonConstant, + WR_CLK_ENABLE?: JsonConstant, + [key: string]: any + }; -type YosysNet = { - hide_name: 0 | 1, - bits: any, - attributes: { [key: string]: string } -}; + export type JsonConstant = number | string; -type YosysModule = { - ports: { [key: string]: YosysPort }, - cells: { [key: string]: YosysCell }, - netnames: { [key: string]: YosysNet } -}; + export type Attributes = { + init: JsonConstant, + [key: string]: any + }; + + export type Cell = { + hide_name: 0 | 1, + type: string, + parameters: Parameters, + attributes: Attributes, + port_directions: { [key: string]: 'input' | 'output' }, + connections: { [key: string]: BitVector } + }; + + export type Net = { + hide_name: 0 | 1, + bits: BitVector, + attributes: { [key: string]: string } + }; + + export type Module = { + ports: { [key: string]: Port }, + cells: { [key: string]: Cell }, + netnames: { [key: string]: Net } + }; + + export type Output = { + modules: { [key: string]: Module } + }; -type YosysOutput = { - modules: { [key: string]: YosysModule } }; -type Yosys2DigitaljsOptions = { +type Options = { propagation?: number, optimize?: boolean, fsmexpand?: boolean, @@ -174,13 +220,32 @@ type Yosys2DigitaljsOptions = { timeout?: number }; -type Yosys2DigitaljsOutput = { - output?: DigitaljsTopModule, +type Output = { + output?: Digitaljs.TopModule, yosys_output?: any, yosys_stdout: string, yosys_stderr: string }; +type Portmap = { [key: string]: string }; +type Portmaps = { [key: string]: Portmap }; + +type Bit = Yosys.Bit | `bit${number}`; + +type Net = Bit[]; + +type NetInfo = { + source: undefined | Digitaljs.Port, + targets: Digitaljs.Port[], + name: undefined | string +}; + +type BitInfo = { + id: string, + port: string, + num: number +}; + function chunkArray(a, chunk_size){ let results = []; let ca = a.splice(); @@ -192,7 +257,7 @@ function chunkArray(a, chunk_size){ return results; } -function module_deps(data: YosysOutput) { +function module_deps(data: Yosys.Output): [string, string | number][] { const out: [string, string | number][] = []; for (const [name, mod] of Object.entries(data.modules)) { out.push([name, 1/0]); @@ -205,7 +270,7 @@ function module_deps(data: YosysOutput) { return out; } -function order_ports(data: YosysOutput) { +function order_ports(data: Yosys.Output): Portmaps { const unmap = {A: 'in', Y: 'out'}; const binmap = {A: 'in1', B: 'in2', Y: 'out'}; const out = { @@ -219,7 +284,7 @@ function order_ports(data: YosysOutput) { binary_gates.forEach((nm) => out[nm] = binmap); unary_gates.forEach((nm) => out[nm] = unmap); for (const [name, mod] of Object.entries(data.modules)) { - const portmap = {}; + const portmap: Portmap = {}; const ins = [], outs = []; for (const pname in mod.ports) { portmap[pname] = pname; @@ -229,7 +294,7 @@ function order_ports(data: YosysOutput) { return out; } -function decode_json_bigint(param) { +function decode_json_bigint(param: string | number): bigInt.BigInteger { if (typeof param == 'string') return bigInt(param, 2) else if (typeof param == 'number') @@ -237,11 +302,11 @@ function decode_json_bigint(param) { else assert(false); } -function decode_json_bigint_as_array(param) { +function decode_json_bigint_as_array(param: string | number): number[] { return decode_json_bigint(param).toArray(2).value; } -function decode_json_constant(param, bits) { +function decode_json_constant(param: Yosys.JsonConstant, bits: number): string { if (typeof param == 'number') return bigInt(param).toArray(2).value.map(String).reverse() .concat(Array(bits).fill('0')).slice(0, bits).reverse().join(''); @@ -249,7 +314,7 @@ function decode_json_constant(param, bits) { return param; } -function yosys_to_digitaljs(data: YosysOutput, portmaps, options = {}): {[key: string]: DigitaljsModule} { +function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: Options = {}): {[key: string]: Digitaljs.Module} { const out = {}; for (const [name, mod] of Object.entries(data.modules)) { out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options); @@ -257,24 +322,24 @@ function yosys_to_digitaljs(data: YosysOutput, portmaps, options = {}): {[key: s return out } -function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, options: Yosys2DigitaljsOptions = {}): DigitaljsModule { - function constbit(bit) { +function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: Options = {}): Digitaljs.Module { + function constbit(bit: Bit) { return bit == '0' || bit == '1' || bit == 'x'; } - const nets = new HashMap(); + const nets = new HashMap(); const netnames = new HashMap(); - const bits = new Map(); - const devnets = new Map(); + const bits = new Map(); + const devnets = new Map>(); let n = 0, pn = 0; - function gen_name() { - const nm = 'dev' + n++; + function gen_name(): string { + const nm = `dev${n++}`; devnets.set(nm, new Map()); return nm; } - function gen_bitname() { - return 'bit' + pn++; + function gen_bitname(): Bit { + return `bit${pn++}`; } - function get_net(k) { + function get_net(k: Net): NetInfo { // create net if does not exist yet if (!nets.has(k)) { const nms = netnames.get(k); @@ -282,7 +347,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } return nets.get(k); } - function add_net_source(k, d, p, primary: boolean = false) { + function add_net_source(k: Net, d: string, p: string, primary: boolean = false) { if (k.length == 0) return; // for unconnected ports const net = get_net(k); if(net.source !== undefined) { @@ -297,7 +362,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } devnets.get(d).set(p, k); } - function add_net_target(k, d, p) { + function add_net_target(k: Net, d: string, p: string) { if (k.length == 0) return; // for unconnected ports const net = get_net(k); net.targets.push({ id: d, port: p }); @@ -307,14 +372,14 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option devices: {}, connectors: [] } - function add_device(dev : DigitaljsDevice): string { + function add_device(dev : Digitaljs.Device): string { const dname = gen_name(); if (options.propagation !== undefined) dev.propagation = options.propagation; mout.devices[dname] = dev; return dname; } - function add_busgroup(nbits, groups) { + function add_busgroup(nbits: Net, groups: Net[]) { if (get_net(nbits).source !== undefined) return; // the bits were already grouped const dname = add_device({ @@ -326,7 +391,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option add_net_target(group, dname, 'in' + gn); } } - function connect_device(dname, cell, portmap) { + function connect_device(dname: string, cell: Yosys.Cell, portmap: Portmap) { for (const [pname, pdir] of Object.entries(cell.port_directions)) { const pconn = cell.connections[pname]; switch (pdir) { @@ -341,7 +406,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } } } - function connect_pmux(dname, cell) { + function connect_pmux(dname: string, cell: Yosys.Cell) { add_net_target(cell.connections.A, dname, 'in0'); add_net_target(cell.connections.S.slice().reverse(), dname, 'sel'); add_net_source(cell.connections.Y, dname, 'out', true); @@ -351,7 +416,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option dname, 'in' + (i+1)); } } - function connect_mem(dname, cell, dev) { + function connect_mem(dname: string, cell: Yosys.Cell, dev: Digitaljs.Device) { for (const [k, port] of dev.rdports.entries()) { const portname = "rd" + k; add_net_target(cell.connections.RD_ADDR.slice(dev.abits * k, dev.abits * (k+1)), @@ -409,7 +474,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } // Add gates for (const [cname, cell] of Object.entries(mod.cells)) { - const dev : DigitaljsDevice = { + const dev : Digitaljs.Device = { label: cname, type: gate_subst.get(cell.type) }; @@ -418,7 +483,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option dev.celltype = cell.type; } const dname = add_device(dev); - function match_port(con, sig, sz) { + function match_port(con: Net, sig: 0 | 1, sz: number) { if (con.length > sz) con.splice(sz); else if (con.length < sz) { @@ -438,7 +503,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } } } - function zero_extend_output(con) { + function zero_extend_output(con: Net) { if (con.length > 1) { const ccon = con.slice(); con.splice(1); @@ -540,7 +605,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option dev.fillx = cell.type == '$shiftx'; break; case '$logic_and': case '$logic_or': { - function reduce_input(con) { + function reduce_input(con: Net) { const ccon = con.slice(); con.splice(0, con.length, gen_bitname()); const extname = add_device({ @@ -691,7 +756,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option dev.memdata = memdata.toJSON(); } for (const k of Array(cell.parameters.RD_PORTS).keys()) { - const port: DigitaljsMemReadPort = { + const port: Digitaljs.MemReadPort = { }; if (rden[k]) { port.clock_polarity = Boolean(rdpol[k]); @@ -703,7 +768,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option dev.rdports.push(port); } for (const k of Array(cell.parameters.WR_PORTS).keys()) { - const port: DigitaljsMemWritePort = { + const port: Digitaljs.MemWritePort = { }; if (wren[k]) { port.clock_polarity = Boolean(wrpol[k]); @@ -737,11 +802,10 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option // Group bits into nets for complex sources for (const [nbits, net] of nets.entries()) { if (net.source !== undefined) continue; - const groups = [[]]; - let group = []; - let pbitinfo = undefined; + const groups: Net[] = [[]]; + let pbitinfo: BitInfo | 'const' | undefined = undefined; for (const bit of nbits) { - let bitinfo = bits.get(bit); + let bitinfo: BitInfo | 'const' = bits.get(bit); if (bitinfo == undefined && constbit(bit)) bitinfo = 'const'; if (groups.slice(-1)[0].length > 0 && @@ -814,7 +878,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option } let first = true; for (const target in net.targets) { - const conn: DigitaljsConnector = { + const conn: Digitaljs.Connector = { to: net.targets[target], from: net.source }; @@ -834,7 +898,7 @@ function yosys_to_digitaljs_mod(name: string, mod: YosysModule, portmaps, option return mout; } -export async function process(filenames: string[], dirname?: string, options: Yosys2DigitaljsOptions = {}): Promise { +export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise { const optimize_simp = options.optimize ? "; opt" : "; opt_clean"; const optimize = options.optimize ? "; opt -full" : "; opt_clean"; const fsmexpand = options.fsmexpand ? " -expand" : ""; @@ -866,7 +930,7 @@ export async function process(filenames: string[], dirname?: string, options: Yo const toporder = topsort(module_deps(obj)); toporder.pop(); const toplevel = toporder.pop(); - const output: DigitaljsTopModule = { subcircuits: {}, ... out[toplevel] }; + const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] }; for (const x of toporder) output.subcircuits[x] = out[x]; return { output: output, @@ -882,7 +946,7 @@ export async function process(filenames: string[], dirname?: string, options: Yo } } -export function io_ui(output: DigitaljsModule) { +export function io_ui(output: Digitaljs.Module) { for (const [name, dev] of Object.entries(output.devices)) { if (dev.type == 'Input' || dev.type == 'Output') { dev.label = dev.net; @@ -899,7 +963,7 @@ export function io_ui(output: DigitaljsModule) { } } -export async function process_files(data: {[key: string]: string}, options: Yosys2DigitaljsOptions): Promise { +export async function process_files(data: {[key: string]: string}, options: Options): Promise { const dir = await tmp.dir(); const names = []; try { @@ -917,7 +981,7 @@ export async function process_files(data: {[key: string]: string}, options: Yosy } } -export async function process_sv(text: string): Promise { +export async function process_sv(text: string): Promise { const tmpsv = await tmp.file({ postfix: '.sv' }); try { await promisify(fs.write)(tmpsv.fd, text); From c4ef64272a2b36128e6ee7fe855f74336cffabf8 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 20 Aug 2021 12:35:16 +0200 Subject: [PATCH 06/38] Add linting via Verilator --- process.js | 8 ++++++++ src/index.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/process.js b/process.js index f1cad2e..f761799 100755 --- a/process.js +++ b/process.js @@ -33,6 +33,7 @@ const yosys2digitaljs = require('./dist/index.js'); const opts = {}; if (argv.optimize) opts.optimize = true; if (argv.fsm) opts.fsm = argv.fsm; +if (argv.lint) opts.lint = true; if (argv.propagation !== undefined) opts.propagation = Number(argv.propagation); const result = argv.tmpdir ? yosys2digitaljs.process_files(read_files(argv._), opts) : yosys2digitaljs.process(argv._, null, opts); result.then(res => { @@ -50,6 +51,13 @@ result.then(res => { console.log(util.inspect(res.yosys_output, {showHidden: false, depth: null, colors: process.stdout.isTTY && process.stdout.hasColors()})); console.log('*/'); } + if (opts.lint && res.lint && res.lint.length) { + console.log('/*'); + for (const lint of res.lint) { + console.log(`${lint.type} ${lint.file}:${lint.line}:${lint.column} ${lint.message}`); + } + console.log('*/'); + } const output = res.output; if (!argv.no_io_ui) yosys2digitaljs.io_ui(output); console.log(JSON.stringify(output, null, argv.noindent ? 0 : 2)); diff --git a/src/index.ts b/src/index.ts index a029133..89ae766 100644 --- a/src/index.ts +++ b/src/index.ts @@ -217,14 +217,16 @@ type Options = { optimize?: boolean, fsmexpand?: boolean, fsm?: boolean | "nomap", - timeout?: number + timeout?: number, + lint?: boolean }; type Output = { output?: Digitaljs.TopModule, yosys_output?: any, yosys_stdout: string, - yosys_stderr: string + yosys_stderr: string, + lint?: LintMessage[] }; type Portmap = { [key: string]: string }; @@ -246,6 +248,14 @@ type BitInfo = { num: number }; +type LintMessage = { + type: string, + file: string, + line: number, + column: number, + message: string +}; + function chunkArray(a, chunk_size){ let results = []; let ca = a.splice(); @@ -898,6 +908,36 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm return mout; } +function escape_filename(cmd: string): string { + return '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"'; +} + +const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/; + +export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise { + try { + const output: LintMessage[] = []; + const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)( + 'verilator -lint-only -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(escape_filename).join(' '), + {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) + .catch(exc => exc); + for (const line of verilator_result.stderr.split('\n')) { + const result = line.match(verilator_re); + if (result == null) continue; + output.push({ + type: result[1], + file: path.basename(result[2]), + line: Number(result[3]), + column: Number(result[4]), + message: result[5] + }); + } + return output; + } catch (exc) { + return null; + } +} + export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise { const optimize_simp = options.optimize ? "; opt" : "; opt_clean"; const optimize = options.optimize ? "; opt -full" : "; opt_clean"; @@ -909,8 +949,7 @@ export async function process(filenames: string[], dirname?: string, options: Op let obj = undefined; const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; dff2dffe; wreduce -memx' + - optimize + '" -o "' + tmpjson + '" ' + - filenames.map(cmd => '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"').join(' '), + optimize + '" -o "' + tmpjson + '" ' + filenames.map(escape_filename).join(' '), {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); try { @@ -932,12 +971,15 @@ export async function process(filenames: string[], dirname?: string, options: Op const toplevel = toporder.pop(); const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] }; for (const x of toporder) output.subcircuits[x] = out[x]; - return { + const ret: Output = { output: output, yosys_output: obj, yosys_stdout: yosys_result.stdout, yosys_stderr: yosys_result.stderr }; + if (options.lint) + ret.lint = await verilator_lint(filenames, dirname, options); + return ret; } catch (exc) { if (obj !== undefined) exc.yosys_output = obj; exc.yosys_stdout = yosys_result.stdout; From 7a838a1446e1e8d8894d9d45eae32d9d9b541ae7 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 20 Aug 2021 15:54:02 +0200 Subject: [PATCH 07/38] Minor fixes --- package.json | 3 +-- src/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f7a3993..a9aa632 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,9 @@ "main": "dist/index", "types": "dist/index.d.ts", "scripts": { - "prepare": "npm run build; npm run doc", + "prepare": "npm run build", "cjs": "tsc -m commonjs", "build": "npm run cjs", - "doc": "typedoc --out dist/docs src/", "test": "./run_tests.sh" }, "author": "Marek Materzok", diff --git a/src/index.ts b/src/index.ts index 89ae766..e0957fb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,6 @@ import * as tmp from 'tmp-promise'; import * as child_process from 'child_process'; import * as assert from 'assert'; import * as fs from 'fs'; -import sanitize from "sanitize-filename"; import * as path from 'path'; import * as HashMap from 'hashmap'; import * as bigInt from 'big-integer'; @@ -13,6 +12,7 @@ import {promisify} from 'util'; import {Vector3vl, Mem3vl} from '3vl'; const topsort: (edges:T[][], options?:{continueOnCircularDependency: boolean}) => T[] = require('topsort'); +const sanitize = require("sanitize-filename"); const unary_gates = new Set([ '$not', '$neg', '$pos', '$reduce_and', '$reduce_or', '$reduce_xor', @@ -918,7 +918,7 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti try { const output: LintMessage[] = []; const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)( - 'verilator -lint-only -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(escape_filename).join(' '), + 'verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(escape_filename).join(' '), {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); for (const line of verilator_result.stderr.split('\n')) { @@ -1017,7 +1017,7 @@ export async function process_files(data: {[key: string]: string}, options: Opti return await process(names, dir.path, options); } finally { for (const name of Object.keys(data)) { - await promisify(fs.unlink)(path.resolve(dir.path, name)); + await promisify(fs.unlink)(path.resolve(dir.path, name)).catch(exc => exc); } dir.cleanup(); } From 970ad065851039af3a3c38cc9fb0ad0a314a0bb4 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 20 Aug 2021 15:56:21 +0200 Subject: [PATCH 08/38] Bump version number --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index efd1b40..1c39e8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a9aa632..ee564a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.3.0", + "version": "0.4.0", "description": "Export Yosys netlists to a logic simulator", "main": "dist/index", "types": "dist/index.d.ts", From 6bc949e26e38467ebdedfd7ab970a012b3b026a8 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 1 Oct 2021 13:12:49 +0200 Subject: [PATCH 09/38] Towards Yosys 0.10 support. --- process.js | 5 +- src/index.ts | 156 +++++++++++++++++++++++++++------------------------ tests/fsm.sv | 1 + 3 files changed, 89 insertions(+), 73 deletions(-) diff --git a/process.js b/process.js index f761799..10ff2ad 100755 --- a/process.js +++ b/process.js @@ -4,7 +4,9 @@ const fs = require('fs'); const argv = require('minimist')( process.argv.slice(2), - {boolean: ["optimize", "fsm", "yosys_out", "yosys_output", "html", "no_io_ui", "tmpdir", "noindent"]} + {boolean: ["optimize", "yosys_out", "yosys_output", "html", "no_io_ui", "tmpdir", "noindent", "fsmexpand"], + string: ["fsm"], + default: {fsm: true}} ); const util = require('util'); @@ -33,6 +35,7 @@ const yosys2digitaljs = require('./dist/index.js'); const opts = {}; if (argv.optimize) opts.optimize = true; if (argv.fsm) opts.fsm = argv.fsm; +if (argv.fsmexpand) opts.fsmexpand = true; if (argv.lint) opts.lint = true; if (argv.propagation !== undefined) opts.propagation = Number(argv.propagation); const result = argv.tmpdir ? yosys2digitaljs.process_files(read_files(argv._), opts) : yosys2digitaljs.process(argv._, null, opts); diff --git a/src/index.ts b/src/index.ts index e0957fb..efe52bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -152,22 +152,23 @@ namespace Yosys { }; export type Parameters = { - WIDTH?: number, - A_WIDTH?: number, - B_WIDTH?: number, - S_WIDTH?: number, - Y_WIDTH?: number, - A_SIGNED?: 0 | 1, - B_SIGNED?: 0 | 1, - CLK_POLARITY?: 0 | 1, - EN_POLARITY?: 0 | 1, - ARST_POLARITY?: 0 | 1, + WIDTH?: JsonConstant, + A_WIDTH?: JsonConstant, + B_WIDTH?: JsonConstant, + S_WIDTH?: JsonConstant, + Y_WIDTH?: JsonConstant, + A_SIGNED?: JsonConstant, + B_SIGNED?: JsonConstant, + CLK_POLARITY?: JsonConstant, + EN_POLARITY?: JsonConstant, + ARST_POLARITY?: JsonConstant, ARST_VALUE: JsonConstant, - CTRL_IN_WIDTH?: number, - CTRL_OUT_WIDTH?: number, - TRANS_NUM?: number, - STATE_NUM_LOG2?: number, - STATE_RST?: number, + CTRL_IN_WIDTH?: JsonConstant, + CTRL_OUT_WIDTH?: JsonConstant, + TRANS_NUM?: JsonConstant, + STATE_NUM?: JsonConstant, + STATE_NUM_LOG2?: JsonConstant, + STATE_RST?: JsonConstant, RD_PORTS?: number, WR_PORTS?: number, RD_CLK_POLARITY?: JsonConstant, @@ -312,6 +313,14 @@ function decode_json_bigint(param: string | number): bigInt.BigInteger { else assert(false); } +function decode_json_number(param: Yosys.JsonConstant): number { + if (typeof param == 'string') + return Number.parseInt(param, 2); + else if (typeof param == 'number') + return param + else assert(false); +} + function decode_json_bigint_as_array(param: string | number): number[] { return decode_json_bigint(param).toArray(2).value; } @@ -420,9 +429,9 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm add_net_target(cell.connections.A, dname, 'in0'); add_net_target(cell.connections.S.slice().reverse(), dname, 'sel'); add_net_source(cell.connections.Y, dname, 'out', true); - for (const i of Array(cell.parameters.S_WIDTH).keys()) { - const p = (cell.parameters.S_WIDTH-i-1) * cell.parameters.WIDTH; - add_net_target(cell.connections.B.slice(p, p + cell.parameters.WIDTH), + for (const i of Array(decode_json_number(cell.parameters.S_WIDTH)).keys()) { + const p = (decode_json_number(cell.parameters.S_WIDTH)-i-1) * decode_json_number(cell.parameters.WIDTH); + add_net_target(cell.connections.B.slice(p, p + decode_json_number(cell.parameters.WIDTH)), dname, 'in' + (i+1)); } } @@ -493,7 +502,8 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.celltype = cell.type; } const dname = add_device(dev); - function match_port(con: Net, sig: 0 | 1, sz: number) { + function match_port(con: Net, nsig: Yosys.JsonConstant, sz: number) { + const sig = decode_json_number(nsig); if (con.length > sz) con.splice(sz); else if (con.length < sz) { @@ -526,22 +536,22 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm } } if (unary_gates.has(cell.type)) { - assert(cell.connections.A.length == cell.parameters.A_WIDTH); - assert(cell.connections.Y.length == cell.parameters.Y_WIDTH); + assert(cell.connections.A.length == decode_json_number(cell.parameters.A_WIDTH)); + assert(cell.connections.Y.length == decode_json_number(cell.parameters.Y_WIDTH)); assert(cell.port_directions.A == 'input'); assert(cell.port_directions.Y == 'output'); } if (binary_gates.has(cell.type)) { - assert(cell.connections.A.length == cell.parameters.A_WIDTH); - assert(cell.connections.B.length == cell.parameters.B_WIDTH); - assert(cell.connections.Y.length == cell.parameters.Y_WIDTH); + assert(cell.connections.A.length == decode_json_number(cell.parameters.A_WIDTH)); + assert(cell.connections.B.length == decode_json_number(cell.parameters.B_WIDTH)); + assert(cell.connections.Y.length == decode_json_number(cell.parameters.Y_WIDTH)); assert(cell.port_directions.A == 'input'); assert(cell.port_directions.B == 'input'); assert(cell.port_directions.Y == 'output'); } if (['$dff', '$dffe', '$adff', '$dlatch'].includes(cell.type)) { - assert(cell.connections.D.length == cell.parameters.WIDTH); - assert(cell.connections.Q.length == cell.parameters.WIDTH); + assert(cell.connections.D.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.D == 'input'); assert(cell.port_directions.Q == 'output'); if (cell.type != '$dlatch') { @@ -555,7 +565,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm in: cell.connections.A.length, out: cell.connections.Y.length }; - dev.signed = Boolean(cell.parameters.A_SIGNED); + dev.signed = Boolean(decode_json_number(cell.parameters.A_SIGNED)); break; case '$not': match_port(cell.connections.A, cell.parameters.A_SIGNED, cell.connections.Y.length); @@ -568,8 +578,8 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm out: cell.connections.Y.length }; dev.signed = { - in1: Boolean(cell.parameters.A_SIGNED), - in2: Boolean(cell.parameters.B_SIGNED) + in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)), + in2: Boolean(decode_json_number(cell.parameters.B_SIGNED)) } break; case '$and': case '$or': case '$xor': case '$xnor': @@ -633,89 +643,91 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm break; } case '$mux': - assert(cell.connections.A.length == cell.parameters.WIDTH); - assert(cell.connections.B.length == cell.parameters.WIDTH); - assert(cell.connections.Y.length == cell.parameters.WIDTH); + assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.B.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.Y.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.A == 'input'); assert(cell.port_directions.B == 'input'); assert(cell.port_directions.Y == 'output'); dev.bits = { - in: cell.parameters.WIDTH, + in: decode_json_number(cell.parameters.WIDTH), sel: 1 }; break; case '$pmux': - assert(cell.connections.B.length == cell.parameters.WIDTH * cell.parameters.S_WIDTH); - assert(cell.connections.A.length == cell.parameters.WIDTH); - assert(cell.connections.S.length == cell.parameters.S_WIDTH); - assert(cell.connections.Y.length == cell.parameters.WIDTH); + assert(cell.connections.B.length == decode_json_number(cell.parameters.WIDTH) * decode_json_number(cell.parameters.S_WIDTH)); + assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.S.length == decode_json_number(cell.parameters.S_WIDTH)); + assert(cell.connections.Y.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.A == 'input'); assert(cell.port_directions.B == 'input'); assert(cell.port_directions.S == 'input'); assert(cell.port_directions.Y == 'output'); dev.bits = { - in: cell.parameters.WIDTH, - sel: cell.parameters.S_WIDTH + in: decode_json_number(cell.parameters.WIDTH), + sel: decode_json_number(cell.parameters.S_WIDTH) }; break; case '$dff': - dev.bits = cell.parameters.WIDTH; + dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { - clock: Boolean(cell.parameters.CLK_POLARITY) + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)) }; break; case '$dffe': assert(cell.connections.EN.length == 1); assert(cell.port_directions.EN == 'input'); - dev.bits = cell.parameters.WIDTH; + dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { - clock: Boolean(cell.parameters.CLK_POLARITY), - enable: Boolean(cell.parameters.EN_POLARITY) + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) }; break; case '$adff': assert(cell.connections.ARST.length == 1); assert(cell.port_directions.ARST == 'input'); - dev.bits = cell.parameters.WIDTH; + dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { - clock: Boolean(cell.parameters.CLK_POLARITY), - arst: Boolean(cell.parameters.ARST_POLARITY) + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY)) }; dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); break; case '$dlatch': assert(cell.connections.EN.length == 1); assert(cell.port_directions.EN == 'input'); - dev.bits = cell.parameters.WIDTH; + dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { - enable: Boolean(cell.parameters.EN_POLARITY) + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) }; break; case '$fsm': { assert(cell.connections.ARST.length == 1); assert(cell.connections.CLK.length == 1); - assert(cell.connections.CTRL_IN.length == cell.parameters.CTRL_IN_WIDTH); - assert(cell.connections.CTRL_OUT.length == cell.parameters.CTRL_OUT_WIDTH); - const step = 2*cell.parameters.STATE_NUM_LOG2 - + cell.parameters.CTRL_IN_WIDTH - + cell.parameters.CTRL_OUT_WIDTH; + assert(cell.connections.CTRL_IN.length == decode_json_number(cell.parameters.CTRL_IN_WIDTH)); + assert(cell.connections.CTRL_OUT.length == decode_json_number(cell.parameters.CTRL_OUT_WIDTH)); + const TRANS_NUM = decode_json_number(cell.parameters.TRANS_NUM); + const STATE_NUM_LOG2 = decode_json_number(cell.parameters.STATE_NUM_LOG2); + const step = 2*STATE_NUM_LOG2 + + decode_json_number(cell.parameters.CTRL_IN_WIDTH) + + decode_json_number(cell.parameters.CTRL_OUT_WIDTH); const tt = typeof(cell.parameters.TRANS_TABLE) == "number" - ? Vector3vl.fromBin(bigInt(cell.parameters.TRANS_TABLE).toString(2), cell.parameters.TRANS_NUM * step).toBin() // workaround for yosys silliness + ? Vector3vl.fromBin(bigInt(cell.parameters.TRANS_TABLE).toString(2), TRANS_NUM * step).toBin() // workaround for yosys silliness : cell.parameters.TRANS_TABLE; - assert(tt.length == cell.parameters.TRANS_NUM * step); + assert(tt.length == TRANS_NUM * step); dev.polarity = { - clock: Boolean(cell.parameters.CLK_POLARITY), - arst: Boolean(cell.parameters.ARST_POLARITY) + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY)) }; dev.wirename = cell.parameters.NAME; dev.bits = { - in: cell.parameters.CTRL_IN_WIDTH, - out: cell.parameters.CTRL_OUT_WIDTH + in: decode_json_number(cell.parameters.CTRL_IN_WIDTH), + out: decode_json_number(cell.parameters.CTRL_OUT_WIDTH) }; - dev.states = cell.parameters.STATE_NUM; - dev.init_state = cell.parameters.STATE_RST; + dev.states = decode_json_number(cell.parameters.STATE_NUM); + dev.init_state = decode_json_number(cell.parameters.STATE_RST); dev.trans_table = []; - for (let i = 0; i < cell.parameters.TRANS_NUM; i++) { + for (let i = 0; i < TRANS_NUM; i++) { let base = i * step; const f = (sz) => { const ret = tt.slice(base, base + sz); @@ -723,10 +735,10 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm return ret; }; const o = { - state_in: parseInt(f(cell.parameters.STATE_NUM_LOG2), 2), - ctrl_in: f(cell.parameters.CTRL_IN_WIDTH).replace(/-/g, 'x'), - state_out: parseInt(f(cell.parameters.STATE_NUM_LOG2), 2), - ctrl_out: f(cell.parameters.CTRL_OUT_WIDTH) + state_in: parseInt(f(STATE_NUM_LOG2), 2), + ctrl_in: f(decode_json_number(cell.parameters.CTRL_IN_WIDTH)).replace(/-/g, 'x'), + state_out: parseInt(f(STATE_NUM_LOG2), 2), + ctrl_out: f(decode_json_number(cell.parameters.CTRL_OUT_WIDTH)) }; dev.trans_table.push(o); } @@ -735,13 +747,13 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm case '$mem': { assert(cell.connections.RD_EN.length == cell.parameters.RD_PORTS); assert(cell.connections.RD_CLK.length == cell.parameters.RD_PORTS); - assert(cell.connections.RD_DATA.length == cell.parameters.RD_PORTS * cell.parameters.WIDTH); + assert(cell.connections.RD_DATA.length == cell.parameters.RD_PORTS * decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.RD_ADDR.length == cell.parameters.RD_PORTS * cell.parameters.ABITS); - assert(cell.connections.WR_EN.length == cell.parameters.WR_PORTS * cell.parameters.WIDTH); + assert(cell.connections.WR_EN.length == cell.parameters.WR_PORTS * decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.WR_CLK.length == cell.parameters.WR_PORTS); - assert(cell.connections.WR_DATA.length == cell.parameters.WR_PORTS * cell.parameters.WIDTH); + assert(cell.connections.WR_DATA.length == cell.parameters.WR_PORTS * decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.WR_ADDR.length == cell.parameters.WR_PORTS * cell.parameters.ABITS); - dev.bits = cell.parameters.WIDTH; + dev.bits = decode_json_number(cell.parameters.WIDTH); dev.abits = cell.parameters.ABITS; dev.words = cell.parameters.SIZE; dev.offset = cell.parameters.OFFSET; @@ -948,7 +960,7 @@ export async function process(filenames: string[], dirname?: string, options: Op const tmpjson = await tmp.tmpName({ postfix: '.json' }); let obj = undefined; const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( - 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; dff2dffe; wreduce -memx' + + 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' + optimize + '" -o "' + tmpjson + '" ' + filenames.map(escape_filename).join(' '), {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); diff --git a/tests/fsm.sv b/tests/fsm.sv index e570920..912ed49 100644 --- a/tests/fsm.sv +++ b/tests/fsm.sv @@ -1,6 +1,7 @@ // Write your modules here! module fsm(input clk, rst, a, output logic b); + (* fsm_encoding = "auto" *) logic [1:0] state; localparam A = 2'b00; From 8f89a340b1eec9316fc3ff0684351588e2d4ee06 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sat, 2 Oct 2021 14:05:47 +0200 Subject: [PATCH 10/38] Added support for new DFF cells in Yosys 0.10 --- ChangeLog.md | 13 +++++++++ src/index.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 ChangeLog.md diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 0000000..102e412 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,13 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [Unreleased] + +### Added + +- Support for Yosys 0.10 cells `$adffe`, `$sdff`, `$sdffe`, `$sdffce`, `$adlatch` + +### Changed + +- Support for JSON output generated by Yosys 0.10 + diff --git a/src/index.ts b/src/index.ts index efe52bc..e952b22 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,7 +85,12 @@ const gate_subst = new Map([ ['$logic_or', 'Or'], ['$dffe', 'Dff'], ['$adff', 'Dff'], - ['$dlatch', 'Dff']]); + ['$adffe', 'Dff'], + ['$sdff', 'Dff'], + ['$sdffe', 'Dff'], + ['$sdffce', 'Dff'], + ['$dlatch', 'Dff'], + ['$adlatch', 'Dff']]); const gate_negations = new Map([ ['And', 'Nand'], ['Nand', 'And'], @@ -289,7 +294,12 @@ function order_ports(data: Yosys.Output): Portmaps { '$dff': {CLK: 'clk', D: 'in', Q: 'out'}, '$dffe': {CLK: 'clk', EN: 'en', D: 'in', Q: 'out'}, '$adff': {CLK: 'clk', ARST: 'arst', D: 'in', Q: 'out'}, + '$adffe': {CLK: 'clk', EN: 'en', ARST: 'arst', D: 'in', Q: 'out'}, + '$sdff': {CLK: 'clk', SRST: 'srst', D: 'in', Q: 'out'}, + '$sdffe': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'}, + '$sdffce': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'}, '$dlatch': {EN: 'en', D: 'in', Q: 'out'}, + '$adlatch': {EN: 'en', ARST: 'arst', D: 'in', Q: 'out'}, '$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'} }; binary_gates.forEach((nm) => out[nm] = binmap); @@ -549,12 +559,12 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.port_directions.B == 'input'); assert(cell.port_directions.Y == 'output'); } - if (['$dff', '$dffe', '$adff', '$dlatch'].includes(cell.type)) { + if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch'].includes(cell.type)) { assert(cell.connections.D.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.D == 'input'); assert(cell.port_directions.Q == 'output'); - if (cell.type != '$dlatch') { + if (cell.type != '$dlatch' && cell.type != '$adlatch') { assert(cell.connections.CLK.length == 1); assert(cell.port_directions.CLK == 'input'); } @@ -693,6 +703,56 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); break; + case '$sdff': + assert(cell.connections.SRST.length == 1); + assert(cell.port_directions.SRST == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY)) + }; + dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); + break; + case '$adffe': + assert(cell.connections.ARST.length == 1); + assert(cell.port_directions.ARST == 'input'); + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) + }; + dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); + break; + case '$sdffe': + assert(cell.connections.SRST.length == 1); + assert(cell.port_directions.SRST == 'input'); + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) + }; + dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); + break; + case '$sdffce': + assert(cell.connections.SRST.length == 1); + assert(cell.port_directions.SRST == 'input'); + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + srst: Boolean(decode_json_number(cell.parameters.SRST_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) + }; + dev.enable_srst = true; + dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); + break; case '$dlatch': assert(cell.connections.EN.length == 1); assert(cell.port_directions.EN == 'input'); @@ -701,6 +761,18 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) }; break; + case '$adlatch': + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + assert(cell.connections.ARST.length == 1); + assert(cell.port_directions.ARST == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)), + arst: Boolean(decode_json_number(cell.parameters.ARST_POLARITY)) + }; + dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); + break; case '$fsm': { assert(cell.connections.ARST.length == 1); assert(cell.connections.CLK.length == 1); From c5dffb5da7e59a695d48a2ebf39eb214b5860b06 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sun, 3 Oct 2021 21:58:35 +0200 Subject: [PATCH 11/38] Towards adding $mem_v2 support --- src/index.ts | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/index.ts b/src/index.ts index e952b22..5b4a242 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,7 @@ const gate_subst = new Map([ ['$pmux', 'Mux1Hot'], ['$dff', 'Dff'], ['$mem', 'Memory'], + ['$mem_v2', 'Memory'], ['$fsm', 'FSM'], ['$clock', 'Clock'], ['$button', 'Button'], @@ -174,8 +175,8 @@ namespace Yosys { STATE_NUM?: JsonConstant, STATE_NUM_LOG2?: JsonConstant, STATE_RST?: JsonConstant, - RD_PORTS?: number, - WR_PORTS?: number, + RD_PORTS?: JsonConstant, + WR_PORTS?: JsonConstant, RD_CLK_POLARITY?: JsonConstant, RD_CLK_ENABLE?: JsonConstant, RD_CLK_TRANSPARENT?: JsonConstant, @@ -816,24 +817,29 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm } break; } - case '$mem': { - assert(cell.connections.RD_EN.length == cell.parameters.RD_PORTS); - assert(cell.connections.RD_CLK.length == cell.parameters.RD_PORTS); - assert(cell.connections.RD_DATA.length == cell.parameters.RD_PORTS * decode_json_number(cell.parameters.WIDTH)); - assert(cell.connections.RD_ADDR.length == cell.parameters.RD_PORTS * cell.parameters.ABITS); - assert(cell.connections.WR_EN.length == cell.parameters.WR_PORTS * decode_json_number(cell.parameters.WIDTH)); - assert(cell.connections.WR_CLK.length == cell.parameters.WR_PORTS); - assert(cell.connections.WR_DATA.length == cell.parameters.WR_PORTS * decode_json_number(cell.parameters.WIDTH)); - assert(cell.connections.WR_ADDR.length == cell.parameters.WR_PORTS * cell.parameters.ABITS); + case '$mem': + case '$mem_v2': { + const RD_PORTS = decode_json_number(cell.parameters.RD_PORTS); + const WR_PORTS = decode_json_number(cell.parameters.WR_PORTS); + assert(cell.connections.RD_EN.length == RD_PORTS); + assert(cell.connections.RD_CLK.length == RD_PORTS); + assert(cell.connections.RD_DATA.length == RD_PORTS * decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.RD_ADDR.length == RD_PORTS * decode_json_number(cell.parameters.ABITS)); + assert(cell.connections.WR_EN.length == WR_PORTS * decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.WR_CLK.length == WR_PORTS); + assert(cell.connections.WR_DATA.length == WR_PORTS * decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.WR_ADDR.length == WR_PORTS * decode_json_number(cell.parameters.ABITS)); dev.bits = decode_json_number(cell.parameters.WIDTH); - dev.abits = cell.parameters.ABITS; - dev.words = cell.parameters.SIZE; - dev.offset = cell.parameters.OFFSET; + dev.abits = decode_json_number(cell.parameters.ABITS); + dev.words = decode_json_number(cell.parameters.SIZE); + dev.offset = decode_json_number(cell.parameters.OFFSET); dev.rdports = []; dev.wrports = []; const rdpol = decode_json_bigint_as_array(cell.parameters.RD_CLK_POLARITY).reverse(); const rden = decode_json_bigint_as_array(cell.parameters.RD_CLK_ENABLE).reverse(); - const rdtr = decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse(); + const rdtr = cell.type == "$mem" + ? decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse() + : Array(RD_PORTS).fill(0); // TODO decode transparency mask const wrpol = decode_json_bigint_as_array(cell.parameters.WR_CLK_POLARITY).reverse(); const wren = decode_json_bigint_as_array(cell.parameters.WR_CLK_ENABLE).reverse(); const init = typeof(cell.parameters.INIT) == 'number' @@ -849,7 +855,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm } dev.memdata = memdata.toJSON(); } - for (const k of Array(cell.parameters.RD_PORTS).keys()) { + for (const k of Array(RD_PORTS).keys()) { const port: Digitaljs.MemReadPort = { }; if (rden[k]) { @@ -861,7 +867,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm port.transparent = true; dev.rdports.push(port); } - for (const k of Array(cell.parameters.WR_PORTS).keys()) { + for (const k of Array(WR_PORTS).keys()) { const port: Digitaljs.MemWritePort = { }; if (wren[k]) { @@ -891,6 +897,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm if (portmap) connect_device(dname, cell, portmap); else if (cell.type == '$pmux') connect_pmux(dname, cell); else if (cell.type == '$mem') connect_mem(dname, cell, dev); + else if (cell.type == '$mem_v2') connect_mem(dname, cell, dev); else throw Error('Invalid cell type: ' + cell.type); } // Group bits into nets for complex sources From bb1d8cda807c301a080cfb3231f9a3381b2b9705 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Mon, 4 Oct 2021 13:50:45 +0200 Subject: [PATCH 12/38] Add support for $sr, $dffsr and $dffsre --- ChangeLog.md | 2 +- src/index.ts | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 102e412..9885e2c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ### Added -- Support for Yosys 0.10 cells `$adffe`, `$sdff`, `$sdffe`, `$sdffce`, `$adlatch` +- Support for Yosys 0.10 cells `$adffe`, `$sdff`, `$sdffe`, `$sdffce`, `$adlatch`, `$sr`, `$dffsr`, `$dffsre` ### Changed diff --git a/src/index.ts b/src/index.ts index 5b4a242..844c57f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,7 +59,6 @@ const gate_subst = new Map([ ['$pow', 'Power'], ['$mux', 'Mux'], ['$pmux', 'Mux1Hot'], - ['$dff', 'Dff'], ['$mem', 'Memory'], ['$mem_v2', 'Memory'], ['$fsm', 'FSM'], @@ -84,6 +83,7 @@ const gate_subst = new Map([ ['$shiftx', 'ShiftRight'], ['$logic_and', 'And'], ['$logic_or', 'Or'], + ['$dff', 'Dff'], ['$dffe', 'Dff'], ['$adff', 'Dff'], ['$adffe', 'Dff'], @@ -91,7 +91,10 @@ const gate_subst = new Map([ ['$sdffe', 'Dff'], ['$sdffce', 'Dff'], ['$dlatch', 'Dff'], - ['$adlatch', 'Dff']]); + ['$adlatch', 'Dff'], + ['$sr', 'Dff'], + ['$dffsr', 'Dff'], + ['$dffsre', 'Dff']]); const gate_negations = new Map([ ['And', 'Nand'], ['Nand', 'And'], @@ -301,6 +304,9 @@ function order_ports(data: Yosys.Output): Portmaps { '$sdffce': {CLK: 'clk', EN: 'en', SRST: 'srst', D: 'in', Q: 'out'}, '$dlatch': {EN: 'en', D: 'in', Q: 'out'}, '$adlatch': {EN: 'en', ARST: 'arst', D: 'in', Q: 'out'}, + '$dffsr': {CLK: 'clk', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'}, + '$dffsre': {CLK: 'clk', EN: 'en', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'}, + '$sr': {SET: 'set', CLR: 'clr', Q: 'out'}, '$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'} }; binary_gates.forEach((nm) => out[nm] = binmap); @@ -560,7 +566,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.port_directions.B == 'input'); assert(cell.port_directions.Y == 'output'); } - if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch'].includes(cell.type)) { + if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch', '$dffsr', '$dffsre'].includes(cell.type)) { assert(cell.connections.D.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.D == 'input'); @@ -570,6 +576,12 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.port_directions.CLK == 'input'); } } + if (['$dffsr', '$dffsre'].includes(cell.type)) { + assert(cell.connections.SET.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.CLR.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.port_directions.SET == 'input'); + assert(cell.port_directions.CLR == 'input'); + } switch (cell.type) { case '$neg': case '$pos': dev.bits = { @@ -774,6 +786,35 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); break; + case '$dffsr': + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)), + clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY)) + }; + break; + case '$dffsre': + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)), + set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)), + clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY)) + }; + break; + case '$sr': + assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.port_directions.Q == 'output'); + dev.no_data = true; + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + set: Boolean(decode_json_number(cell.parameters.SET_POLARITY)), + clr: Boolean(decode_json_number(cell.parameters.CLR_POLARITY)) + }; + break; case '$fsm': { assert(cell.connections.ARST.length == 1); assert(cell.connections.CLK.length == 1); From f60a89e50ede7d4403ca0b571d8dbb31b0a7980b Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Mon, 4 Oct 2021 13:51:04 +0200 Subject: [PATCH 13/38] Add missing ChangeLog entries --- ChangeLog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 9885e2c..3d67c0e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,3 +11,14 @@ All notable changes to this project will be documented in this file. - Support for JSON output generated by Yosys 0.10 +## [0.4.0] -- 2021-08-20 + +### Added + +- Linting via Verilator + +### Changed + +- Ported to TypeScript +- Removed ad-hoc transformations, as they are now performed in DigitalJS + From 1b151968bfcc6a8c99bbbf301bdaff72851010ab Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Mon, 4 Oct 2021 15:09:54 +0200 Subject: [PATCH 14/38] Support for source code positions --- ChangeLog.md | 1 + src/index.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 3d67c0e..6957762 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Added - Support for Yosys 0.10 cells `$adffe`, `$sdff`, `$sdffe`, `$sdffce`, `$adlatch`, `$sr`, `$dffsr`, `$dffsre` +- Support for source code positions ### Changed diff --git a/src/index.ts b/src/index.ts index 844c57f..0ab0f77 100644 --- a/src/index.ts +++ b/src/index.ts @@ -350,6 +350,18 @@ function decode_json_constant(param: Yosys.JsonConstant, bits: number): string { return param; } +function parse_source_positions(str: string): object[] { + const ret = []; + for (const entry of str.split('|')) { + const colonIdx = entry.lastIndexOf(':'); + const name = entry.slice(0, colonIdx); + const pos = entry.slice(colonIdx+1); + const [from, to] = pos.split('-').map(s => s.split('.').map(v => Number(v))).map(([line, column]) => ({line, column})); + ret.push({name, from, to}); + } + return ret; +} + function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: Options = {}): {[key: string]: Digitaljs.Module} { const out = {}; for (const [name, mod] of Object.entries(data.modules)) { @@ -518,6 +530,9 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.type = 'Subcircuit'; dev.celltype = cell.type; } + if (typeof cell.attributes == 'object' && cell.attributes.src) { + dev.source_positions = parse_source_positions(cell.attributes.src); + } const dname = add_device(dev); function match_port(con: Net, nsig: Yosys.JsonConstant, sz: number) { const sig = decode_json_number(nsig); From 7b31746b9d6dc45c509b3afa14e7939d28629f74 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Mon, 4 Oct 2021 17:49:15 +0200 Subject: [PATCH 15/38] Support for `$mem_v2` --- ChangeLog.md | 1 + src/index.ts | 65 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 6957762..bc03bc9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Added - Support for Yosys 0.10 cells `$adffe`, `$sdff`, `$sdffe`, `$sdffce`, `$adlatch`, `$sr`, `$dffsr`, `$dffsre` +- Support for Yosys 0.10 improved memory cell `$mem_v2` - Support for source code positions ### Changed diff --git a/src/index.ts b/src/index.ts index 0ab0f77..398a0b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -114,7 +114,14 @@ namespace Digitaljs { export type MemReadPort = { clock_polarity?: boolean, enable_polarity?: boolean, - transparent?: boolean + arst_polarity?: boolean, + srst_polarity?: boolean, + enable_srst?: boolean, + transparent?: boolean | boolean[], + collision?: boolean | boolean[], + init_value?: string, + arst_value?: string, + srst_value?: string }; export type MemWritePort = { @@ -151,6 +158,8 @@ namespace Digitaljs { namespace Yosys { + export type BitChar = '0' | '1' | 'x'; + export type Bit = number | '0' | '1' | 'x'; export type BitVector = Bit[]; @@ -342,10 +351,10 @@ function decode_json_bigint_as_array(param: string | number): number[] { return decode_json_bigint(param).toArray(2).value; } -function decode_json_constant(param: Yosys.JsonConstant, bits: number): string { +function decode_json_constant(param: Yosys.JsonConstant, bits: number, fill : Yosys.BitChar = '0'): string { if (typeof param == 'number') return bigInt(param).toArray(2).value.map(String).reverse() - .concat(Array(bits).fill('0')).slice(0, bits).reverse().join(''); + .concat(Array(bits).fill(fill)).slice(0, bits).reverse().join(''); else return param; } @@ -885,6 +894,10 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.connections.WR_CLK.length == WR_PORTS); assert(cell.connections.WR_DATA.length == WR_PORTS * decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.WR_ADDR.length == WR_PORTS * decode_json_number(cell.parameters.ABITS)); + if (cell.type == "$mem_v2") { + assert(cell.connections.RD_ARST.length == RD_PORTS); + assert(cell.connections.RD_SRST.length == RD_PORTS); + } dev.bits = decode_json_number(cell.parameters.WIDTH); dev.abits = decode_json_number(cell.parameters.ABITS); dev.words = decode_json_number(cell.parameters.SIZE); @@ -893,14 +906,22 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.wrports = []; const rdpol = decode_json_bigint_as_array(cell.parameters.RD_CLK_POLARITY).reverse(); const rden = decode_json_bigint_as_array(cell.parameters.RD_CLK_ENABLE).reverse(); - const rdtr = cell.type == "$mem" - ? decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse() - : Array(RD_PORTS).fill(0); // TODO decode transparency mask + const rdtr = cell.type == "$mem_v2" + ? [] + : decode_json_bigint_as_array(cell.parameters.RD_TRANSPARENT).reverse(); const wrpol = decode_json_bigint_as_array(cell.parameters.WR_CLK_POLARITY).reverse(); const wren = decode_json_bigint_as_array(cell.parameters.WR_CLK_ENABLE).reverse(); const init = typeof(cell.parameters.INIT) == 'number' - ? bigInt(cell.parameters.INIT).toArray(2).value.map(String).reverse() - : cell.parameters.INIT.split('').reverse(); + ? bigInt(cell.parameters.INIT).toArray(2).value.map(String).reverse() + : cell.parameters.INIT.split('').reverse(); + const v2_feature = (param) => cell.type == "$mem_v2" ? decode_json_bigint_as_array(param).reverse() : []; + const v2_feature_const = (param, size) => cell.type == "$mem_v2" ? decode_json_constant(param, size) : ""; + const rdtrmask = v2_feature(cell.parameters.RD_TRANSPARENCY_MASK); + const rdcolmask = v2_feature(cell.parameters.RD_COLLISION_X_MASK); + const rdensrst = v2_feature(cell.parameters.RD_CE_OVER_SRST); + const rdinit = v2_feature_const(cell.parameters.RD_INIT_VALUE, dev.bits * RD_PORTS); + const rdarst = v2_feature_const(cell.parameters.RD_ARST_VALUE, dev.bits * RD_PORTS); + const rdsrst = v2_feature_const(cell.parameters.RD_SRST_VALUE, dev.bits * RD_PORTS); if (cell.parameters.INIT) { const l = init.slice(-1)[0] == 'x' ? 'x' : '0'; const memdata = new Mem3vl(dev.bits, dev.words); @@ -921,6 +942,34 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; if (rdtr[k]) port.transparent = true; + if (cell.type == "$mem_v2") { + if (rdensrst[k]) + port.enable_srst = true; + function mk_init(s: string, f: (v: string) => void) { + const v = s.slice(dev.bits * k, dev.bits * (k+1)); + if (!v.split('').every(c => c == 'x')) + f(v); + }; + mk_init(rdinit, v => port.init_value = v); + if (cell.connections.RD_ARST[k] != '0') { + port.arst_polarity = true; + mk_init(rdarst, v => port.arst_value = v); + } + if (cell.connections.RD_SRST[k] != '0') { + port.srst_polarity = true; + mk_init(rdsrst, v => port.srst_value = v); + } + function mk_mask(s: number[], f: (v: boolean | boolean[]) => void) { + const v = Array(WR_PORTS).fill(0); + s.slice(WR_PORTS * k, WR_PORTS * (k+1)).map((c, i) => { v[i] = c }); + if (v.every(c => c)) + f(true); + else if (v.some(c => c)) + f(v.map(c => Boolean(c))); + } + mk_mask(rdtrmask, v => port.transparent = v); + mk_mask(rdcolmask, v => port.collision = v); + } dev.rdports.push(port); } for (const k of Array(WR_PORTS).keys()) { From 7b517c75a9456dcfb70912603dbfe33e810e8e79 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Tue, 5 Oct 2021 13:45:33 +0200 Subject: [PATCH 16/38] More support for `$mem_v2` --- src/index.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 398a0b2..538cf58 100644 --- a/src/index.ts +++ b/src/index.ts @@ -126,7 +126,8 @@ namespace Digitaljs { export type MemWritePort = { clock_polarity?: boolean, - enable_polarity?: boolean + enable_polarity?: boolean, + no_bit_enable?: boolean }; export type Device = { @@ -484,6 +485,10 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm add_net_target([cell.connections.RD_CLK[k]], dname, portname + "clk"); if ('enable_polarity' in port) add_net_target([cell.connections.RD_EN[k]], dname, portname + "en"); + if ('arst_polarity' in port) + add_net_target([cell.connections.RD_ARST[k]], dname, portname + "arst"); + if ('srst_polarity' in port) + add_net_target([cell.connections.RD_SRST[k]], dname, portname + "srst"); } for (const [k, port] of dev.wrports.entries()) { const portname = "wr" + k; @@ -493,9 +498,13 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dname, portname + "data"); if ('clock_polarity' in port) add_net_target([cell.connections.WR_CLK[k]], dname, portname + "clk"); - if ('enable_polarity' in port) - add_net_target(cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)), - dname, portname + "en"); + if ('enable_polarity' in port) { + if (port.no_bit_enable) + add_net_target([cell.connections.WR_EN[dev.bits * k]], dname, portname + "en"); + else + add_net_target(cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)), + dname, portname + "en"); + } } } // Find net names @@ -977,9 +986,12 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; if (wren[k]) { port.clock_polarity = Boolean(wrpol[k]); - if (cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)) - .some(z => z != '1')) + const wr_en_connections = cell.connections.WR_EN.slice(dev.bits * k, dev.bits * (k+1)); + if (wr_en_connections.some(z => z != '1')) { port.enable_polarity = true; + if (wr_en_connections.every(z => z == wr_en_connections[0])) + port.no_bit_enable = true; + } }; dev.wrports.push(port); } From 38c43f9241158e053aea1fe724d8bc2e033d8510 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Tue, 5 Oct 2021 15:24:23 +0200 Subject: [PATCH 17/38] Fix issue with Yosys 0.10 output --- src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 538cf58..07742b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -661,8 +661,8 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm in2: cell.connections.B.length }; dev.signed = { - in1: Boolean(cell.parameters.A_SIGNED), - in2: Boolean(cell.parameters.B_SIGNED) + in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)), + in2: Boolean(decode_json_number(cell.parameters.B_SIGNED)) }; zero_extend_output(cell.connections.Y); break; @@ -674,9 +674,9 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm out: cell.connections.Y.length }; dev.signed = { - in1: Boolean(cell.parameters.A_SIGNED), - in2: Boolean(cell.parameters.B_SIGNED && ['$shift', '$shiftx'].includes(cell.type)), - out: Boolean(cell.parameters.A_SIGNED && ['$sshl', '$sshr'].includes(cell.type)) + in1: Boolean(decode_json_number(cell.parameters.A_SIGNED)), + in2: Boolean(decode_json_number(cell.parameters.B_SIGNED) && ['$shift', '$shiftx'].includes(cell.type)), + out: Boolean(decode_json_number(cell.parameters.A_SIGNED) && ['$sshl', '$sshr'].includes(cell.type)) }; dev.fillx = cell.type == '$shiftx'; break; From ef0053bc91639b5a0c3055101eea98d0b931810f Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Tue, 5 Oct 2021 15:59:15 +0200 Subject: [PATCH 18/38] Source positions for connectors --- src/index.ts | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 07742b7..e4786a7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -111,6 +111,17 @@ const gate_negations = new Map([ namespace Digitaljs { + export type FilePosition = { + line: number, + column: number + }; + + export type SourcePosition = { + name: string, + from: FilePosition, + to: FilePosition + }; + export type MemReadPort = { clock_polarity?: boolean, enable_polarity?: boolean, @@ -132,6 +143,7 @@ namespace Digitaljs { export type Device = { type: string, + source_positions?: SourcePosition[], [key: string]: any }; @@ -143,7 +155,8 @@ namespace Digitaljs { export type Connector = { from: Port, to: Port, - name?: string + name?: string, + source_positions?: SourcePosition[] }; export type Module = { @@ -259,7 +272,8 @@ type Net = Bit[]; type NetInfo = { source: undefined | Digitaljs.Port, targets: Digitaljs.Port[], - name: undefined | string + name: undefined | string, + source_positions: Digitaljs.SourcePosition[] }; type BitInfo = { @@ -360,7 +374,7 @@ function decode_json_constant(param: Yosys.JsonConstant, bits: number, fill : Yo return param; } -function parse_source_positions(str: string): object[] { +function parse_source_positions(str: string): Digitaljs.SourcePosition[] { const ret = []; for (const entry of str.split('|')) { const colonIdx = entry.lastIndexOf(':'); @@ -385,7 +399,8 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm return bit == '0' || bit == '1' || bit == 'x'; } const nets = new HashMap(); - const netnames = new HashMap(); + const netnames = new HashMap(); + const netsrc = new HashMap(); const bits = new Map(); const devnets = new Map>(); let n = 0, pn = 0; @@ -401,7 +416,8 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm // create net if does not exist yet if (!nets.has(k)) { const nms = netnames.get(k); - nets.set(k, {source: undefined, targets: [], name: nms ? nms[0] : undefined}); + const src = netsrc.get(k); + nets.set(k, {source: undefined, targets: [], name: nms ? nms[0] : undefined, source_positions: src || []}); } return nets.get(k); } @@ -516,6 +532,15 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm netnames.set(data.bits, l); } l.push(nname); + if (typeof data.attributes == 'object' && data.attributes.src) { + let l = netsrc.get(data.bits); + if (l === undefined) { + l = []; + netsrc.set(data.bits, l); + } + const positions = parse_source_positions(data.attributes.src); + l.push(...positions); + } } // Add inputs/outputs for (const [pname, port] of Object.entries(mod.ports)) { @@ -1101,6 +1126,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm from: net.source }; if (net.name) conn.name = net.name; + if (net.source_positions) conn.source_positions = net.source_positions; if (!first && mout.devices[conn.from.id].type == "Constant") { // replicate constants for better clarity const dname = add_device({ From 6e3dca080080f0dfbc61e15f0f6998a4865d35ef Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Wed, 6 Oct 2021 13:31:37 +0200 Subject: [PATCH 19/38] Update dependencies. --- package-lock.json | 18 +++++++++--------- package.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1c39e8d..200e28a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "integrity": "sha512-xyBvuoClECIQHqKDEhcDnpaAVTDvcWV3BcX0farUKBjaoxAXta/4zyJr5l3utnuf90kbQPvXWCFAK71O4u5N1g==" }, "@types/node": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.2.tgz", - "integrity": "sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA==", + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, "balanced-match": { @@ -21,9 +21,9 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + "version": "1.6.49", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz", + "integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==" }, "bluebird": { "version": "3.5.5", @@ -154,9 +154,9 @@ } }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "utf8-byte-length": { diff --git a/package.json b/package.json index ee564a4..a132fde 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "BSD-2-Clause", "dependencies": { "3vl": "^0.3.5", - "big-integer": "^1.6.48", + "big-integer": "^1.6.49", "hashmap": "^2.4.0", "minimist": "^1.2.5", "sanitize-filename": "^1.6.3", @@ -30,7 +30,7 @@ "url": "https://github.com/tilk/yosys2digitaljs.git" }, "devDependencies": { - "@types/node": "^16.6.2", - "typescript": "^4.3.5" + "@types/node": "^16.10.3", + "typescript": "^4.4.3" } } From 7b2512633ccc93779344e984c881de632e806826 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Wed, 6 Oct 2021 13:33:25 +0200 Subject: [PATCH 20/38] Bump version number. --- ChangeLog.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index bc03bc9..cee0f50 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -## [Unreleased] +## [0.5.0] -- 2021-10-06 ### Added diff --git a/package-lock.json b/package-lock.json index 200e28a..2641925 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a132fde..e133665 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.4.0", + "version": "0.5.0", "description": "Export Yosys netlists to a logic simulator", "main": "dist/index", "types": "dist/index.d.ts", From d6c61e906ca6de212280aa136831c46ed05e8100 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Wed, 22 Dec 2021 11:29:20 +0100 Subject: [PATCH 21/38] Add support for new Yosys 0.11 cells --- ChangeLog.md | 6 ++++++ src/index.ts | 61 ++++++++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index cee0f50..454c176 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,12 @@ # Changelog All notable changes to this project will be documented in this file. +## Unreleased + +### Added + +- Support for Yosys 0.11 cells `$aldff`, `$aldffe` + ## [0.5.0] -- 2021-10-06 ### Added diff --git a/src/index.ts b/src/index.ts index e4786a7..1c596b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -94,7 +94,9 @@ const gate_subst = new Map([ ['$adlatch', 'Dff'], ['$sr', 'Dff'], ['$dffsr', 'Dff'], - ['$dffsre', 'Dff']]); + ['$dffsre', 'Dff'], + ['$aldff', 'Dff'], + ['$aldffe', 'Dff']]); const gate_negations = new Map([ ['And', 'Nand'], ['Nand', 'And'], @@ -330,6 +332,8 @@ function order_ports(data: Yosys.Output): Portmaps { '$adlatch': {EN: 'en', ARST: 'arst', D: 'in', Q: 'out'}, '$dffsr': {CLK: 'clk', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'}, '$dffsre': {CLK: 'clk', EN: 'en', SET: 'set', CLR: 'clr', D: 'in', Q: 'out'}, + '$aldff': {CLK: 'clk', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'}, + '$aldffe': {CLK: 'clk', EN: 'en', ALOAD: 'aload', AD: 'ain', D: 'in', Q: 'out'}, '$sr': {SET: 'set', CLR: 'clr', Q: 'out'}, '$fsm': {ARST: 'arst', CLK: 'clk', CTRL_IN: 'in', CTRL_OUT: 'out'} }; @@ -624,7 +628,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.port_directions.B == 'input'); assert(cell.port_directions.Y == 'output'); } - if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch', '$dffsr', '$dffsre'].includes(cell.type)) { + if (['$dff', '$dffe', '$adff', '$adffe', '$sdff', '$sdffe', '$sdffce', '$dlatch', '$adlatch', '$dffsr', '$dffsre', '$aldff', '$aldffe'].includes(cell.type)) { assert(cell.connections.D.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.Q.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.port_directions.D == 'input'); @@ -634,6 +638,18 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm assert(cell.port_directions.CLK == 'input'); } } + if (['$dffe', '$adffe', '$sdffe', '$sdffce', '$dffsre', '$aldffe', '$dlatch', '$adlatch'].includes(cell.type)) { + assert(cell.connections.EN.length == 1); + assert(cell.port_directions.EN == 'input'); + } + if (['$adff', '$adffe', '$adlatch'].includes(cell.type)) { + assert(cell.connections.ARST.length == 1); + assert(cell.port_directions.ARST == 'input'); + } + if (['$sdff', '$sdffe', '$sdffce'].includes(cell.type)) { + assert(cell.connections.SRST.length == 1); + assert(cell.port_directions.SRST == 'input'); + } if (['$dffsr', '$dffsre'].includes(cell.type)) { assert(cell.connections.SET.length == decode_json_number(cell.parameters.WIDTH)); assert(cell.connections.CLR.length == decode_json_number(cell.parameters.WIDTH)); @@ -756,17 +772,28 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; break; case '$dffe': - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) }; break; + case '$aldff': + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + aload: Boolean(decode_json_number(cell.parameters.ALOAD_POLARITY)) + }; + break; + case '$aldffe': + dev.bits = decode_json_number(cell.parameters.WIDTH); + dev.polarity = { + clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), + enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)), + aload: Boolean(decode_json_number(cell.parameters.ALOAD_POLARITY)) + }; + break; case '$adff': - assert(cell.connections.ARST.length == 1); - assert(cell.port_directions.ARST == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), @@ -775,8 +802,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); break; case '$sdff': - assert(cell.connections.SRST.length == 1); - assert(cell.port_directions.SRST == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), @@ -785,10 +810,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); break; case '$adffe': - assert(cell.connections.ARST.length == 1); - assert(cell.port_directions.ARST == 'input'); - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), @@ -798,10 +819,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.arst_value = decode_json_constant(cell.parameters.ARST_VALUE, dev.bits); break; case '$sdffe': - assert(cell.connections.SRST.length == 1); - assert(cell.port_directions.SRST == 'input'); - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), @@ -811,10 +828,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); break; case '$sdffce': - assert(cell.connections.SRST.length == 1); - assert(cell.port_directions.SRST == 'input'); - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), @@ -825,18 +838,12 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm dev.srst_value = decode_json_constant(cell.parameters.SRST_VALUE, dev.bits); break; case '$dlatch': - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)) }; break; case '$adlatch': - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); - assert(cell.connections.ARST.length == 1); - assert(cell.port_directions.ARST == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { enable: Boolean(decode_json_number(cell.parameters.EN_POLARITY)), @@ -853,8 +860,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm }; break; case '$dffsre': - assert(cell.connections.EN.length == 1); - assert(cell.port_directions.EN == 'input'); dev.bits = decode_json_number(cell.parameters.WIDTH); dev.polarity = { clock: Boolean(decode_json_number(cell.parameters.CLK_POLARITY)), From e8180f7c950ab043e9cd5d71c2790345feeff38d Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Sun, 16 Jan 2022 23:13:28 -0500 Subject: [PATCH 22/38] Export a yosys2digitaljs function to do the full conversion From Yosys.Output to Digitaljs.TopModule. This allow doing reusing most of the yosys2digitaljs functionality without running the yosys command line tool. --- src/index.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1c596b0..86940c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1177,6 +1177,18 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti } } +export function yosys2digitaljs(obj: Yosys.Output, options: Options = {}): Digitaljs.TopModule { + const portmaps = order_ports(obj); + const out = yosys_to_digitaljs(obj, portmaps, options); + const toporder = topsort(module_deps(obj)); + toporder.pop(); + const toplevel = toporder.pop(); + const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] }; + for (const x of toporder) + output.subcircuits[x] = out[x]; + return output; +} + export async function process(filenames: string[], dirname?: string, options: Options = {}): Promise { const optimize_simp = options.optimize ? "; opt" : "; opt_clean"; const optimize = options.optimize ? "; opt -full" : "; opt_clean"; @@ -1203,13 +1215,7 @@ export async function process(filenames: string[], dirname?: string, options: Op } obj = JSON.parse(fs.readFileSync(tmpjson, 'utf8')); await promisify(fs.unlink)(tmpjson); - const portmaps = order_ports(obj); - const out = yosys_to_digitaljs(obj, portmaps, options); - const toporder = topsort(module_deps(obj)); - toporder.pop(); - const toplevel = toporder.pop(); - const output: Digitaljs.TopModule = { subcircuits: {}, ... out[toplevel] }; - for (const x of toporder) output.subcircuits[x] = out[x]; + const output = yosys2digitaljs(obj, options); const ret: Output = { output: output, yosys_output: obj, From d4e3da6aa9abd44bcd216229ea83553b982dd746 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sat, 29 Jan 2022 12:53:44 +0100 Subject: [PATCH 23/38] Split options type, add options to all API funs --- src/index.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 86940c1..eb9f594 100644 --- a/src/index.ts +++ b/src/index.ts @@ -247,8 +247,11 @@ namespace Yosys { }; -type Options = { +type ConvertOptions = { propagation?: number, +}; + +type Options = ConvertOptions & { optimize?: boolean, fsmexpand?: boolean, fsm?: boolean | "nomap", @@ -390,7 +393,7 @@ function parse_source_positions(str: string): Digitaljs.SourcePosition[] { return ret; } -function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: Options = {}): {[key: string]: Digitaljs.Module} { +function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: ConvertOptions = {}): {[key: string]: Digitaljs.Module} { const out = {}; for (const [name, mod] of Object.entries(data.modules)) { out[name] = yosys_to_digitaljs_mod(name, mod, portmaps, options); @@ -398,7 +401,7 @@ function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: Opt return out } -function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: Options = {}): Digitaljs.Module { +function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: ConvertOptions = {}): Digitaljs.Module { function constbit(bit: Bit) { return bit == '0' || bit == '1' || bit == 'x'; } @@ -1177,7 +1180,7 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti } } -export function yosys2digitaljs(obj: Yosys.Output, options: Options = {}): Digitaljs.TopModule { +export function yosys2digitaljs(obj: Yosys.Output, options: ConvertOptions = {}): Digitaljs.TopModule { const portmaps = order_ports(obj); const out = yosys_to_digitaljs(obj, portmaps, options); const toporder = topsort(module_deps(obj)); @@ -1250,7 +1253,7 @@ export function io_ui(output: Digitaljs.Module) { } } -export async function process_files(data: {[key: string]: string}, options: Options): Promise { +export async function process_files(data: {[key: string]: string}, options: Options = {}): Promise { const dir = await tmp.dir(); const names = []; try { @@ -1268,12 +1271,12 @@ export async function process_files(data: {[key: string]: string}, options: Opti } } -export async function process_sv(text: string): Promise { +export async function process_sv(text: string, options: Options = {}): Promise { const tmpsv = await tmp.file({ postfix: '.sv' }); try { await promisify(fs.write)(tmpsv.fd, text); await promisify(fs.close)(tmpsv.fd); - return await process([tmpsv.path]); + return await process([tmpsv.path], undefined, options); } finally { tmpsv.cleanup(); } From dda00e5a484600837eb602dc3544c2e8e6ef98f4 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sat, 29 Jan 2022 12:54:29 +0100 Subject: [PATCH 24/38] Update ChangeLog and README --- ChangeLog.md | 1 + README.md | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 454c176..48ce1f7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Added - Support for Yosys 0.11 cells `$aldff`, `$aldffe` +- Added a function `yosys2digitaljs` for converting a Yosys JSON output obtained elsewhere, without running Yosys locally ## [0.5.0] -- 2021-10-06 diff --git a/README.md b/README.md index dea66bb..6d910a7 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,10 @@ The generated JSON is printed on standard output. # API Yosys2digitaljs can be used as a library. The API is promise (or async/await) based. Available functions are: - - `process_sv(sv_text)` - converts a single SystemVerilog source passed as a string. - - `process_files(texts)` - converts multiple Verilog/SystemVerilog sources. The `texts` parameter is an object, with keys being file names, and corresponding values containing the file contents as strings. Example: `{ 'test.sv': 'module test; ...' }`. - - `process(filenames, dirname)` - converts Verilog/SystemVerilog sources saved on the filesystem under names `filenames` in the directory `dirname`. + - `yosys2digitaljs(json, options)` - converts the Yosys JSON output `json` (passed as an JS object) to a DigitalJS representation of the same circuit. + - `process_sv(sv_text, options)` - converts a single SystemVerilog source passed as a string. + - `process_files(texts, options)` - converts multiple Verilog/SystemVerilog sources. The `texts` parameter is an object, with keys being file names, and corresponding values containing the file contents as strings. Example: `{ 'test.sv': 'module test; ...' }`. + - `process(filenames, dirname, options)` - converts Verilog/SystemVerilog sources saved on the filesystem under names `filenames` in the directory `dirname`. The functions return a promise, which fulfills with an object value with following keys: From 6287d4fb97d5103f13ac553f84d088d8bbe3ba74 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Sat, 29 Jan 2022 12:55:37 +0100 Subject: [PATCH 25/38] Remove Travis --- .travis.yml | 8 -------- README.md | 1 - 2 files changed, 9 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 90c4735..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -before_install: - - sudo add-apt-repository ppa:saltmakrell/ppa -y - - sudo apt-get update - - sudo apt-get install -y yosys - -language: node_js -node_js: - - "8" diff --git a/README.md b/README.md index 6d910a7..627efe0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![Build Status](https://travis-ci.org/tilk/yosys2digitaljs.svg?branch=master)](https://travis-ci.org/tilk/yosys2digitaljs) # yosys2digitaljs This program converts JSON netlist output generated by [Yosys](http://www.clifford.at/yosys/) circuit synthesis software for use with the From b40e24cf13dbc8b0232c074013604c5a2d4b0275 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Wed, 2 Feb 2022 10:43:02 +0100 Subject: [PATCH 26/38] Bump version number --- ChangeLog.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 48ce1f7..555ba4e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -## Unreleased +## [0.6.0] -- 2022-02-02 ### Added diff --git a/package-lock.json b/package-lock.json index 2641925..fdfff8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e133665..c33fbc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.5.0", + "version": "0.6.0", "description": "Export Yosys netlists to a logic simulator", "main": "dist/index", "types": "dist/index.d.ts", From 049970854a7f5c0bfc8900e8929d3dfbc1106ebe Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Thu, 10 Feb 2022 11:59:33 -0500 Subject: [PATCH 27/38] Fix RAM test module name --- tests/ram.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ram.sv b/tests/ram.sv index 0d634e4..18a5046 100644 --- a/tests/ram.sv +++ b/tests/ram.sv @@ -1,5 +1,5 @@ // Simple RAM -module rom +module ram #(parameter AWIDTH = 4, DWIDTH = 4)( input clk, input [AWIDTH-1:0] addr, From b97870419fee529339fce0465376968f3f1507eb Mon Sep 17 00:00:00 2001 From: Engineer-of-Efficiency <100697850+Engineer-of-Efficiency@users.noreply.github.com> Date: Tue, 1 Mar 2022 14:49:20 +0100 Subject: [PATCH 28/38] Change yosys link to actual repository Changed the link from one to a scam site to one to the actual site and the actual repository. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 627efe0..d52ac2b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # yosys2digitaljs -This program converts JSON netlist output generated by [Yosys](http://www.clifford.at/yosys/) -circuit synthesis software for use with the +This program converts JSON netlist output generated by [Yosys](https://yosyshq.net/yosys/) +circuit synthesis software (Github repo [here](https://github.com/YosysHQ/yosys/)) for use with the [DigitalJS](http://github.com/tilk/digitaljs) graphical circuit simulator. # Usage -You need to have [Yosys](http://www.clifford.at/yosys/) installed to run +You need to have [Yosys](https://yosyshq.net/yosys/) installed to run yosys2digitaljs. For example, in Debian/Ubuntu, run: ```bash apt install yosys From 9167d768673a3ceff59dcf0fff9d8ab2df65bf5d Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Wed, 10 Aug 2022 17:27:43 +0200 Subject: [PATCH 29/38] Add test on PR, change node versions on CI (#11) --- .github/workflows/nodejs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 01694d9..de9d9ca 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,6 +1,6 @@ name: Node.js CI -on: [push] +on: [push, pull_request] jobs: build: @@ -9,7 +9,7 @@ jobs: strategy: matrix: - node-version: [8.x, 10.x, 12.x] + node-version: [12.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 From 500a17eaabc17054dfaabe30aede15ee9827d8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Huisman?= Date: Mon, 19 Sep 2022 15:05:40 +0200 Subject: [PATCH 30/38] Add LUT to memory cell mapping (#13) --- src/index.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/index.ts b/src/index.ts index eb9f594..ae19e9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,7 @@ const gate_subst = new Map([ ['$pmux', 'Mux1Hot'], ['$mem', 'Memory'], ['$mem_v2', 'Memory'], + ['$lut', 'Memory'], ['$fsm', 'FSM'], ['$clock', 'Clock'], ['$button', 'Button'], @@ -1030,6 +1031,24 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm } break; } + case '$lut': + assert(cell.connections.A.length == decode_json_number(cell.parameters.WIDTH)); + assert(cell.connections.Y.length == 1); + assert(cell.port_directions.A == 'input'); + assert(cell.port_directions.Y == 'output'); + dev.abits = cell.connections.A.length; + dev.bits = cell.connections.Y.length; + dev.rdports = [{}]; + dev.wrports = []; + dev.memdata = cell.parameters.LUT.split('').reverse(); + assert(dev.memdata.length == Math.pow(2, dev.abits)); + + // Rewrite cell connections to be $mem compatible for port mapping + cell.connections.RD_ADDR = cell.connections.A; + cell.connections.RD_DATA = cell.connections.Y; + delete cell.connections.A; + delete cell.connections.Y; + break; default: } if (dev.type == 'Dff') { @@ -1048,6 +1067,7 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm else if (cell.type == '$pmux') connect_pmux(dname, cell); else if (cell.type == '$mem') connect_mem(dname, cell, dev); else if (cell.type == '$mem_v2') connect_mem(dname, cell, dev); + else if (cell.type == '$lut') connect_mem(dname, cell, dev); else throw Error('Invalid cell type: ' + cell.type); } // Group bits into nets for complex sources From 1b4afeae612bcb75eb4e8bffc8c86029d5c6606b Mon Sep 17 00:00:00 2001 From: mateusz-sz Date: Mon, 24 Oct 2022 11:35:41 +0200 Subject: [PATCH 31/38] Add 7 segment display device type. (#14) --- src/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index ae19e9b..f5ec8b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1268,8 +1268,14 @@ export function io_ui(output: Digitaljs.Module) { } if (dev.type == 'Input') dev.type = dev.bits == 1 ? 'Button' : 'NumEntry'; - if (dev.type == 'Output') - dev.type = dev.bits == 1 ? 'Lamp' : 'NumDisplay'; + if (dev.type == 'Output') { + if (dev.bits == 1) + dev.type = 'Lamp'; + else if (dev.bits == 8 && (dev.label == 'display7' || dev.label.startsWith('display7_'))) + dev.type = 'Display7'; + else + dev.type = 'NumDisplay'; + } } } From 69a7d1163021259e717ce0cca784d58e83390c88 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 23 Mar 2023 14:16:33 +0100 Subject: [PATCH 32/38] Bump deps and version number --- ChangeLog.md | 7 ++ package-lock.json | 248 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 4 +- 3 files changed, 244 insertions(+), 15 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 555ba4e..e31e60d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,13 @@ # Changelog All notable changes to this project will be documented in this file. +## [0.7.0] -- 2023-03-2023 + +### Added + +- Support for `$lut` cells +- Support for generating 7-segment display outputs + ## [0.6.0] -- 2022-02-02 ### Added diff --git a/package-lock.json b/package-lock.json index fdfff8d..9461362 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,242 @@ { "name": "yosys2digitaljs", - "version": "0.6.0", - "lockfileVersion": 1, + "version": "0.7.0", + "lockfileVersion": 2, "requires": true, - "dependencies": { - "3vl": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/3vl/-/3vl-0.3.5.tgz", - "integrity": "sha512-xyBvuoClECIQHqKDEhcDnpaAVTDvcWV3BcX0farUKBjaoxAXta/4zyJr5l3utnuf90kbQPvXWCFAK71O4u5N1g==" + "packages": { + "": { + "name": "yosys2digitaljs", + "version": "0.7.0", + "license": "BSD-2-Clause", + "dependencies": { + "3vl": "^1.0.1", + "big-integer": "^1.6.49", + "hashmap": "^2.4.0", + "minimist": "^1.2.5", + "sanitize-filename": "^1.6.3", + "tmp-promise": "^1.1.0", + "topsort": "^0.0.2" + }, + "devDependencies": { + "@types/node": "^16.10.3", + "typescript": "^4.4.3" + }, + "engines": { + "node": ">=8.11.1" + } + }, + "node_modules/@types/node": { + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", + "dev": true + }, + "node_modules/3vl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/3vl/-/3vl-1.0.1.tgz", + "integrity": "sha512-Z75UMwicaLbb3p1FjL1cBxGq/rpJIv73h5LtHl3FWD7vfOSjxzU37LAYhyMYul4tZa0wZtsJHtqCYeisIsVlJw==" + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "node_modules/big-integer": { + "version": "1.6.49", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz", + "integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/hashmap": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/hashmap/-/hashmap-2.4.0.tgz", + "integrity": "sha512-Ngj48lhnxJdnBAEVbubKBJuN1elfVLZJs94ZixRi98X3GCU4v6pgj9qRkHt6H8WaVJ69Wv0r1GhtS7hvF9zCgg==", + "engines": { + "node": "*" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "dependencies": { + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tmp-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz", + "integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==", + "dependencies": { + "bluebird": "^3.5.0", + "tmp": "0.1.0" + } + }, + "node_modules/topsort": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/topsort/-/topsort-0.0.2.tgz", + "integrity": "sha1-Ll4O6KFDlBfxAdW5stA15iAmMyE=", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + }, + "dependencies": { "@types/node": { "version": "16.10.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==", "dev": true }, + "3vl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/3vl/-/3vl-1.0.1.tgz", + "integrity": "sha512-Z75UMwicaLbb3p1FjL1cBxGq/rpJIv73h5LtHl3FWD7vfOSjxzU37LAYhyMYul4tZa0wZtsJHtqCYeisIsVlJw==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -82,17 +304,17 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "once": { "version": "1.4.0", diff --git a/package.json b/package.json index c33fbc9..59da246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.6.0", + "version": "0.7.0", "description": "Export Yosys netlists to a logic simulator", "main": "dist/index", "types": "dist/index.d.ts", @@ -13,7 +13,7 @@ "author": "Marek Materzok", "license": "BSD-2-Clause", "dependencies": { - "3vl": "^0.3.5", + "3vl": "^1.0.1", "big-integer": "^1.6.49", "hashmap": "^2.4.0", "minimist": "^1.2.5", From a217e59c659877fe74ca116727f644efddca2abe Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Thu, 23 Mar 2023 14:20:49 +0100 Subject: [PATCH 33/38] Fix workflow --- .github/workflows/nodejs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index de9d9ca..428fc90 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [18.x] steps: - uses: actions/checkout@v2 From 3c1ad441029bdbbc73f677756cf9b6003495b4fb Mon Sep 17 00:00:00 2001 From: "Pepper (they/them)" <111446242+peppergrayxyz@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:21:07 +0100 Subject: [PATCH 34/38] add support for high impedance state 'bZ (#15) --- src/index.ts | 8 +++++--- tests/z.sv | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 tests/z.sv diff --git a/src/index.ts b/src/index.ts index f5ec8b1..5f7a9ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -175,9 +175,11 @@ namespace Digitaljs { namespace Yosys { - export type BitChar = '0' | '1' | 'x'; + export const ConstChars = ["0", "1", "x", "z"] as const; - export type Bit = number | '0' | '1' | 'x'; + export type BitChar = (typeof ConstChars)[number]; + + export type Bit = number | BitChar; export type BitVector = Bit[]; @@ -404,7 +406,7 @@ function yosys_to_digitaljs(data: Yosys.Output, portmaps: Portmaps, options: Con function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portmaps, options: ConvertOptions = {}): Digitaljs.Module { function constbit(bit: Bit) { - return bit == '0' || bit == '1' || bit == 'x'; + return (Yosys.ConstChars as readonly string[]).includes(bit.toString()); } const nets = new HashMap(); const netnames = new HashMap(); diff --git a/tests/z.sv b/tests/z.sv new file mode 100644 index 0000000..26f8321 --- /dev/null +++ b/tests/z.sv @@ -0,0 +1,7 @@ +// high impedance state +module sourceZ( + output out +); + assign out = 1'bZ; + +endmodule \ No newline at end of file From 79f9704b7bb2f669f92490ef2574829f22c0b5b0 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 4 Aug 2023 13:57:30 +0200 Subject: [PATCH 35/38] Workaround for new versions of Yosys --- src/index.ts | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5f7a9ee..5406145 100644 --- a/src/index.ts +++ b/src/index.ts @@ -436,8 +436,6 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm const net = get_net(k); if(net.source !== undefined) { // multiple sources driving one net, disallowed in digitaljs - console.log(k); - console.log(net); throw Error('Multiple sources driving net: ' + net.name); } net.source = { id: d, port: p }; @@ -1172,17 +1170,36 @@ function yosys_to_digitaljs_mod(name: string, mod: Yosys.Module, portmaps: Portm return mout; } -function escape_filename(cmd: string): string { - return '"' + cmd.replace(/(["\s'$`\\])/g,'\\$1') + '"'; +function ansi_c_escape_contents(cmd: string): string { + function func(ch: string) { + if (ch == '\t') return '\\t'; + if (ch == '\r') return '\\r'; + if (ch == '\n') return '\\n'; + return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0'); + } + return cmd.replace(/(["'\\])/g,'\\$1') + .replace(/[\x00-\x1F\x7F-\x9F]/g, func); +} + +function ansi_c_escape(cmd: string): string { + return '"' + ansi_c_escape_contents(cmd) + '"'; +} + +function shell_escape_contents(cmd: string): string { + return cmd.replace(/(["\r\n$`\\])/g,'\\$1'); +} + +function shell_escape(cmd: string): string { + return '"' + shell_escape_contents(cmd) + '"'; } - + const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/; export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise { try { const output: LintMessage[] = []; const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)( - 'verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(escape_filename).join(' '), + 'verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(shell_escape).join(' '), {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); for (const line of verilator_result.stderr.split('\n')) { @@ -1224,8 +1241,9 @@ export async function process(filenames: string[], dirname?: string, options: Op const tmpjson = await tmp.tmpName({ postfix: '.json' }); let obj = undefined; const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( - 'yosys -p "hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' + - optimize + '" -o "' + tmpjson + '" ' + filenames.map(escape_filename).join(' '), + 'yosys -p "read_verilog ' + shell_escape_contents(filenames.map(ansi_c_escape).join(' ')) + + '; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' + + optimize + '" -o "' + tmpjson + '"', {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); try { From 956f443b3a24fc04280a7028d58b90ea78d668b3 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 19 Jan 2024 13:47:07 +0100 Subject: [PATCH 36/38] Handle SystemVerilog --- src/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 5406145..eea71ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1193,6 +1193,11 @@ function shell_escape(cmd: string): string { return '"' + shell_escape_contents(cmd) + '"'; } +function process_filename(filename: string): string { + const flags = /\.sv$/.test(filename) ? " -sv" : ""; + return "read_verilog" + flags + " " + ansi_c_escape(filename); +} + const verilator_re = /^%(Warning|Error)[^:]*: ([^:]*):([0-9]+):([0-9]+): (.*)$/; export async function verilator_lint(filenames: string[], dirname?: string, options: Options = {}): Promise { @@ -1241,7 +1246,7 @@ export async function process(filenames: string[], dirname?: string, options: Op const tmpjson = await tmp.tmpName({ postfix: '.json' }); let obj = undefined; const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( - 'yosys -p "read_verilog ' + shell_escape_contents(filenames.map(ansi_c_escape).join(' ')) + + 'yosys -p "' + shell_escape_contents(filenames.map(process_filename).join('; ')) + '; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' + optimize + '" -o "' + tmpjson + '"', {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) From b850fbdca30284daa44527080cab596d118a3c69 Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 19 Jan 2024 13:52:46 +0100 Subject: [PATCH 37/38] Add `timeout` for better protection --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index eea71ae..5e548f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1204,7 +1204,7 @@ export async function verilator_lint(filenames: string[], dirname?: string, opti try { const output: LintMessage[] = []; const verilator_result: {stdout: string, stderr: string} = await promisify(child_process.exec)( - 'verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(shell_escape).join(' '), + 'timeout -k10s 40s verilator -lint-only -Wall -Wno-DECLFILENAME -Wno-UNOPT -Wno-UNOPTFLAT ' + filenames.map(shell_escape).join(' '), {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) .catch(exc => exc); for (const line of verilator_result.stderr.split('\n')) { @@ -1246,7 +1246,7 @@ export async function process(filenames: string[], dirname?: string, options: Op const tmpjson = await tmp.tmpName({ postfix: '.json' }); let obj = undefined; const yosys_result: {stdout: string, stderr: string, killed?: boolean, code?: number} = await promisify(child_process.exec)( - 'yosys -p "' + shell_escape_contents(filenames.map(process_filename).join('; ')) + + 'timeout -k10s 40s yosys -p "' + shell_escape_contents(filenames.map(process_filename).join('; ')) + '; hierarchy -auto-top; proc' + optimize_simp + fsmpass + '; memory -nomap; wreduce -memx' + optimize + '" -o "' + tmpjson + '"', {maxBuffer: 1000000, cwd: dirname || null, timeout: options.timeout || 60000}) From e5991f3e18a8f1e5d31c302097669cf89be1a1ed Mon Sep 17 00:00:00 2001 From: Marek Materzok Date: Fri, 19 Jan 2024 14:59:23 +0100 Subject: [PATCH 38/38] Bump version number --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9461362..c242b23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yosys2digitaljs", - "version": "0.7.0", + "version": "0.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yosys2digitaljs", - "version": "0.7.0", + "version": "0.8.0", "license": "BSD-2-Clause", "dependencies": { "3vl": "^1.0.1", diff --git a/package.json b/package.json index 59da246..b6d41e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yosys2digitaljs", - "version": "0.7.0", + "version": "0.8.0", "description": "Export Yosys netlists to a logic simulator", "main": "dist/index", "types": "dist/index.d.ts",