npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

silicon

v0.2.2

Published

Hardware description for software engineers.

Downloads

4

Readme

Silicon.js

Hardware description for software engineers.

Silicon is still a work in progress, if you find any bugs or want to request a feature, please file an issue on the github issues page, thanks!!

Silicon.js is a module used to describe and simulate logic circuits. It uses a simple object format to describe each unit (called a chip) that is built up from other, lower level chips.

Super Simple API.

With Silicon.js it's easy to describe a chip. Here's an example:

var Silicon = require('silicon');

var xor = {
  in: ['a', 'b'],
  out: 'out',
  arch: {
      out : { or: ['w1', 'w2'] },
      w1  : { and: ['a', 'notb'] },
      w2  : { and: ['nota', 'b'] },
      nota: { not: 'a' },
      notb: { not: 'b' }
    }
}

Silicon.add(xor);

This describes the xor chip, which takes two inputs and produces one output. The arch section describes the layout of the chip. There are three main sections in a chip object: in, out, and arch.

in

  • This section describes the names of the input signals. It may be a single string, such as 'input', or an array of strings that will make up the input bus.

out

  • This section describes the names of the output signals. It also may be a single string or an array of strings.

arch

  • This is the architecture object of the chip. Each property of the object either names an internal signal that may be used in any other part of the chip, or the refers to the name of an output signal defined in the out section. The value of this property is an object that may only have one property: the name of a chip which will provide the value of the signal. This may sound confusing, but it's really pretty simple. For example: nota: { not: 'a' } will take the value of a, pass it as the input to the (already defined) not chip, and assign the value of the result to the signal nota. This is then is referenced again as one of the inputs to the and chip that comprises the signal w2.
  • The architecture object may contain signal objects which are just shorthand for defining a signal inline. It is a slightly more compact way of expressing a chip's architecture, at the expense of possibly being less readable. Example:
Silicon.add({
        name: 'xor',
        in  : ['a', 'b'],
        out : 'out',
        arch: {
          out: {or: [{and: ['a', {not: 'b'}]}, {and: [{not: 'a'}, 'b']}]}
        }
      });

This is exactly the same architecture as before, only without the explicit signal names w1, nota, etc. You can see that out is comprised of an or chip that takes as its inputs an array consisting of the output of two and chips, and so on. This is merely for convenience instead of having to explicitly name each and every internal signal.

Calling Silicon.add(chip) will verify the validity of the chip and then add it to the internal registry of defined chips to be referenced in future architectures.

Once a chip is created, it can be simulated either by calling chip.simulate() or Silicon.simulate('name'). This will return a simulation function which will emulate the behavior of the chip. This function can then be called with specific arguments to return a result. For example:

var Silicon = require('silicon');

var xor = Silicon.add({
  name: 'xor',
  in: ['a', 'b'],
  out: 'out',
  arch: {
    nota: {not: 'a'},
    notb: {not: 'b'},
    w1  : {and: ['a', 'notb']},
    w2  : {and: ['nota', 'b']},
    out : {or: ['w1', 'w2']}
  }
}).simulate();

The not chip uses arithmetic negation (~) so we use -1 as true and 0 as false for these tests:

expect(xor(0, 0)).toEqual(0);
expect(xor(-1, 0)).toEqual(-1);
expect(xor(0, -1)).toEqual(-1);
expect(xor(-1, -1)).toEqual(0);

At this point, xor and Silicon.simulate('xor') are the same function.

Under the hood, Silicon uses a simulation engine which stores the value of its signals between usages. The gist of the algorithm is something like this:

  1. Locate all output and internal signals and assign them a random value.
  2. For each output signal, trace a route through the chips back to an input signal.
  3. Using the input signal's value, evaluate the route forward through the chips and determine what the output signal should be.
  4. If there are any circular dependencies, note what the value for that chip is and use this value to call the dependent chip again.
  5. If the new value is the same as the original value, the circular definition is stable, and we can use its output.
  6. If the new value is different, the original output value will be used.

This lets us be able to simulate something like a SR Latch:

var rs = Silicon.add({
        name: 'rsLatch',
        in  : ['r', 's'],
        out : ['q', 'notQ'],
        arch: {
          q   : {nor: ['r', 'notQ']},
          notQ: {nor: ['s', 'q']}
        }
      }).simulate();

expect(rs(0, 0)).toEqual({q: 0, notQ: -1});
expect(rs(0, -1)).toEqual({q: -1, notQ: 0});
expect(rs(0, 0)).toEqual({q: -1, notQ: 0});
expect(rs(-1, 0)).toEqual({q: 0, notQ: -1});
expect(rs(0, 0)).toEqual({q: 0, notQ: -1});

This is a perfectly valid chip definition and will not produce an infinite recursion. Trying to simulate this chip, however, will throw an Error because it will never reach a stable state:

circular = {
        name: 'circular',
        in  : 'in',
        out : 'out',
        arch: {
          out: {not: 'out'}
        }
      };

If at any point you want to clear the stored signal values of a chip, just call chip.reset() or Silicon.reset('name').

We can explicitly define the simulation function of a chip if we wish. Just create a property on the chip object of 'sim' and have it return whatever simulation function you wish. For example, the built in not chip is defined as:

Silicon.prototype.add({
  name: 'not',
  in  : 'a',
  out : 'out',
  sim : function (a) {
    return ~a;
  }
});