Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace insertion toposort with traditional toposort #381

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions simulator/src/chip/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class ChipBuilder {
}

// Reset clock order after wiring sub-pins
this.chip.sortParts();
for (const part of this.chip.parts) {
part.subscribeToClock();
}
Expand Down
3 changes: 3 additions & 0 deletions simulator/src/chip/chip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ describe("Chip", () => {
}

const fooA = new FooA();
fooA.sortParts();
expect(fooA.parts).toEqual([fooA.notB, fooA.notA]);

class FooB extends Chip {
Expand Down Expand Up @@ -672,6 +673,7 @@ describe("Chip", () => {
}

const fooB = new FooB();
fooB.sortParts();
expect(fooB.parts).toEqual([fooB.notA, fooB.notB]);
});

Expand Down Expand Up @@ -719,6 +721,7 @@ describe("Chip", () => {
}
}
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]);
});
Expand Down
67 changes: 51 additions & 16 deletions simulator/src/chip/chip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
Result,
isErr,
} from "@davidsouther/jiffies/lib/esm/result.js";
import type { Subscription } from "rxjs";
import { bin } from "../util/twos.js";
import { Clock } from "./clock.js";
import type { Subscription } from "rxjs";

export const HIGH = 1;
export const LOW = 0;
Expand Down Expand Up @@ -450,25 +450,60 @@ export class Chip {
}
}
}

// Topological insertion sort for where this part should go.
// It should go at the lower of:
// before the first chip that it has an output to, or at the end
// after the last chip it has an input from, or at the beginning
const before = this.parts
.map((other, i) => ({ other, i }))
.filter(({ other }) => this.hasConnection(part, other));
const beforeIdx = before.at(0)?.i ?? this.parts.length;
const after = this.parts
.map((other, i) => ({ other, i }))
.filter(({ other }) => this.hasConnection(other, part));
const afterIdx = (after.at(-1)?.i ?? -1) + 1;
const index = Math.min(beforeIdx, afterIdx);
this.parts.splice(index, 0, part);
this.parts.push(part);

return Ok();
}

sortParts() {
const sorted: Chip[] = [];
const visited = new Set<Chip>();
const visiting = new Set<Chip>();

type Node = { part: Chip; isReturning: boolean };

const stack: Node[] = this.parts.map((part) => ({
part,
isReturning: false,
}));

while (stack.length > 0) {
const node = assertExists(stack.pop());

if (node.isReturning) {
// If we are returning to this node, we can safely add it to the sorted list
visited.add(node.part);
sorted.push(node.part);
} else if (!visited.has(node.part)) {
if (visiting.has(node.part)) {
continue;
}
visiting.add(node.part);

// Re-push this node to handle it on return
stack.push({ part: node.part, isReturning: true });

// Push all its children to visit them
for (const out of this.partToOuts.get(node.part) ?? []) {
stack.push(
...Array.from(this.insToPart.get(out) ?? [])
.filter(
(part) =>
!(part.clocked && this.isInternalPin(out)) &&
!visited.has(part),
)
.map((part) => ({
part,
isReturning: false,
})),
);
}
}
}

this.parts = sorted.reverse();
}

private findPin(from: string, minWidth?: number): Pin {
if (from === "true" || from === "1") {
return TRUE_BUS;
Expand Down
Loading