From 4d86ed3ba7e0304ac7aadbf4625d35e59f93c0d3 Mon Sep 17 00:00:00 2001 From: Neta London Date: Thu, 1 Feb 2024 12:47:06 +0200 Subject: [PATCH 1/3] Enforce equal bus widths --- simulator/src/chip/builder.ts | 61 +++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index ca8fdab76..22647565d 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -93,6 +93,13 @@ export async function build( interface InternalPin { isDefined: boolean; firstUse: Span; + width?: number; +} + +interface Wire { + chip: Chip; + lhs: PinParts; + rhs: PinParts; } function isConstant(pinName: string): boolean { @@ -104,6 +111,20 @@ function isConstant(pinName: string): boolean { ); } +function getSubBusWidth(pin: PinParts): number | undefined { + if (pin.start != undefined && pin.end != undefined) { + return pin.end - pin.start + 1; + } + return undefined; +} + +function display(pin: PinParts): string { + if (pin.start != undefined && pin.end != undefined) { + return `${pin.pin}[${pin.start}..${pin.end}]`; + } + return pin.pin; +} + function createWire(lhs: PinParts, rhs: PinParts): Connection { return { to: { @@ -172,6 +193,7 @@ class ChipBuilder { private internalPins: Map = new Map(); private inPins: Map> = new Map(); private outPins: Map> = new Map(); + private wires: Wire[] = []; constructor(parts: HdlParse, fs?: FileSystem, name?: string) { this.parts = parts; @@ -229,7 +251,12 @@ class ChipBuilder { return result; } } - const result = this.validateInternalPins(); + let result = this.validateInternalPins(); + if (isErr(result)) { + return result; + } + // We need to check this at the end because during wiring we might not know the width of some internal pins + result = this.validateWireWidths(); if (isErr(result)) { return result; } @@ -266,7 +293,7 @@ class ChipBuilder { return result; } } else if (partChip.isOutPin(lhs.pin)) { - const result = this.validateOutputWire(rhs); + const result = this.validateOutputWire(partChip, lhs, rhs); if (isErr(result)) { return result; } @@ -276,6 +303,7 @@ class ChipBuilder { span: lhs.span, }); } + this.wires.push({ chip: partChip, lhs, rhs }); return Ok(); } @@ -316,7 +344,11 @@ class ChipBuilder { return Ok(); } - private validateOutputWire(rhs: PinParts): Result { + private validateOutputWire( + partChip: Chip, + lhs: PinParts, + rhs: PinParts + ): Result { let result = this.validateWriteTarget(rhs); if (isErr(result)) { return result; @@ -337,10 +369,12 @@ class ChipBuilder { } // track internal pin creation to detect undefined pins const pinData = this.internalPins.get(rhs.pin); + const width = getSubBusWidth(lhs) ?? partChip.get(lhs.pin)?.width; if (pinData == undefined) { this.internalPins.set(rhs.pin, { isDefined: true, firstUse: rhs.span, + width, }); } else { if (pinData.isDefined) { @@ -350,6 +384,7 @@ class ChipBuilder { }); } pinData.isDefined = true; + pinData.width = width; } } return Ok(); @@ -402,4 +437,24 @@ class ChipBuilder { } return Ok(); } + + private validateWireWidths(): Result { + for (const wire of this.wires) { + const lhsWidth = + getSubBusWidth(wire.lhs) ?? wire.chip.get(wire.lhs.pin)?.width; + const rhsWidth = + getSubBusWidth(wire.rhs) ?? + this.chip.get(wire.rhs.pin)?.width ?? + this.internalPins.get(wire.rhs.pin)?.width; + if (lhsWidth != rhsWidth) { + return Err({ + message: `Different bus widths: ${display( + wire.lhs + )}(${lhsWidth}) and ${display(wire.rhs)}(${rhsWidth})`, + span: wire.lhs.span, + }); + } + } + return Ok(); + } } From 1fe02ab71002cc9f3dc3761730a1043dd013eff5 Mon Sep 17 00:00:00 2001 From: Neta London Date: Thu, 1 Feb 2024 13:55:00 +0200 Subject: [PATCH 2/3] Allow constant bus of any width --- simulator/src/chip/builder.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 22647565d..0ee239161 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -303,7 +303,9 @@ class ChipBuilder { span: lhs.span, }); } - this.wires.push({ chip: partChip, lhs, rhs }); + if (!isConstant(rhs.pin)) { + this.wires.push({ chip: partChip, lhs, rhs }); + } return Ok(); } From f767ada284c8fd8de48305083e93cffddfb9948d Mon Sep 17 00:00:00 2001 From: Neta London Date: Mon, 5 Feb 2024 14:09:47 +0200 Subject: [PATCH 3/3] Add tests --- simulator/src/chip/builder.test.ts | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/simulator/src/chip/builder.test.ts b/simulator/src/chip/builder.test.ts index 485888444..7558a24de 100644 --- a/simulator/src/chip/builder.test.ts +++ b/simulator/src/chip/builder.test.ts @@ -138,6 +138,40 @@ describe("Chip Builder", () => { foo.eval(); expect(foo.out("b").busVoltage).toBe(0); }); + + it("returns error for mismatching input width", async () => { + try { + const chip = unwrap( + HDL.parse(`CHIP Foo { + IN in[3]; OUT out; + PARTS: Or8Way(in=in, out=out); + }`) + ); + const foo = await build(chip); + expect(foo).toBeErr(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + throw new Error(display(e.message ?? e.shortMessage ?? e)); + } + }); + + it("returns error for mismatching output width", async () => { + try { + const chip = unwrap( + HDL.parse(`CHIP Foo { + IN in; OUT out[5]; + PARTS: Not(in=in, out=out); + }`) + ); + const foo = await build(chip); + expect(foo).toBeErr(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + throw new Error(display(e.message ?? e.shortMessage ?? e)); + } + }); }); const USE_COPY_HDL = `CHIP UseCopy {