911 lines
24 KiB
TypeScript
911 lines
24 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js";
|
|
import { bin } from "../util/twos.js";
|
|
import { And, Inc16, Mux16, Not, Not16, Or, Xor } from "./builtins/index.js";
|
|
import { Nand } from "./builtins/logic/nand.js";
|
|
import { Bit, PC, Register } from "./builtins/sequential/bit.js";
|
|
import { DFF } from "./builtins/sequential/dff.js";
|
|
import {
|
|
Bus,
|
|
Chip,
|
|
ConstantBus,
|
|
HIGH,
|
|
InSubBus,
|
|
LOW,
|
|
OutSubBus,
|
|
parseToPin,
|
|
printChip,
|
|
TRUE_BUS,
|
|
} from "./chip.js";
|
|
import { Clock } from "./clock.js";
|
|
|
|
describe("Chip", () => {
|
|
it("parses toPin", () => {
|
|
expect(parseToPin("a")).toMatchObject({ pin: "a" });
|
|
expect(parseToPin("a[2]")).toMatchObject({ pin: "a", start: 2 });
|
|
expect(parseToPin("a[2..4]")).toMatchObject({
|
|
pin: "a",
|
|
start: 2,
|
|
end: 4,
|
|
});
|
|
});
|
|
|
|
describe("combinatorial", () => {
|
|
describe("nand", () => {
|
|
it("can eval a nand gate", () => {
|
|
const nand = new Nand();
|
|
nand.eval();
|
|
expect(nand.out().voltage()).toBe(HIGH);
|
|
|
|
nand.in("a")?.pull(HIGH);
|
|
nand.eval();
|
|
expect(nand.out().voltage()).toBe(HIGH);
|
|
|
|
nand.in("b")?.pull(HIGH);
|
|
nand.eval();
|
|
expect(nand.out().voltage()).toBe(LOW);
|
|
|
|
nand.in("a")?.pull(LOW);
|
|
nand.eval();
|
|
expect(nand.out().voltage()).toBe(HIGH);
|
|
});
|
|
});
|
|
|
|
describe("not", () => {
|
|
it("evaluates a not gate", () => {
|
|
const notChip = new Not();
|
|
|
|
notChip.eval();
|
|
expect(notChip.out().voltage()).toBe(HIGH);
|
|
|
|
notChip.in().pull(HIGH);
|
|
notChip.eval();
|
|
expect(notChip.out().voltage()).toBe(LOW);
|
|
});
|
|
});
|
|
|
|
describe("and", () => {
|
|
it("evaluates an and gate", () => {
|
|
const andChip = new And();
|
|
|
|
const a = assertExists(andChip.in("a"));
|
|
const b = assertExists(andChip.in("b"));
|
|
|
|
andChip.eval();
|
|
expect(andChip.out().voltage()).toBe(LOW);
|
|
|
|
a.pull(HIGH);
|
|
andChip.eval();
|
|
expect(andChip.out().voltage()).toBe(LOW);
|
|
|
|
b.pull(HIGH);
|
|
andChip.eval();
|
|
expect(andChip.out().voltage()).toBe(HIGH);
|
|
|
|
a.pull(LOW);
|
|
andChip.eval();
|
|
expect(andChip.out().voltage()).toBe(LOW);
|
|
});
|
|
});
|
|
|
|
describe("or", () => {
|
|
it("evaluates an or gate", () => {
|
|
const orChip = new Or();
|
|
|
|
const a = assertExists(orChip.in("a"));
|
|
const b = assertExists(orChip.in("b"));
|
|
|
|
orChip.eval();
|
|
expect(orChip.out().voltage()).toBe(LOW);
|
|
|
|
a.pull(HIGH);
|
|
orChip.eval();
|
|
printChip(orChip);
|
|
expect(orChip.out().voltage()).toBe(HIGH);
|
|
|
|
b.pull(HIGH);
|
|
orChip.eval();
|
|
expect(orChip.out().voltage()).toBe(HIGH);
|
|
|
|
a.pull(LOW);
|
|
orChip.eval();
|
|
expect(orChip.out().voltage()).toBe(HIGH);
|
|
});
|
|
});
|
|
|
|
describe("xor", () => {
|
|
it("evaluates an xor gate", () => {
|
|
const xorChip = new Xor();
|
|
|
|
const a = assertExists(xorChip.in("a"));
|
|
const b = assertExists(xorChip.in("b"));
|
|
|
|
xorChip.eval();
|
|
expect(xorChip.out().voltage()).toBe(LOW);
|
|
|
|
a.pull(HIGH);
|
|
xorChip.eval();
|
|
expect(xorChip.out().voltage()).toBe(HIGH);
|
|
|
|
b.pull(HIGH);
|
|
xorChip.eval();
|
|
expect(xorChip.out().voltage()).toBe(LOW);
|
|
|
|
a.pull(LOW);
|
|
xorChip.eval();
|
|
expect(xorChip.out().voltage()).toBe(HIGH);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("wide", () => {
|
|
describe("Not16", () => {
|
|
it("evaluates a not16 gate", () => {
|
|
const not16 = new Not16();
|
|
|
|
const inn = not16.in();
|
|
|
|
inn.busVoltage = 0x0;
|
|
not16.eval();
|
|
expect(not16.out().busVoltage).toBe(0xffff);
|
|
|
|
inn.busVoltage = 0xf00f;
|
|
not16.eval();
|
|
expect(not16.out().busVoltage).toBe(0x0ff0);
|
|
});
|
|
});
|
|
|
|
describe("bus voltage", () => {
|
|
it("sets and returns wide busses", () => {
|
|
const pin = new Bus("wide", 16);
|
|
pin.busVoltage = 0xf00f;
|
|
expect(pin.voltage(0)).toBe(1);
|
|
expect(pin.voltage(8)).toBe(0);
|
|
expect(pin.voltage(9)).toBe(0);
|
|
expect(pin.voltage(15)).toBe(1);
|
|
expect(pin.busVoltage).toBe(0xf00f);
|
|
});
|
|
|
|
it("creates wide busses internally", () => {
|
|
const chip = new Chip([], [], "WithWide");
|
|
|
|
chip.wire(new Not16(), [
|
|
{
|
|
to: { name: "out", start: 0, width: 16 },
|
|
from: { name: "a", start: 0, width: 16 },
|
|
},
|
|
]);
|
|
|
|
const width = chip.pins.get("a")?.width;
|
|
expect(width).toBe(16);
|
|
});
|
|
});
|
|
|
|
describe("and16", () => undefined);
|
|
});
|
|
|
|
describe("SubBus", () => {
|
|
class Not3 extends Chip {
|
|
constructor() {
|
|
super(["in[3]"], ["out[3]"]);
|
|
}
|
|
|
|
override eval() {
|
|
const inn = this.in().busVoltage;
|
|
const out = ~inn & 0b111;
|
|
this.out().busVoltage = out;
|
|
}
|
|
}
|
|
|
|
it("drives OutSubBus", () => {
|
|
const notChip = new Not();
|
|
const inPin = new Bus("in", 3);
|
|
const outSubBus = new OutSubBus(notChip.in(), 1, 1);
|
|
inPin.connect(outSubBus);
|
|
|
|
inPin.busVoltage = 0b0;
|
|
expect(notChip.in().busVoltage).toBe(0b0);
|
|
inPin.busVoltage = 0b111;
|
|
expect(notChip.in().busVoltage).toBe(0b1);
|
|
});
|
|
|
|
it("wires SubBus in=in[1]", () => {
|
|
const not3Chip = new Not3();
|
|
const notPart = new Not();
|
|
const inPin = not3Chip.in();
|
|
|
|
not3Chip.wire(notPart, [
|
|
{
|
|
from: { name: "in", start: 1, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
|
|
inPin.busVoltage = 0b0;
|
|
not3Chip.eval();
|
|
expect(notPart.in().busVoltage).toBe(0b0);
|
|
inPin.busVoltage = 0b111;
|
|
not3Chip.eval();
|
|
expect(notPart.in().busVoltage).toBe(0b1);
|
|
});
|
|
|
|
it("wires SubBus in[0]=a", () => {
|
|
const chip = new Chip(["a", "b"], ["out[3]"]);
|
|
const not3 = new Not3();
|
|
|
|
// Not3(in[0]=a, in[1]=b, in[2]=b, out=out)
|
|
chip.wire(not3, [
|
|
{
|
|
from: { name: "a", start: 0, width: undefined },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "b", start: 0, width: undefined },
|
|
to: { name: "in", start: 1, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "b", start: 0, width: undefined },
|
|
to: { name: "in", start: 2, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: undefined },
|
|
to: { name: "out", start: 0, width: undefined },
|
|
},
|
|
]);
|
|
|
|
assertExists(chip.in("b")).busVoltage = 1;
|
|
assertExists(chip.in("a")).busVoltage = 0;
|
|
chip.eval();
|
|
expect(chip.out().busVoltage).toBe(0b001);
|
|
});
|
|
|
|
it("wires SubBus out=out[1]", () => {
|
|
const threeChip = new (class ThreeChip extends Chip {
|
|
constructor() {
|
|
super([], ["out[3]"]);
|
|
}
|
|
})();
|
|
|
|
const notPart = new Not();
|
|
threeChip.wire(notPart, [
|
|
{
|
|
from: { name: "out", start: 1, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
const outPin = notPart.out();
|
|
|
|
outPin.busVoltage = 0b0;
|
|
expect(threeChip.out().busVoltage).toBe(0b0);
|
|
outPin.busVoltage = 0b1;
|
|
expect(threeChip.out().busVoltage).toBe(0b010);
|
|
});
|
|
|
|
it("widens output busses if necessary", () => {
|
|
const mux4way16 = new Chip(
|
|
["in[16]", "b[16]", "c[16]", "d[16]", "sel[2]"],
|
|
["out[16]"],
|
|
);
|
|
|
|
mux4way16.wire(new Mux16(), [
|
|
{
|
|
from: { name: "a", start: 0 },
|
|
to: { name: "a", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "b", start: 0 },
|
|
to: { name: "b", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "sel", start: 0, width: 1 },
|
|
to: { name: "sel", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "out1", start: 0 },
|
|
to: { name: "out", start: 0 },
|
|
},
|
|
]);
|
|
|
|
mux4way16.wire(new Mux16(), [
|
|
{
|
|
from: { name: "c", start: 0 },
|
|
to: { name: "a", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "d", start: 0 },
|
|
to: { name: "b", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "sel", start: 0, width: 1 },
|
|
to: { name: "sel", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "out2", start: 0 },
|
|
to: { name: "out", start: 0 },
|
|
},
|
|
]);
|
|
|
|
mux4way16.wire(new Mux16(), [
|
|
{
|
|
from: { name: "out1", start: 0 },
|
|
to: { name: "a", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "out2", start: 0 },
|
|
to: { name: "b", start: 0 },
|
|
},
|
|
{
|
|
from: { name: "sel", start: 1, width: 1 },
|
|
to: { name: "sel", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("widens internal busses if necessary", () => {
|
|
const chip = new Chip(["in"], [], "test", ["t"]);
|
|
|
|
chip.wire(new Not(), [
|
|
{
|
|
from: { name: "in", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "t", start: 1, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
|
|
chip.in().busVoltage = 0b0;
|
|
chip.eval();
|
|
expect(chip.pin("t").busVoltage).toBe(0b10);
|
|
});
|
|
|
|
class Not8 extends Chip {
|
|
constructor() {
|
|
super(["in[8]"], ["out[8]"]);
|
|
}
|
|
|
|
override eval() {
|
|
const inn = this.in().busVoltage;
|
|
const out = ~inn & 0xff;
|
|
this.out().busVoltage = out;
|
|
}
|
|
}
|
|
|
|
it("assigns input inside wide busses", () => {
|
|
class Foo extends Chip {
|
|
readonly not8 = new Not8();
|
|
constructor() {
|
|
super([], []);
|
|
this.parts.push(this.not8);
|
|
this.pins.insert(new ConstantBus("pal", 0b1010_1100_0011_0101));
|
|
this.pins.get("pal")?.connect(new OutSubBus(this.not8.in(), 4, 8));
|
|
this.pins.emplace("out1", 5);
|
|
const out1Bus = new OutSubBus(
|
|
assertExists(this.pins.get("out1")),
|
|
3,
|
|
5,
|
|
);
|
|
this.not8.out().connect(out1Bus);
|
|
}
|
|
}
|
|
|
|
const foo = new Foo();
|
|
foo.eval();
|
|
expect(foo.not8.in().busVoltage).toEqual(0b1100_0011);
|
|
expect(foo.pin("out1")?.busVoltage).toEqual(0b00111);
|
|
});
|
|
|
|
it("assigns output inside wide busses", () => {
|
|
// From figure A2.2, page 287, 2nd edition
|
|
class Foo extends Chip {
|
|
readonly not8 = new Not8();
|
|
constructor() {
|
|
super([], []);
|
|
this.parts.push(this.not8);
|
|
this.pins.insert(new ConstantBus("six", 0b110));
|
|
// in[0..1] = true
|
|
TRUE_BUS.connect(new InSubBus(this.not8.in(), 0, 2));
|
|
// in[3..5] = six, 110
|
|
this.pins.get("six")?.connect(new InSubBus(this.not8.in(), 3, 3));
|
|
// in[7] = true
|
|
TRUE_BUS.connect(new InSubBus(this.not8.in(), 7, 1));
|
|
// out[3..7] = out1
|
|
this.pins.emplace("out1", 5);
|
|
const out1Bus = new OutSubBus(
|
|
assertExists(this.pins.get("out1")),
|
|
3,
|
|
5,
|
|
);
|
|
this.not8.out().connect(out1Bus);
|
|
}
|
|
}
|
|
|
|
const foo = new Foo();
|
|
foo.eval();
|
|
expect(foo.not8.in().busVoltage).toBe(0b10110011);
|
|
expect(foo.pin("out1").busVoltage).toBe(0b01001);
|
|
});
|
|
|
|
it("pulls portions of true", () => {
|
|
class Foo extends Chip {
|
|
readonly chip = new Not3();
|
|
constructor() {
|
|
super([], []);
|
|
this.wire(this.chip, [
|
|
{
|
|
from: { name: "true", start: 0, width: 1 },
|
|
to: { name: "in", start: 1, width: 2 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const foo = new Foo();
|
|
|
|
const inVoltage = foo.chip.in().busVoltage;
|
|
expect(bin(inVoltage)).toBe(bin(0b110));
|
|
});
|
|
|
|
it("pulls start of true", () => {
|
|
class Foo extends Chip {
|
|
readonly chip = new Not3();
|
|
constructor() {
|
|
super([], []);
|
|
this.wire(this.chip, [
|
|
{
|
|
from: { name: "true", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 2 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const foo = new Foo();
|
|
|
|
const inVoltage = foo.chip.in().busVoltage;
|
|
expect(bin(inVoltage)).toBe(bin(0b11));
|
|
});
|
|
});
|
|
|
|
describe("sequential", () => {
|
|
const clock = Clock.get();
|
|
beforeEach(() => {
|
|
clock.reset();
|
|
});
|
|
|
|
describe("dff", () => {
|
|
it("flips and flops", () => {
|
|
clock.reset();
|
|
const dff = new DFF();
|
|
|
|
clock.tick(); // Read input, low
|
|
expect(dff.out().voltage()).toBe(LOW);
|
|
clock.tock(); // Write t, low
|
|
expect(dff.out().voltage()).toBe(LOW);
|
|
|
|
dff.in().pull(HIGH);
|
|
clock.tick(); // Read input, HIGH
|
|
expect(dff.out().voltage()).toBe(LOW);
|
|
clock.tock(); // Write t, HIGH
|
|
expect(dff.out().voltage()).toBe(HIGH);
|
|
|
|
clock.tick();
|
|
expect(dff.out().voltage()).toBe(HIGH);
|
|
clock.tock();
|
|
expect(dff.out().voltage()).toBe(HIGH);
|
|
});
|
|
});
|
|
|
|
describe("bit", () => {
|
|
it("does not update when load is low", () => {
|
|
clock.reset();
|
|
const bit = new Bit();
|
|
|
|
const inn = bit.in();
|
|
const load = bit.in("load");
|
|
const out = bit.out();
|
|
|
|
load.pull(LOW);
|
|
inn.pull(HIGH);
|
|
expect(out.voltage()).toBe(LOW);
|
|
clock.tick();
|
|
expect(out.voltage()).toBe(LOW);
|
|
clock.tock();
|
|
expect(out.voltage()).toBe(LOW);
|
|
});
|
|
|
|
it("does updates when load is high", () => {
|
|
clock.reset();
|
|
const bit = new Bit();
|
|
|
|
const inn = bit.in();
|
|
const load = bit.in("load");
|
|
const out = bit.out();
|
|
|
|
load.pull(HIGH);
|
|
inn.pull(HIGH);
|
|
expect(out.voltage()).toBe(LOW);
|
|
clock.tick();
|
|
expect(out.voltage()).toBe(LOW);
|
|
clock.tock();
|
|
expect(out.voltage()).toBe(HIGH);
|
|
});
|
|
});
|
|
|
|
describe("PC", () => {
|
|
it("remains constant when not ticking", () => {
|
|
clock.reset();
|
|
const pc = new PC();
|
|
const out = pc.out();
|
|
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tick();
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tock();
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tick();
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tock();
|
|
expect(out.busVoltage).toBe(0);
|
|
});
|
|
|
|
it("increments when ticking", () => {
|
|
clock.reset();
|
|
const pc = new PC();
|
|
const out = pc.out();
|
|
|
|
pc.in("inc").pull(HIGH);
|
|
|
|
clock.tick();
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tock();
|
|
expect(out.busVoltage).toBe(1);
|
|
|
|
clock.tick();
|
|
expect(out.busVoltage).toBe(1);
|
|
clock.tock();
|
|
expect(out.busVoltage).toBe(2);
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
clock.eval();
|
|
expect(out.busVoltage).toBe(i + 3);
|
|
}
|
|
});
|
|
|
|
it("loads a jump value", () => {
|
|
clock.reset();
|
|
const pc = new PC();
|
|
const out = pc.out();
|
|
|
|
pc.in().busVoltage = 0x8286;
|
|
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tick();
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.tock();
|
|
expect(out.busVoltage).toBe(0);
|
|
|
|
pc.in("load").pull(HIGH);
|
|
|
|
expect(out.busVoltage).toBe(0);
|
|
clock.eval();
|
|
expect(out.busVoltage).toBe(0x8286);
|
|
});
|
|
|
|
it("resets", () => {
|
|
clock.reset();
|
|
const pc = new PC();
|
|
const out = pc.out();
|
|
pc.in("inc").pull(HIGH);
|
|
|
|
expect(out.busVoltage).toBe(0);
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
clock.eval();
|
|
}
|
|
|
|
expect(out.busVoltage).toBe(10);
|
|
|
|
pc.in("reset").pull(HIGH);
|
|
|
|
clock.eval();
|
|
|
|
expect(out.busVoltage).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("sorts parts before eval", () => {
|
|
class FooA extends Chip {
|
|
readonly notA = new Not();
|
|
readonly notB = new Not();
|
|
constructor() {
|
|
super([], ["out"], "Foo", ["x"]);
|
|
this.wire(this.notA, [
|
|
{
|
|
from: { name: "x", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.notB, [
|
|
{
|
|
from: { name: "true", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "x", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const fooA = new FooA();
|
|
fooA.sortParts();
|
|
expect(fooA.parts).toEqual([fooA.notB, fooA.notA]);
|
|
|
|
class FooB extends Chip {
|
|
readonly notA = new Not();
|
|
readonly notB = new Not();
|
|
constructor() {
|
|
super([], ["out"], "Foo", ["x"]);
|
|
this.wire(this.notA, [
|
|
{
|
|
from: { name: "true", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "x", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.notB, [
|
|
{
|
|
from: { name: "x", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const fooB = new FooB();
|
|
fooB.sortParts();
|
|
expect(fooB.parts).toEqual([fooB.notA, fooB.notB]);
|
|
});
|
|
|
|
it("sorts clocked chips", () => {
|
|
class FooC extends Chip {
|
|
readonly register = new Register();
|
|
readonly inc16A = new Inc16();
|
|
readonly inc16B = new Inc16();
|
|
constructor() {
|
|
super([], [], "Foo", []);
|
|
this.wire(this.inc16B, [
|
|
{
|
|
from: { name: "b", start: 0, width: 16 },
|
|
to: { name: "in", start: 0, width: 16 },
|
|
},
|
|
{
|
|
from: { name: "c", start: 0, width: 16 },
|
|
to: { name: "out", start: 0, width: 16 },
|
|
},
|
|
]);
|
|
this.wire(this.register, [
|
|
{
|
|
from: { name: "c", start: 0, width: 16 },
|
|
to: { name: "in", start: 0, width: 16 },
|
|
},
|
|
{
|
|
from: { name: "true", start: 0, width: 1 },
|
|
to: { name: "load", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "a", start: 0, width: 16 },
|
|
to: { name: "out", start: 0, width: 16 },
|
|
},
|
|
]);
|
|
this.wire(this.inc16A, [
|
|
{
|
|
from: { name: "a", start: 0, width: 16 },
|
|
to: { name: "in", start: 0, width: 16 },
|
|
},
|
|
{
|
|
from: { name: "b", start: 0, width: 16 },
|
|
to: { name: "out", start: 0, width: 16 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
const fooC = new FooC();
|
|
fooC.sortParts();
|
|
const parts = fooC.parts.map((chip) => chip.id);
|
|
expect(parts).toEqual([fooC.register.id, fooC.inc16A.id, fooC.inc16B.id]);
|
|
});
|
|
|
|
it("evals without order issues (after sorting)", () => {
|
|
/*
|
|
CHIP Or {
|
|
IN a, b;
|
|
OUT out;
|
|
|
|
PARTS:
|
|
|
|
Not(in =b , out = net2);
|
|
Nand(a = net, b =net2 , out =out );
|
|
Not(in =a , out = net);
|
|
}
|
|
*/
|
|
class OrA extends Chip {
|
|
readonly nota = new Not();
|
|
readonly nand = new Nand();
|
|
readonly notb = new Not();
|
|
constructor() {
|
|
super(["a", "b"], ["out"], "OrA", []);
|
|
this.wire(this.nota, [
|
|
{
|
|
from: { name: "b", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.nand, [
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "a", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "b", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.notb, [
|
|
{
|
|
from: { name: "a", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.sortParts();
|
|
}
|
|
}
|
|
|
|
class OrB extends Chip {
|
|
readonly nota = new Not();
|
|
readonly nand = new Nand();
|
|
readonly notb = new Not();
|
|
constructor() {
|
|
super(["a", "b"], ["out"], "OrB", []);
|
|
this.wire(this.nota, [
|
|
{
|
|
from: { name: "b", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.notb, [
|
|
{
|
|
from: { name: "a", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
this.wire(this.nand, [
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "a", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "b", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
class OrC extends Chip {
|
|
readonly nota = new Not();
|
|
readonly nand = new Nand();
|
|
readonly notb = new Not();
|
|
constructor() {
|
|
super(["a", "b"], ["out"], "OrC", []);
|
|
this.wireAll([
|
|
{
|
|
part: this.nota,
|
|
connections: [
|
|
{
|
|
from: { name: "b", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
],
|
|
},
|
|
{
|
|
part: this.nand,
|
|
connections: [
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "a", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net2", start: 0, width: 1 },
|
|
to: { name: "b", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "out", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
],
|
|
},
|
|
{
|
|
part: this.notb,
|
|
connections: [
|
|
{
|
|
from: { name: "a", start: 0, width: 1 },
|
|
to: { name: "in", start: 0, width: 1 },
|
|
},
|
|
{
|
|
from: { name: "net", start: 0, width: 1 },
|
|
to: { name: "out", start: 0, width: 1 },
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
const ora = new OrA();
|
|
ora.in("a").pull(HIGH);
|
|
ora.in("b").pull(LOW);
|
|
ora.eval();
|
|
expect(ora.out("out").busVoltage).toBe(HIGH);
|
|
|
|
const orb = new OrB();
|
|
orb.in("a").pull(HIGH);
|
|
orb.in("b").pull(LOW);
|
|
orb.eval();
|
|
expect(orb.out("out").busVoltage).toBe(HIGH);
|
|
|
|
const orc = new OrC();
|
|
orc.in("a").pull(HIGH);
|
|
orc.in("b").pull(LOW);
|
|
orc.eval();
|
|
expect(orc.out("out").busVoltage).toBe(HIGH);
|
|
});
|
|
});
|