├── .gitignore ├── media └── banner.png ├── Utility ├── new.js └── ioManager.js ├── index.js ├── package.json ├── LICENSE ├── Combinational ├── decoders.js ├── arithmetics.js └── gates.js ├── Connectors └── transport.js ├── Sequential └── ff.js ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /media/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbad0la/Architect/HEAD/media/banner.png -------------------------------------------------------------------------------- /Utility/new.js: -------------------------------------------------------------------------------- 1 | class Hardware { 2 | 3 | constructor(io) { 4 | this.ioMapping = io 5 | this.internalWiring = [] 6 | this.components = [] 7 | } 8 | 9 | } 10 | 11 | module.exports = { Hardware } 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = function(path) { 2 | return { 3 | 'Gates': require('./Combinational/gates'), 4 | 'Arithmetics': require('./Combinational/arithmetics'), 5 | 'Connectors': require('./Connectors/transport'), 6 | 'Sequential': require('./Sequential/ff'), 7 | 'IO': require('./Utility/ioManager'), 8 | 'Base': require('./Utility/new') 9 | } [ path ] 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "architectjs", 3 | "version": "0.1.2", 4 | "description": "Hardware Description and Emulation Library", 5 | "keywords": [ 6 | "hardware", 7 | "design", 8 | "emulation", 9 | "hardware emulation", 10 | "vhdl", 11 | "architect", 12 | "architectjs", 13 | "library" 14 | ], 15 | "scripts": { 16 | "test": "ava" 17 | }, 18 | "repository": "mbad0la/Architect", 19 | "author": { 20 | "name": "Mayank Badola", 21 | "email": "badola21295@gmail.com", 22 | "url": "https://mayankbadola.me" 23 | }, 24 | "license": "MIT", 25 | "devDependencies": { 26 | "ava": "^0.25.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Utility/ioManager.js: -------------------------------------------------------------------------------- 1 | class StringIO { 2 | 3 | constructor({ioMapping}) { 4 | let inputGroup = ioMapping.length - 1 5 | this.i = ioMapping.slice(0, inputGroup) 6 | this.o = ioMapping[inputGroup] 7 | } 8 | 9 | input(...inputSeqs) { 10 | let inpIndex = inputSeqs[0].length - 1 11 | let totalInps = this.i.length 12 | let pos = 0 13 | while (inpIndex >= 0) { 14 | for (let inpNum = 0; inpNum < totalInps; ++inpNum) { 15 | this.i[inpNum][pos].propagateSignal(Number(inputSeqs[inpNum][inpIndex])) 16 | } 17 | ++pos 18 | --inpIndex 19 | } 20 | 21 | let outBuff = this.o.map((wire) => { 22 | return wire.getSignal() 23 | }) 24 | 25 | outBuff = outBuff.join('') 26 | 27 | return outBuff 28 | } 29 | 30 | } 31 | 32 | module.exports = { StringIO } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mayank Badola 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Combinational/decoders.js: -------------------------------------------------------------------------------- 1 | const { Hardware } = require('../Utility/new') 2 | const { wires } = require('../Connectors/transport') 3 | const { NotGate, AndGate } = require('./gates') 4 | 5 | class Decoder1x2 extends Hardware { 6 | 7 | constructor(x, o) { 8 | if (x.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 9 | super([x, [o[0], x[0]]]) 10 | this.components.push(new NotGate(x, o)) 11 | } 12 | 13 | } 14 | 15 | class Decoder2x4 extends Hardware { 16 | 17 | constructor(x0, x1, o) { 18 | if(x0.length != 1 || x1.length != 1 || o.length != 4) throw new Error('Invalid Connection/s') 19 | super([x0, x1, o]) 20 | this.internalWiring = wires(2) 21 | this.components.push(new Decoder1x2(x0, [this.internalWiring[0]])) 22 | this.components.push(new Decoder1x2(x1, [this.internalWiring[1]])) 23 | this.components.push(new AndGate([this.internalWiring[0]], [this.internalWiring[1]], [o[0]])) 24 | this.components.push(new AndGate([this.internalWiring[0]], x1, [o[1]])) 25 | this.components.push(new AndGate(x0, [this.internalWiring[1]], [o[2]])) 26 | this.components.push(new AndGate(x0, x1, [o[3]])) 27 | } 28 | 29 | } 30 | 31 | module.exports = { Decoder1x2, Decoder2x4 } 32 | -------------------------------------------------------------------------------- /Connectors/transport.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events') 2 | 3 | class Wire extends EventEmitter { 4 | 5 | constructor(sig) { 6 | super() 7 | 8 | this.signal = sig 9 | this.propagateSignal = this.propagateSignal.bind(this) 10 | this.getSignal = this.getSignal.bind(this) 11 | } 12 | 13 | propagateSignal(newSignal) { 14 | let oldSignal = this.signal 15 | 16 | this.signal = newSignal 17 | 18 | if (oldSignal != this.signal) this.emit('signal') 19 | } 20 | 21 | getSignal() { 22 | return this.signal 23 | } 24 | 25 | } 26 | 27 | class Pulse extends Wire { 28 | 29 | constructor(t, i) { 30 | super() 31 | 32 | this.i = i 33 | this.timePeriod = t 34 | this.alter = this.alter.bind(this) 35 | this.switchOn = this.switchOn.bind(this) 36 | this.switchOff = this.switchOff.bind(this) 37 | this.interval = undefined 38 | } 39 | 40 | alter() { 41 | this.propagateSignal(Number(!this.signal)) 42 | } 43 | 44 | switchOn() { 45 | if (!this.interval) { 46 | this.signal = this.i 47 | this.interval = setInterval(this.alter, this.timePeriod) 48 | } 49 | } 50 | 51 | switchOff() { 52 | if (this.interval) { 53 | clearInterval(this.interval) 54 | this.signal = undefined 55 | this.interval = undefined 56 | } 57 | } 58 | 59 | } 60 | 61 | function wires(n) { 62 | let wireSet = [] 63 | for(let i = 0; i < n ; i++) { 64 | wireSet.push(new Wire()) 65 | } 66 | return wireSet 67 | } 68 | 69 | module.exports = { Pulse, wires } 70 | -------------------------------------------------------------------------------- /Sequential/ff.js: -------------------------------------------------------------------------------- 1 | const { AndGate, NorGate, NandGate, NotGate } = require('../Combinational/gates') 2 | const { wires } = require('../Connectors/transport') 3 | const { Hardware } = require('../Utility/new') 4 | 5 | class SRFlipFlop extends Hardware { 6 | 7 | constructor(s, r, qqbar, c) { 8 | if (s.length != 1 || r.length != 1 || qqbar.length != 2) throw new Error('Invalid Connection/s') 9 | super([s, r, [qqbar[0]]]) 10 | this.internalWiring = wires(2) 11 | this.components.push(new AndGate([c], s, [this.internalWiring[0]])) 12 | this.components.push(new AndGate([c], r, [this.internalWiring[1]])) 13 | this.components.push(new NorGate([this.internalWiring[0]], [qqbar[0]], [qqbar[1]])) 14 | this.components.push(new NorGate([this.internalWiring[1]], [qqbar[1]], [qqbar[0]])) 15 | } 16 | 17 | } 18 | 19 | class DFlipFlop extends Hardware { 20 | 21 | constructor(d, qqbar, c) { 22 | if (d.length != 1 || qqbar.length != 2) throw new Error('Invalid Connection/s') 23 | super([d, [qqbar[0]]]) 24 | this.internalWiring = wires(3) 25 | this.components.push(new NotGate(d, [this.internalWiring[0]])) 26 | this.components.push(new NandGate([c], d, [this.internalWiring[1]])) 27 | this.components.push(new NandGate([c], [this.internalWiring[0]], [this.internalWiring[2]])) 28 | this.components.push(new NandGate([this.internalWiring[1]], [qqbar[1]], [qqbar[0]])) 29 | this.components.push(new NandGate([this.internalWiring[2]], [qqbar[0]], [qqbar[1]])) 30 | } 31 | 32 | } 33 | 34 | module.exports = { SRFlipFlop, DFlipFlop } 35 | -------------------------------------------------------------------------------- /Combinational/arithmetics.js: -------------------------------------------------------------------------------- 1 | const { AndGate, XorGate, OrGate } = require('./gates') 2 | const { wires } = require('../Connectors/transport') 3 | const { Hardware } = require('../Utility/new') 4 | 5 | class HalfAdder extends Hardware { 6 | 7 | constructor(x, s) { 8 | if (x.length != 2 || s.length != 2) throw new Error('Invalid Connection/s') 9 | super([x, s]) 10 | this.components.push(new XorGate([x[0]], [x[1]], [s[1]])) 11 | this.components.push(new AndGate([x[0]], [x[1]], [s[0]])) 12 | } 13 | 14 | } 15 | 16 | class FullAdder extends Hardware { 17 | 18 | constructor(x, s) { 19 | if (x.length != 3 || s.length != 2) throw new Error('Invalid Connection/s') 20 | super([x, s]) 21 | this.internalWiring = wires(3) 22 | this.components.push(new HalfAdder([x[0], x[1]], [this.internalWiring[0], this.internalWiring[1]])) 23 | this.components.push(new HalfAdder([this.internalWiring[1], x[2]], [this.internalWiring[2], s[1]])) 24 | this.components.push(new OrGate([this.internalWiring[0]], [this.internalWiring[2]], [s[0]])) 25 | } 26 | 27 | } 28 | 29 | class PipoAdder extends Hardware { 30 | 31 | constructor(a, b, s) { 32 | if (a.length != b.length || s.length != a.length + 1) throw new Error('Invalid Connection/s') 33 | super([a, b, s]) 34 | let size = a.length 35 | this.internalWiring = wires(size) 36 | if (size > 1) { 37 | this.components.push(new FullAdder([a[0], b[0], this.internalWiring[0]], [this.internalWiring[1], s[size]])) 38 | for(let i = 1; i < size - 1; i++) { 39 | this.components.push(new FullAdder([a[i], b[i], this.internalWiring[i]], [this.internalWiring[i+1], s[size-i]])) 40 | } 41 | this.components.push(new FullAdder([a[size-1], b[size-1], this.internalWiring[size-1]], [s[0], s[1]])) 42 | } else { 43 | this.components.push(new FullAdder([a[0], b[0], this.internalWiring[0]], [s[0], s[1]])) 44 | } 45 | this.internalWiring[0].propagateSignal(0) 46 | } 47 | 48 | } 49 | 50 | module.exports = { HalfAdder, FullAdder, PipoAdder } 51 | -------------------------------------------------------------------------------- /Combinational/gates.js: -------------------------------------------------------------------------------- 1 | const { Hardware } = require('../Utility/new') 2 | 3 | class AndGate extends Hardware { 4 | 5 | constructor(x, y, o) { 6 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 7 | super([x, y, o]) 8 | this.x = x 9 | this.y = y 10 | this.o = o 11 | this.hardware = this.hardware.bind(this) 12 | x[0].on('signal', this.hardware) 13 | y[0].on('signal', this.hardware) 14 | } 15 | 16 | hardware() { 17 | let xSig = this.x[0].getSignal() 18 | let ySig = this.y[0].getSignal() 19 | if (xSig === 0 || ySig === 0) { 20 | this.o[0].propagateSignal(0) 21 | } else if (xSig === undefined || ySig === undefined) { 22 | this.o[0].propagateSignal(undefined) 23 | } else this.o[0].propagateSignal(xSig && ySig) 24 | } 25 | 26 | } 27 | 28 | class TriInpAndGate extends Hardware { 29 | 30 | constructor(x, y, z, o) { 31 | if (x.length != 1 || y.length != 1 || z.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 32 | super([x, y, z, o]) 33 | this.x = x 34 | this.y = y 35 | this.z = z 36 | this.o = o 37 | this.hardware = this.hardware.bind(this) 38 | x[0].on('signal', this.hardware) 39 | y[0].on('signal', this.hardware) 40 | z[0].on('signal', this.hardware) 41 | } 42 | 43 | hardware() { 44 | let xSig = this.x[0].getSignal() 45 | let ySig = this.y[0].getSignal() 46 | let zSig = this.z[0].getSignal() 47 | if (xSig === 0 || ySig === 0 || zSig === 0) { 48 | this.o[0].propagateSignal(0) 49 | } else if (xSig === undefined || ySig === undefined || zSig === undefined) { 50 | this.o[0].propagateSignal(undefined) 51 | } else this.o[0].propagateSignal(xSig && ySig && zSig) 52 | } 53 | 54 | } 55 | 56 | class OrGate extends Hardware { 57 | 58 | constructor(x, y, o) { 59 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 60 | super([x, y, o]) 61 | this.x = x 62 | this.y = y 63 | this.o = o 64 | this.hardware = this.hardware.bind(this) 65 | x[0].on('signal', this.hardware) 66 | y[0].on('signal', this.hardware) 67 | } 68 | 69 | hardware() { 70 | let xSig = this.x[0].getSignal() 71 | let ySig = this.y[0].getSignal() 72 | if (xSig === 1 || ySig === 1) { 73 | this.o[0].propagateSignal(1) 74 | } else if (xSig === undefined || ySig === undefined) { 75 | this.o[0].propagateSignal(undefined) 76 | } else this.o[0].propagateSignal(xSig || ySig) 77 | } 78 | 79 | } 80 | 81 | class XorGate extends Hardware { 82 | 83 | constructor(x, y, o) { 84 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 85 | super([x, y, o]) 86 | this.x = x 87 | this.y = y 88 | this.o = o 89 | this.hardware = this.hardware.bind(this) 90 | x[0].on('signal', this.hardware) 91 | y[0].on('signal', this.hardware) 92 | } 93 | 94 | hardware() { 95 | let xSig = this.x[0].getSignal() 96 | let ySig = this.y[0].getSignal() 97 | if (xSig === undefined || ySig === undefined) { 98 | this.o[0].propagateSignal(undefined) 99 | } else this.o[0].propagateSignal(Number(xSig != ySig)) 100 | } 101 | 102 | } 103 | 104 | class NotGate extends Hardware { 105 | 106 | constructor(x, o) { 107 | if (x.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 108 | super([x, o]) 109 | this.x = x 110 | this.o = o 111 | this.hardware = this.hardware.bind(this) 112 | x[0].on('signal', this.hardware) 113 | } 114 | 115 | hardware() { 116 | let xSig = this.x[0].getSignal() 117 | if (xSig === undefined) { 118 | this.o[0].propagateSignal(undefined) 119 | } else this.o[0].propagateSignal(Number(!xSig)) 120 | } 121 | 122 | } 123 | 124 | class NandGate extends Hardware { 125 | 126 | constructor(x, y, o) { 127 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 128 | super([x, y, o]) 129 | this.x = x 130 | this.y = y 131 | this.o = o 132 | this.hardware = this.hardware.bind(this) 133 | x[0].on('signal', this.hardware) 134 | y[0].on('signal', this.hardware) 135 | } 136 | 137 | hardware() { 138 | let xSig = this.x[0].getSignal() 139 | let ySig = this.y[0].getSignal() 140 | if (xSig === 0 || ySig === 0) { 141 | this.o[0].propagateSignal(1) 142 | } else if (xSig === undefined || ySig === undefined) { 143 | this.o[0].propagateSignal(undefined) 144 | } else this.o[0].propagateSignal(Number(!(xSig && ySig))) 145 | } 146 | 147 | } 148 | 149 | class NorGate extends Hardware { 150 | 151 | constructor(x, y, o) { 152 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 153 | super([x, y, o]) 154 | this.x = x 155 | this.y = y 156 | this.o = o 157 | this.hardware = this.hardware.bind(this) 158 | x[0].on('signal', this.hardware) 159 | y[0].on('signal', this.hardware) 160 | } 161 | 162 | hardware() { 163 | let xSig = this.x[0].getSignal() 164 | let ySig = this.y[0].getSignal() 165 | if (xSig === 1 || ySig === 1) { 166 | this.o[0].propagateSignal(0) 167 | } else if (xSig === undefined || ySig === undefined) { 168 | this.o[0].propagateSignal(undefined) 169 | } else this.o[0].propagateSignal(Number(!(xSig || ySig))) 170 | } 171 | 172 | } 173 | 174 | class XnorGate extends Hardware { 175 | 176 | constructor(x, y, o) { 177 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 178 | super([x, y, o]) 179 | this.x = x 180 | this.y = y 181 | this.o = o 182 | this.hardware = this.hardware.bind(this) 183 | x[0].on('signal', this.hardware) 184 | y[0].on('signal', this.hardware) 185 | } 186 | 187 | hardware() { 188 | let xSig = this.x[0].getSignal() 189 | let ySig = this.y[0].getSignal() 190 | if (xSig === undefined || ySig === undefined) { 191 | this.o[0].propagateSignal(undefined) 192 | } else this.o[0].propagateSignal(Number(xSig == ySig)) 193 | } 194 | 195 | } 196 | 197 | module.exports = { AndGate, TriInpAndGate, OrGate, XorGate, NotGate, NandGate, NorGate, XnorGate } 198 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Architect](media/banner.png) 2 | 3 | > Highly Extensible Hardware Description Library for JavaScript Developers 4 | 5 | Hardware is event-driven. And it is functional in nature, having abstractions on abstractions. 6 | 7 | Makes sense to have JavaScript emulate it doesn't it? 8 | 9 | ### Motivation 10 | 11 | Hardware Description can be fun and very educational, but I had to learn [VHDL](https://en.wikipedia.org/wiki/VHDL) to be able to do so. 12 | 13 | Hence, being a JavaScript Enthusiast, I decided to write this library down for JavaScript Developers who wanted to get into Hardware Description but were reluctant to learn a new language for it. 14 | 15 | For people not acquainted with both VHDL and JS, I'm pretty sure the learning curve would be lesser for this library. Although I cannot state that my library is better as it is not easy to compete with a language intended for hardware description, nevertheless, I will keep on hacking this library to see how this experiment goes. 16 | 17 | Let's get to business! 18 | 19 | ### Installation 20 | 21 | `npm install architectjs` 22 | 23 | ### Existing Hardware Abstractions 24 | 25 | * Gates 26 | * AndGate 27 | * TriInpAndGate 28 | * OrGate 29 | * XorGate 30 | * NotGate 31 | * NandGate 32 | * NorGate 33 | * XnorGate 34 | * Decoders 35 | * Decoder1x2 36 | * Decoder2x4 37 | * Arithmetics 38 | * HalfAdder 39 | * FullAdder 40 | * PipoAdder 41 | * Flip-Flops 42 | * SRFlipFlop 43 | * DFlipFlop 44 | 45 | ### :electric_plug: Plug-n-Play 46 | 47 | Use existing abstractions seemlessly. 48 | 49 | Let's plug in an AND-Gate 50 | 51 | ```js 52 | const { wires } = require('architectjs')('Connectors') 53 | const { AndGate } = require('architectjs')('Gates') 54 | const { StringIO } = require('architectjs')('IO') 55 | 56 | // provision wires to connect to your hardware 57 | const inputA = wires(1) 58 | const inputB = wires(1) 59 | const output = wires(1) 60 | // initialise the hardware 61 | const hWare = new AndGate(inputA, inputB, output) 62 | // wrap hardware in a I/O BlackBox 63 | // this is compulsory, to be able to do I/O using strings 64 | const ioHandler = new StringIO(hWare) 65 | 66 | console.log(ioHandler.input('1', '1')) // prints 1 67 | 68 | console.log(ioHandler.input('0', '0')) // prints 0 69 | ``` 70 | 71 | Say what? `AND` is way too easy to be called an abstraction? 72 | 73 | No worries, let's plug in this generalised Parallel-in-Parallel-out Adder! 74 | 75 | ```js 76 | const { wires } = require('architectjs')('Connectors') 77 | const { PipoAdder } = require('architectjs')('Arithmetics') 78 | const { StringIO } = require('architectjs')('IO') 79 | 80 | // code for 4-bit adder 81 | const inputA = wires(4) 82 | const inputB = wires(4) 83 | const sum = wires(5) 84 | const hWare = new PipoAdder(inputA, inputB, sum) 85 | const ioHandler = new StringIO(hWare) 86 | console.log(ioHandler.input('1111', '1111')) // prints 11110 87 | ``` 88 | 89 | Or maybe we want to build something from existing abstractions? 90 | 91 | #### Abstraction Rules and Specs 92 | 93 | * Every Class/hardware extends on `Hardware`. 94 | * Every initialisation argument to the class instance has to be an array of `Wire` instances (obtained from the `wires` method). 95 | * An array consisting of I/O `wires` is passed onto the parent class `Hardware`, with only the last element being the output parameter. It is necessary to provide every input parameter and the output parameter to be able to wrap this in a `StringIO` instance to do I/O operations with `string` arguments. 96 | * Every class instance has two instance variables available from the parent `Hardware` instance : 97 | * internalWiring - Array of `Wire` instances (initially empty). 98 | * components - Array of abstractions used to build your hardware (initially empty). 99 | * Your entire logic goes into your Class' constructor. 100 | * `internalWiring` variable is used to initialise `Wire` instances that are not a part of the I/O for the hardware but are required to inter-connect the sub-components in your abstraction. 101 | * `components` variable is used to store instances of subcomponents used in your hardware. This helps a designer to quickly refer to all the build blocks that went into making a particular piece of hardware. 102 | 103 | 104 | Let's build a 4-input AND Gate using the above rules and specifications. 105 | 106 | ```js 107 | const { wires } = require('architectjs')('Connectors') 108 | const { AndGate } = require('architectjs')('Gates') 109 | const { StringIO } = require('architectjs')('IO') 110 | const { Hardware } = require('architectjs')('Base') 111 | 112 | class FourInpAndGate extends Hardware { 113 | 114 | constructor(a, b, c, d, o) { 115 | super([a, b, c, d, o]) 116 | this.internalWiring = wires(2) // declare wires to be used internally 117 | this.components.push(new AndGate(a, b, this.internalWiring[0])) 118 | this.components.push(new AndGate(c, d, this.internalWiring[1])) 119 | this.components.push(new AndGate(this.internalWiring[0], this.internalWiring[1], o)) 120 | } 121 | 122 | } 123 | 124 | const a = wires(1) 125 | const b = wires(1) 126 | const c = wires(1) 127 | const d = wires(1) 128 | const o = wires(1) 129 | 130 | const fourInpAnd = new FourInpAndGate(a, b, c, d, o) 131 | const ioHandler = new StringIO(fourInpAnd) 132 | 133 | console.log(ioHandler('0', '1', '1', '1')) // prints 0 134 | 135 | console.log(ioHandler('1', '1', '1', '1')) // prints 1 136 | ``` 137 | 138 | ### Creating a Declarative Hardware Component 139 | 140 | #### Some Basic Rules 141 | 142 | * Every Class/hardware extends on `Hardware`. 143 | * All the logic goes inside the `hardware` method of your component's Class. 144 | * Event to be listened for must be `signal`. 145 | 146 | #### Let's get started 147 | 148 | Every `Wire` instance extends on `EventEmitter`, thus this library essentially works by registering listeners in a Class instance and binding them to the `hardware` method of the Class. 149 | 150 | With the help of `getSignal` and `propagateSignal` methods of `Wire`, read changes from input `Wire` instances, use your logic on them, and emit result through the output `Wire` instance. 151 | 152 | Let's set this up with an example taken from this library 153 | 154 | ```js 155 | const { Hardware } = require('architectjs')('Base') 156 | 157 | class AndGate extends Hardware { 158 | 159 | constructor(x, y, o) { 160 | if (x.length != 1 || y.length != 1 || o.length != 1) throw new Error('Invalid Connection/s') 161 | super([x, y, o]) 162 | this.x = x 163 | this.y = y 164 | this.o = o 165 | this.hardware = this.hardware.bind(this) 166 | x[0].on('signal', this.hardware) 167 | y[0].on('signal', this.hardware) 168 | } 169 | 170 | hardware() { 171 | let xSig = this.x[0].getSignal() 172 | let ySig = this.y[0].getSignal() 173 | if (xSig === 0 || ySig === 0) { 174 | this.o[0].propagateSignal(0) 175 | } else if (xSig === undefined || ySig === undefined) { 176 | this.o[0].propagateSignal(undefined) 177 | } else this.o[0].propagateSignal(xSig && ySig) 178 | } 179 | 180 | } 181 | ``` 182 | ### Development 183 | 184 | New Hardware Component Proposals should be put up as an issue to discuss it's vialibility and modelling. I won't be considering anything else other than component proposals at the moment. 185 | 186 | I am also facing some problems in figuring out how to implement clock-edge driven circuits and circuits that have a feedback to them. Most of time, infinte events are triggered due to the feedbacking in the circuits. 187 | 188 | There are just so many possibilities to do here! Would love to get contributions from the community :smile: 189 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import { wires, Pulse } from './Connectors/transport' 3 | import { NotGate, AndGate, TriInpAndGate, XorGate } from './Combinational/gates' 4 | import { PipoAdder, HalfAdder, FullAdder } from './Combinational/arithmetics' 5 | import { SRFlipFlop } from './Sequential/ff' 6 | import { StringIO } from './Utility/ioManager' 7 | import { Decoder1x2, Decoder2x4 } from './Combinational/decoders' 8 | 9 | 10 | test('Not-Gate : 1', t => { 11 | const inputA = wires(1) 12 | const output = wires(1) 13 | const hWare = new NotGate(inputA, output) 14 | const ioHandler = new StringIO(hWare) 15 | t.is(ioHandler.input('1'), '0') 16 | }) 17 | 18 | test('Not-Gate : 2', t => { 19 | const inputA = wires(1) 20 | const output = wires(1) 21 | const hWare = new NotGate(inputA, output) 22 | const ioHandler = new StringIO(hWare) 23 | t.is(ioHandler.input('0'), '1') 24 | }) 25 | 26 | test('And-Gate : 1', t => { 27 | const inputA = wires(1) 28 | const inputB = wires(1) 29 | const output = wires(1) 30 | const hWare = new AndGate(inputA, inputB, output) 31 | const ioHandler = new StringIO(hWare) 32 | t.is(ioHandler.input('0', '0'), '0') 33 | }) 34 | 35 | test('And-Gate : 2', t => { 36 | const inputA = wires(1) 37 | const inputB = wires(1) 38 | const output = wires(1) 39 | const hWare = new AndGate(inputA, inputB, output) 40 | const ioHandler = new StringIO(hWare) 41 | t.is(ioHandler.input('0', '1'), '0') 42 | }) 43 | 44 | test('And-Gate : 3', t => { 45 | const inputA = wires(1) 46 | const inputB = wires(1) 47 | const output = wires(1) 48 | const hWare = new AndGate(inputA, inputB, output) 49 | const ioHandler = new StringIO(hWare) 50 | t.is(ioHandler.input('1', '1'), '1') 51 | }) 52 | 53 | test('Tri-Input And-Gate', t => { 54 | const inputX = wires(1) 55 | const inputY = wires(1) 56 | const inputZ = wires(1) 57 | const output = wires(1) 58 | const hWare = new TriInpAndGate(inputX, inputY, inputZ, output) 59 | const ioHandler = new StringIO(hWare) 60 | t.is(ioHandler.input('1', '1', '1'), '1') 61 | }) 62 | 63 | test('Xor-Gate : 1', t => { 64 | const inputA = wires(1) 65 | const inputB = wires(1) 66 | const output = wires(1) 67 | const hWare = new XorGate(inputA, inputB, output) 68 | const ioHandler = new StringIO(hWare) 69 | t.is(ioHandler.input('1', '1'), '0') 70 | }) 71 | 72 | test('Xor-Gate : 2', t => { 73 | const inputA = wires(1) 74 | const inputB = wires(1) 75 | const output = wires(1) 76 | const hWare = new XorGate(inputA, inputB, output) 77 | const ioHandler = new StringIO(hWare) 78 | t.is(ioHandler.input('0', '1'), '1') 79 | }) 80 | 81 | test('Overflow for HalfAdder', t => { 82 | const inputA = wires(2) 83 | const sum = wires(2) 84 | const hWare = new HalfAdder(inputA, sum) 85 | const ioHandler = new StringIO(hWare) 86 | t.is(ioHandler.input('11'), '10') 87 | }) 88 | 89 | test('Overflow for FullAdder', t => { 90 | const inputA = wires(3) 91 | const sum = wires(2) 92 | const hWare = new FullAdder(inputA, sum) 93 | const ioHandler = new StringIO(hWare) 94 | t.is(ioHandler.input('111'), '11') 95 | }) 96 | 97 | test('Overflow for Parallel Adder (1 bit)', t => { 98 | const inputA = wires(1) 99 | const inputB = wires(1) 100 | const sum = wires(2) 101 | const hWare = new PipoAdder(inputA, inputB, sum) 102 | const ioHandler = new StringIO(hWare) 103 | t.is(ioHandler.input('1', '1'), '10') 104 | }) 105 | 106 | test('Overflow for Parallel Adder (2 bit)', t => { 107 | const inputA = wires(2) 108 | const inputB = wires(2) 109 | const sum = wires(3) 110 | const hWare = new PipoAdder(inputA, inputB, sum) 111 | const ioHandler = new StringIO(hWare) 112 | t.is(ioHandler.input('11', '11'), '110') 113 | }) 114 | 115 | test('Overflow for Parallel Adder (4 bit)', t => { 116 | const inputA = wires(4) 117 | const inputB = wires(4) 118 | const sum = wires(5) 119 | const hWare = new PipoAdder(inputA, inputB, sum) 120 | const ioHandler = new StringIO(hWare) 121 | t.is(ioHandler.input('1111', '1111'), '11110') 122 | }) 123 | 124 | test('HalfAdder - 1 bit PIPO Equivalence : 1', t => { 125 | const inputA = wires(1) 126 | const inputB = wires(1) 127 | const sum1 = wires(2) 128 | const halfAdderInput = wires(2) 129 | const sum2 = wires(2) 130 | const hWare1 = new PipoAdder(inputA, inputB, sum1) 131 | const hWare2 = new HalfAdder(halfAdderInput, sum2) 132 | const ioHandler1 = new StringIO(hWare1) 133 | const ioHandler2 = new StringIO(hWare2) 134 | t.is(ioHandler1.input('1', '1'), ioHandler2.input('11')) 135 | }) 136 | 137 | test('HalfAdder - 1 bit PIPO Equivalence : 2', t => { 138 | const inputA = wires(1) 139 | const inputB = wires(1) 140 | const sum1 = wires(2) 141 | const halfAdderInput = wires(2) 142 | const sum2 = wires(2) 143 | const hWare1 = new PipoAdder(inputA, inputB, sum1) 144 | const hWare2 = new HalfAdder(halfAdderInput, sum2) 145 | const ioHandler1 = new StringIO(hWare1) 146 | const ioHandler2 = new StringIO(hWare2) 147 | t.is(ioHandler1.input('1', '0'), ioHandler2.input('10')) 148 | }) 149 | 150 | test('HalfAdder - 1 bit PIPO Equivalence : 3', t => { 151 | const inputA = wires(1) 152 | const inputB = wires(1) 153 | const sum1 = wires(2) 154 | const halfAdderInput = wires(2) 155 | const sum2 = wires(2) 156 | const hWare1 = new PipoAdder(inputA, inputB, sum1) 157 | const hWare2 = new HalfAdder(halfAdderInput, sum2) 158 | const ioHandler1 = new StringIO(hWare1) 159 | const ioHandler2 = new StringIO(hWare2) 160 | t.is(ioHandler1.input('0', '0'), ioHandler2.input('00')) 161 | }) 162 | 163 | test('SR-Flip-Flop : Set', t => { 164 | const s = wires(1) 165 | const r = wires(1) 166 | const qqbar = wires(2) 167 | const clock = new Pulse(500, 1) 168 | clock.switchOn() 169 | const ff = new SRFlipFlop(s, r, qqbar, clock) 170 | const ioHandler = new StringIO(ff) 171 | t.is(ioHandler.input('1', '0'), '1') 172 | clock.switchOff() 173 | }) 174 | 175 | test('SR-Flip-Flop : Reset', t => { 176 | const s = wires(1) 177 | const r = wires(1) 178 | const qqbar = wires(2) 179 | const clock = new Pulse(500, 1) 180 | clock.switchOn() 181 | const ff = new SRFlipFlop(s, r, qqbar, clock) 182 | const ioHandler = new StringIO(ff) 183 | t.is(ioHandler.input('0', '1'), '0') 184 | clock.switchOff() 185 | }) 186 | 187 | test('SR-Flip-Flop : No Change', t => { 188 | const s = wires(1) 189 | const r = wires(1) 190 | const qqbar = wires(2) 191 | const clock = new Pulse(500, 1) 192 | clock.switchOn() 193 | const ff = new SRFlipFlop(s, r, qqbar, clock) 194 | const ioHandler = new StringIO(ff) 195 | const prevQ = ioHandler.input('0', '1') 196 | t.is(ioHandler.input('0', '0'), prevQ) 197 | clock.switchOff() 198 | }) 199 | 200 | test('1x2 Decoder : 1', t => { 201 | const inputA = wires(1) 202 | const outputA = wires(1) 203 | const linerDecoder = new Decoder1x2(inputA, outputA) 204 | const ioHandler = new StringIO(linerDecoder) 205 | t.is(ioHandler.input('0'), '10') 206 | }) 207 | 208 | test('1x2 Decoder : 2', t => { 209 | const inputA = wires(1) 210 | const outputA = wires(1) 211 | const linerDecoder = new Decoder1x2(inputA, outputA) 212 | const ioHandler = new StringIO(linerDecoder) 213 | t.is(ioHandler.input('1'), '01') 214 | }) 215 | 216 | test('2x4 Decoder : 1', t => { 217 | const inputX = wires(1) 218 | const inputY = wires(1) 219 | const output = wires(4) 220 | const d2x4 = new Decoder2x4(inputX, inputY, output) 221 | const ioHandler = new StringIO(d2x4) 222 | t.is(ioHandler.input('0', '0'), '1000') 223 | }) 224 | 225 | test('2x4 Decoder : 2', t => { 226 | const inputX = wires(1) 227 | const inputY = wires(1) 228 | const output = wires(4) 229 | const d2x4 = new Decoder2x4(inputX, inputY, output) 230 | const ioHandler = new StringIO(d2x4) 231 | t.is(ioHandler.input('1' ,'0'), '0010') 232 | }) 233 | 234 | test('2x4 Decoder : 3', t => { 235 | const inputX = wires(1) 236 | const inputY = wires(1) 237 | const output = wires(4) 238 | const d2x4 = new Decoder2x4(inputX, inputY, output) 239 | const ioHandler = new StringIO(d2x4) 240 | t.is(ioHandler.input('1', '1'), '0001') 241 | }) 242 | --------------------------------------------------------------------------------