From 9b6288d8915290c4c8eb432c8849cc488a18e641 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 09:49:34 +0200 Subject: [PATCH 01/18] Forbid use of internal pin sub bus --- simulator/src/chip/builder.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index e93afefb9..9d9cdd9f3 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -132,6 +132,12 @@ export async function build( ); if (partChip.isInPin(lhs.pin)) { + if (rhs.start && isRhsInternal) { + return Err({ + message: `Cannot use sub bus of an internal pin as input`, + span: rhs.span, + }); + } if (isRhsInternal) { // internal pin is being used const pinData = internalPins.get(rhs.pin); From 81dc725c161d3ab2d924428fcf823b8587f786ee Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 09:54:40 +0200 Subject: [PATCH 02/18] Forbid feeding back an output pin --- simulator/src/chip/builder.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 9d9cdd9f3..f0ef7900a 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -132,6 +132,12 @@ export async function build( ); if (partChip.isInPin(lhs.pin)) { + if (buildChip.isOutPin(rhs.pin)) { + return Err({ + message: `Cannot use chip output pin as input`, + span: rhs.span, + }); + } if (rhs.start && isRhsInternal) { return Err({ message: `Cannot use sub bus of an internal pin as input`, From d716b4b80772138225a42a616b82f9ade179ffbd Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 10:02:43 +0200 Subject: [PATCH 03/18] Forbid multiple assignments to in pin --- simulator/src/chip/builder.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index f0ef7900a..824bfed65 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -124,6 +124,7 @@ export async function build( const partChip = Ok(builtin); const wires: Connection[] = []; + const inPins: Set = new Set(); for (const { lhs, rhs } of part.wires) { const isRhsInternal = !( buildChip.isInPin(rhs.pin) || @@ -132,6 +133,15 @@ export async function build( ); if (partChip.isInPin(lhs.pin)) { + if (inPins.has(lhs.pin)) { + return Err({ + message: `Cannot input to the same pin multiple times`, + span: lhs.span, + }); + } else { + inPins.add(lhs.pin); + } + if (buildChip.isOutPin(rhs.pin)) { return Err({ message: `Cannot use chip output pin as input`, From 385e6525a13052e24cee487dd0a991f68b9cde5c Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 13:48:41 +0200 Subject: [PATCH 04/18] Allow splitting input from multiple non-overlapping busses --- simulator/src/chip/builder.ts | 43 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 824bfed65..012b6a3cb 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -7,7 +7,7 @@ import { Result, } from "@davidsouther/jiffies/lib/esm/result.js"; import { ParseError, Span } from "../languages/base.js"; -import { HDL, HdlParse } from "../languages/hdl.js"; +import { HDL, HdlParse, PinParts } from "../languages/hdl.js"; import { getBuiltinChip, hasBuiltinChip } from "./builtins/index.js"; import { Chip, Connection } from "./chip.js"; @@ -95,6 +95,17 @@ interface InternalPin { firstUse: Span; } +function getIndices(pin: PinParts): number[] { + if (pin.start != undefined && pin.end != undefined) { + const indices = []; + for (let i = pin.start; i <= pin.end; i++) { + indices.push(i); + } + return indices; + } + return [-1]; +} + export async function build( parts: HdlParse, fs?: FileSystem @@ -124,7 +135,7 @@ export async function build( const partChip = Ok(builtin); const wires: Connection[] = []; - const inPins: Set = new Set(); + const inPins: Map> = new Map(); for (const { lhs, rhs } of part.wires) { const isRhsInternal = !( buildChip.isInPin(rhs.pin) || @@ -133,13 +144,29 @@ export async function build( ); if (partChip.isInPin(lhs.pin)) { - if (inPins.has(lhs.pin)) { - return Err({ - message: `Cannot input to the same pin multiple times`, - span: lhs.span, - }); + const indices = inPins.get(lhs.pin); + if (!indices) { + inPins.set(lhs.pin, new Set(getIndices(lhs))); } else { - inPins.add(lhs.pin); + if (indices.has(-1)) { + // -1 stands for the whole bus width + return Err({ + message: `Cannot input to the same pin multiple times`, + span: lhs.span, + }); + } else if (lhs.start !== undefined && lhs.end !== undefined) { + for (const i of getIndices(lhs)) { + if (indices.has(i)) { + return Err({ + message: `Cannot input to the same pin multiple times`, + span: lhs.span, + }); + } + indices.add(i); + } + } else { + indices.add(-1); + } } if (buildChip.isOutPin(rhs.pin)) { From dfec6dc3e799689b16da91124265ed5fb0b45196 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 13:53:03 +0200 Subject: [PATCH 05/18] Add pin names to error messages --- simulator/src/chip/builder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 012b6a3cb..8f7796165 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -151,14 +151,14 @@ export async function build( if (indices.has(-1)) { // -1 stands for the whole bus width return Err({ - message: `Cannot input to the same pin multiple times`, + message: `Cannot input to the same pin multiple times: ${lhs.pin}`, span: lhs.span, }); } else if (lhs.start !== undefined && lhs.end !== undefined) { for (const i of getIndices(lhs)) { if (indices.has(i)) { return Err({ - message: `Cannot input to the same pin multiple times`, + message: `Cannot input to the same pin multiple times: ${lhs.pin}[${i}]`, span: lhs.span, }); } @@ -177,7 +177,7 @@ export async function build( } if (rhs.start && isRhsInternal) { return Err({ - message: `Cannot use sub bus of an internal pin as input`, + message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, span: rhs.span, }); } From 80ea1ce15e802c97057a24a837d2f6da14cd4810 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 15:28:54 +0200 Subject: [PATCH 06/18] Fix sub bus check to include 0 start index --- simulator/src/chip/builder.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 8f7796165..b71ca2974 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -175,13 +175,13 @@ export async function build( span: rhs.span, }); } - if (rhs.start && isRhsInternal) { - return Err({ - message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, - span: rhs.span, - }); - } if (isRhsInternal) { + if (rhs.start != undefined) { + return Err({ + message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, + span: rhs.span, + }); + } // internal pin is being used const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { From 0cb654924b874c0298c5eb01d55950664bcc07cd Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 15:56:56 +0200 Subject: [PATCH 07/18] Forbid writing to chip's input or writing to output multiple times --- simulator/src/chip/builder.ts | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index b71ca2974..8d599e424 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -144,6 +144,7 @@ export async function build( ); if (partChip.isInPin(lhs.pin)) { + // inputting from rhs to chip input const indices = inPins.get(lhs.pin); if (!indices) { inPins.set(lhs.pin, new Set(getIndices(lhs))); @@ -182,7 +183,6 @@ export async function build( span: rhs.span, }); } - // internal pin is being used const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { internalPins.set(rhs.pin, { @@ -197,23 +197,29 @@ export async function build( } } } else if (partChip.isOutPin(lhs.pin)) { - if (isRhsInternal) { - // internal pin is being defined - const pinData = internalPins.get(rhs.pin); - if (pinData == undefined) { - internalPins.set(rhs.pin, { - isDefined: true, - firstUse: rhs.span, + // inputting from chip output to rhs + if (buildChip.isInPin(rhs.pin)) { + return Err({ + message: `Cannot output to chip input pin ${rhs.pin}`, + span: rhs.span, + }); + } + const pinData = internalPins.get(rhs.pin); + if (pinData == undefined) { + internalPins.set(rhs.pin, { + isDefined: true, + firstUse: rhs.span, + }); + } else { + if (pinData.isDefined) { + return Err({ + message: buildChip.isOutPin(rhs.pin) + ? `Cannot input to output chip ${rhs.pin} multiple times` + : `Internal pin ${rhs.pin} already defined`, + span: rhs.span, }); - } else { - if (pinData.isDefined) { - return Err({ - message: `Internal pin ${rhs.pin} already defined`, - span: rhs.span, - }); - } - pinData.isDefined = true; } + pinData.isDefined = true; } } else { return Err({ From 0dfedf2d61f6caffa3b8cc0cdac7f772bfddc907 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:00:48 +0200 Subject: [PATCH 08/18] Clarify error messages --- simulator/src/chip/builder.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 8d599e424..41265d54e 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -152,14 +152,14 @@ export async function build( if (indices.has(-1)) { // -1 stands for the whole bus width return Err({ - message: `Cannot input to the same pin multiple times: ${lhs.pin}`, + message: `Cannot write to the same pin multiple times: ${lhs.pin}`, span: lhs.span, }); } else if (lhs.start !== undefined && lhs.end !== undefined) { for (const i of getIndices(lhs)) { if (indices.has(i)) { return Err({ - message: `Cannot input to the same pin multiple times: ${lhs.pin}[${i}]`, + message: `Cannot write to the same pin multiple times: ${lhs.pin}[${i}]`, span: lhs.span, }); } @@ -172,7 +172,7 @@ export async function build( if (buildChip.isOutPin(rhs.pin)) { return Err({ - message: `Cannot use chip output pin as input`, + message: `Cannot use output pin as input`, span: rhs.span, }); } @@ -200,7 +200,7 @@ export async function build( // inputting from chip output to rhs if (buildChip.isInPin(rhs.pin)) { return Err({ - message: `Cannot output to chip input pin ${rhs.pin}`, + message: `Cannot write to input pin ${rhs.pin}`, span: rhs.span, }); } @@ -214,7 +214,7 @@ export async function build( if (pinData.isDefined) { return Err({ message: buildChip.isOutPin(rhs.pin) - ? `Cannot input to output chip ${rhs.pin} multiple times` + ? `Cannot write to output pin ${rhs.pin} multiple times` : `Internal pin ${rhs.pin} already defined`, span: rhs.span, }); From 26ba61ab718edfce18b583899d900ef0c65f2e1f Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:04:40 +0200 Subject: [PATCH 09/18] Show error span and line for writing to constant bus --- simulator/src/chip/builder.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 41265d54e..16ee193b5 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -204,6 +204,13 @@ export async function build( span: rhs.span, }); } + if (isConstant(rhs.pin)) { + return Err({ + message: `Cannot write to constant bus`, + span: rhs.span, + }); + } + const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { internalPins.set(rhs.pin, { From b8d24eb50defe1f881485dbbfd172b0ccf3b482b Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:11:35 +0200 Subject: [PATCH 10/18] Forbid using chip inside its own implementation --- simulator/src/chip/builder.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 16ee193b5..8fad9c58a 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -134,6 +134,13 @@ export async function build( } const partChip = Ok(builtin); + if (partChip.name == buildChip.name) { + return Err({ + message: `Cannot use chip ${partChip.name} to implement itself`, + span: part.span, + }); + } + const wires: Connection[] = []; const inPins: Map> = new Map(); for (const { lhs, rhs } of part.wires) { From a5ee860122e8246c74a9b5d97ed16a42028f734f Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:25:18 +0200 Subject: [PATCH 11/18] Make wrong chip name a compilation error instead of a warning --- components/src/stores/chip.store.ts | 9 ++------- simulator/src/chip/builder.ts | 15 ++++++++++++--- simulator/src/languages/hdl.ts | 6 ++++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/components/src/stores/chip.store.ts b/components/src/stores/chip.store.ts index e73752c69..46d5468eb 100644 --- a/components/src/stores/chip.store.ts +++ b/components/src/stores/chip.store.ts @@ -321,7 +321,7 @@ export function makeChipStore( async compileChip(hdl: string) { chip.remove(); - const maybeChip = await parseChip(hdl); + const maybeChip = await parseChip(hdl, chipName); if (isErr(maybeChip)) { const error = Err(maybeChip); setStatus( @@ -335,12 +335,7 @@ export function makeChipStore( }); return; } - if (Ok(maybeChip).name != chipName) { - setStatus("Warning: Chip name doesn't match selected chip"); - } else { - setStatus(`Compiled ${chipName}`); - setStatus(`HDL code: No syntax errors`); - } + setStatus(`HDL code: No syntax errors`); this.replaceChip(Ok(maybeChip)); }, diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 8fad9c58a..be2da3e27 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -43,13 +43,14 @@ function parseErrorToCompilationError(error: ParseError) { } export async function parse( - code: string + code: string, + name?: string ): Promise> { const parsed = HDL.parse(code.toString()); if (isErr(parsed)) { return Err(parseErrorToCompilationError(Err(parsed))); } - return build(Ok(parsed)); + return build(Ok(parsed), undefined, name); } export async function loadChip( @@ -108,8 +109,16 @@ function getIndices(pin: PinParts): number[] { export async function build( parts: HdlParse, - fs?: FileSystem + fs?: FileSystem, + name?: string ): Promise> { + if (parts.name != name) { + return Err({ + message: `Wrong chip name`, + span: parts.nameSpan, + }); + } + if (parts.parts === "BUILTIN") { return getBuiltinChip(parts.name.toString()); } diff --git a/simulator/src/languages/hdl.ts b/simulator/src/languages/hdl.ts index 9a63c4d52..08394b742 100644 --- a/simulator/src/languages/hdl.ts +++ b/simulator/src/languages/hdl.ts @@ -30,6 +30,7 @@ export interface Part { export interface HdlParse { name: string; + nameSpan: Span; ins: PinDeclaration[]; outs: PinDeclaration[]; clocked: string[]; @@ -129,9 +130,10 @@ hdlSemantics.addAttribute("PinList", { }); hdlSemantics.addAttribute("Chip", { - Chip(_a, { name }, _b, body, _c) { + Chip(_a, name, _b, body, _c) { return { - name, + name: name.sourceString, + nameSpan: span(name.source), ins: body.child(0).child(0)?.child(1)?.PinList ?? [], outs: body.child(1).child(0)?.child(1)?.PinList ?? [], parts: body.child(2).PartList ?? [], From 5b82cc63e77310ad43a60857a22c52c596a4b229 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:47:01 +0200 Subject: [PATCH 12/18] Extract compilation checks to separate functions --- simulator/src/chip/builder.ts | 129 ++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 45 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index be2da3e27..7842740f4 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -107,6 +107,78 @@ function getIndices(pin: PinParts): number[] { return [-1]; } +function checkMultipleInputAssignments( + lhs: PinParts, + inPins: Map> +): Result { + const indices = inPins.get(lhs.pin); + if (!indices) { + inPins.set(lhs.pin, new Set(getIndices(lhs))); + } else { + if (indices.has(-1)) { + // -1 stands for the whole bus width + return Err({ + message: `Cannot write to the same pin multiple times: ${lhs.pin}`, + span: lhs.span, + }); + } else if (lhs.start !== undefined && lhs.end !== undefined) { + for (const i of getIndices(lhs)) { + if (indices.has(i)) { + return Err({ + message: `Cannot write to the same pin multiple times: ${lhs.pin}[${i}]`, + span: lhs.span, + }); + } + indices.add(i); + } + } else { + indices.add(-1); + } + } + return Ok(true); +} + +function checkBadInputSource( + rhs: PinParts, + buildChip: Chip +): Result { + if (buildChip.isOutPin(rhs.pin)) { + return Err({ + message: `Cannot use output pin as input`, + span: rhs.span, + }); + } else if ( + !buildChip.isInPin(rhs.pin) && + !isConstant(rhs.pin) && + rhs.start != undefined + ) { + return Err({ + message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, + span: rhs.span, + }); + } + return Ok(true); +} + +function checkBadWriteTarget( + rhs: PinParts, + buildChip: Chip +): Result { + if (buildChip.isInPin(rhs.pin)) { + return Err({ + message: `Cannot write to input pin ${rhs.pin}`, + span: rhs.span, + }); + } + if (isConstant(rhs.pin)) { + return Err({ + message: `Cannot write to constant bus`, + span: rhs.span, + }); + } + return Ok(true); +} + export async function build( parts: HdlParse, fs?: FileSystem, @@ -161,44 +233,18 @@ export async function build( if (partChip.isInPin(lhs.pin)) { // inputting from rhs to chip input - const indices = inPins.get(lhs.pin); - if (!indices) { - inPins.set(lhs.pin, new Set(getIndices(lhs))); - } else { - if (indices.has(-1)) { - // -1 stands for the whole bus width - return Err({ - message: `Cannot write to the same pin multiple times: ${lhs.pin}`, - span: lhs.span, - }); - } else if (lhs.start !== undefined && lhs.end !== undefined) { - for (const i of getIndices(lhs)) { - if (indices.has(i)) { - return Err({ - message: `Cannot write to the same pin multiple times: ${lhs.pin}[${i}]`, - span: lhs.span, - }); - } - indices.add(i); - } - } else { - indices.add(-1); - } + let result = checkMultipleInputAssignments(lhs, inPins); + if (isErr(result)) { + return result; } - if (buildChip.isOutPin(rhs.pin)) { - return Err({ - message: `Cannot use output pin as input`, - span: rhs.span, - }); + result = checkBadInputSource(rhs, buildChip); + if (isErr(result)) { + return result; } + + // track internal pin use to detect undefined pins if (isRhsInternal) { - if (rhs.start != undefined) { - return Err({ - message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, - span: rhs.span, - }); - } const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { internalPins.set(rhs.pin, { @@ -214,19 +260,12 @@ export async function build( } } else if (partChip.isOutPin(lhs.pin)) { // inputting from chip output to rhs - if (buildChip.isInPin(rhs.pin)) { - return Err({ - message: `Cannot write to input pin ${rhs.pin}`, - span: rhs.span, - }); - } - if (isConstant(rhs.pin)) { - return Err({ - message: `Cannot write to constant bus`, - span: rhs.span, - }); + const result = checkBadWriteTarget(rhs, buildChip); + if (isErr(result)) { + return result; } + // track internal pin creation to detect undefined pins const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { internalPins.set(rhs.pin, { From 2fe613e4705e9525ff7d82aac276689e1dd9c396 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 16:55:16 +0200 Subject: [PATCH 13/18] Forbid use of constant sub bus --- simulator/src/chip/builder.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 7842740f4..c9a07d72b 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -147,13 +147,11 @@ function checkBadInputSource( message: `Cannot use output pin as input`, span: rhs.span, }); - } else if ( - !buildChip.isInPin(rhs.pin) && - !isConstant(rhs.pin) && - rhs.start != undefined - ) { + } else if (!buildChip.isInPin(rhs.pin) && rhs.start != undefined) { return Err({ - message: `Cannot use sub bus of internal pin ${rhs.pin} as input`, + message: isConstant(rhs.pin) + ? `Cannot use sub bus of constant bus` + : `Cannot use sub bus of internal pin ${rhs.pin} as input`, span: rhs.span, }); } From b02a3d1e85ab2043f9d01902f9dbf2773da58e6d Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 17:07:15 +0200 Subject: [PATCH 14/18] Update hdl test file --- simulator/src/languages/hdl.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/simulator/src/languages/hdl.test.ts b/simulator/src/languages/hdl.test.ts index f63264565..3d1c19670 100644 --- a/simulator/src/languages/hdl.test.ts +++ b/simulator/src/languages/hdl.test.ts @@ -1,9 +1,9 @@ import { - grammar, - hdlSemantics, HdlParse, Part, PinDeclaration, + grammar, + hdlSemantics, } from "./hdl.js"; const AND_BUILTIN = `CHIP And { @@ -266,6 +266,7 @@ describe("HDL w/ Ohm", () => { expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ name: "And", + nameSpan: { start: 5, end: 8, line: 1 }, ins: [ { pin: "a", width: 1 }, { pin: "b", width: 1 }, @@ -280,6 +281,7 @@ describe("HDL w/ Ohm", () => { expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ name: "Not", + nameSpan: { start: 5, end: 8, line: 1 }, ins: [{ pin: "in", width: 1 }], outs: [{ pin: "out", width: 1 }], parts: [ @@ -311,6 +313,7 @@ describe("HDL w/ Ohm", () => { expect(hdlSemantics(match).Chip).toEqual({ name: "Not", + nameSpan: { start: 5, end: 8, line: 1 }, ins: [{ pin: "in", width: 1 }], outs: [{ pin: "out", width: 1 }], parts: [], @@ -322,6 +325,7 @@ describe("HDL w/ Ohm", () => { expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ name: "And16", + nameSpan: { start: 5, end: 10, line: 1 }, ins: [ { pin: "a", width: 16 }, { pin: "b", width: 16 }, @@ -336,6 +340,7 @@ describe("HDL w/ Ohm", () => { expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ name: "Foo", + nameSpan: { start: 5, end: 8, line: 1 }, ins: [{ pin: "in", width: 1 }], outs: [], parts: [], From c70920fa82ea162b24c70ff589e49f267540862c Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 17:10:02 +0200 Subject: [PATCH 15/18] Don't fail parsing when expected name is not given --- simulator/src/chip/builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index c9a07d72b..354f54223 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -182,7 +182,7 @@ export async function build( fs?: FileSystem, name?: string ): Promise> { - if (parts.name != name) { + if (name && parts.name != name) { return Err({ message: `Wrong chip name`, span: parts.nameSpan, From cb86793adbf6de00166696cdc360263f0f456e84 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 17:45:36 +0200 Subject: [PATCH 16/18] Allow splitting assignement to output --- simulator/src/chip/builder.ts | 72 ++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 354f54223..10f05eaa4 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -107,27 +107,23 @@ function getIndices(pin: PinParts): number[] { return [-1]; } -function checkMultipleInputAssignments( - lhs: PinParts, - inPins: Map> +// returns the index that has been assigned multiple times if one exists +function checkMultipleAssignments( + pin: PinParts, + assignedIndexes: Map> ): Result { - const indices = inPins.get(lhs.pin); + let errorIndex: number | null = null; + const indices = assignedIndexes.get(pin.pin); if (!indices) { - inPins.set(lhs.pin, new Set(getIndices(lhs))); + assignedIndexes.set(pin.pin, new Set(getIndices(pin))); } else { if (indices.has(-1)) { // -1 stands for the whole bus width - return Err({ - message: `Cannot write to the same pin multiple times: ${lhs.pin}`, - span: lhs.span, - }); - } else if (lhs.start !== undefined && lhs.end !== undefined) { - for (const i of getIndices(lhs)) { + errorIndex = pin.start ?? -1; + } else if (pin.start !== undefined && pin.end !== undefined) { + for (const i of getIndices(pin)) { if (indices.has(i)) { - return Err({ - message: `Cannot write to the same pin multiple times: ${lhs.pin}[${i}]`, - span: lhs.span, - }); + errorIndex = i; } indices.add(i); } @@ -135,6 +131,14 @@ function checkMultipleInputAssignments( indices.add(-1); } } + if (errorIndex != null) { + return Err({ + message: `Cannot write to pin ${pin.pin}${ + errorIndex != -1 ? `[${errorIndex}]` : "" + } multiple times`, + span: pin.span, + }); + } return Ok(true); } @@ -202,6 +206,7 @@ export async function build( ); const internalPins: Map = new Map(); + const outPins: Map> = new Map(); for (const part of parts.parts) { const builtin = await loadChip(part.name.toString(), fs); @@ -231,7 +236,7 @@ export async function build( if (partChip.isInPin(lhs.pin)) { // inputting from rhs to chip input - let result = checkMultipleInputAssignments(lhs, inPins); + let result = checkMultipleAssignments(lhs, inPins); if (isErr(result)) { return result; } @@ -263,23 +268,30 @@ export async function build( return result; } - // track internal pin creation to detect undefined pins - const pinData = internalPins.get(rhs.pin); - if (pinData == undefined) { - internalPins.set(rhs.pin, { - isDefined: true, - firstUse: rhs.span, - }); + if (buildChip.isOutPin(rhs.pin)) { + const result = checkMultipleAssignments(rhs, outPins); + if (isErr(result)) { + return result; + } } else { - if (pinData.isDefined) { - return Err({ - message: buildChip.isOutPin(rhs.pin) - ? `Cannot write to output pin ${rhs.pin} multiple times` - : `Internal pin ${rhs.pin} already defined`, - span: rhs.span, + // track internal pin creation to detect undefined pins + const pinData = internalPins.get(rhs.pin); + if (pinData == undefined) { + internalPins.set(rhs.pin, { + isDefined: true, + firstUse: rhs.span, }); + } else { + if (pinData.isDefined) { + return Err({ + message: buildChip.isOutPin(rhs.pin) + ? `Cannot write to output pin ${rhs.pin} multiple times` + : `Internal pin ${rhs.pin} already defined`, + span: rhs.span, + }); + } + pinData.isDefined = true; } - pinData.isDefined = true; } } else { return Err({ From 8e6648822ad9e931c19cb1edbd7543b74d2e3923 Mon Sep 17 00:00:00 2001 From: Neta London Date: Sun, 7 Jan 2024 17:49:12 +0200 Subject: [PATCH 17/18] Forbid writing to sub bus of internal pin --- simulator/src/chip/builder.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index 10f05eaa4..be553c426 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -274,6 +274,13 @@ export async function build( return result; } } else { + // rhs is necessarily an internal pin + if (rhs.start !== undefined || rhs.end !== undefined) { + return Err({ + message: `Cannot write to sub bus of internal pin ${rhs.pin}`, + span: rhs.span, + }); + } // track internal pin creation to detect undefined pins const pinData = internalPins.get(rhs.pin); if (pinData == undefined) { From f48a2cb5c5dfc7cde75a9ef4a3061d5e031b4ec9 Mon Sep 17 00:00:00 2001 From: Neta London Date: Wed, 10 Jan 2024 12:17:47 +0200 Subject: [PATCH 18/18] Make requested change to HDLParse interface --- simulator/src/chip/builder.ts | 10 +++++----- simulator/src/languages/hdl.test.ts | 15 +++++---------- simulator/src/languages/hdl.ts | 6 ++---- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/simulator/src/chip/builder.ts b/simulator/src/chip/builder.ts index be553c426..30e0a3141 100644 --- a/simulator/src/chip/builder.ts +++ b/simulator/src/chip/builder.ts @@ -186,21 +186,21 @@ export async function build( fs?: FileSystem, name?: string ): Promise> { - if (name && parts.name != name) { + if (name && parts.name.value != name) { return Err({ message: `Wrong chip name`, - span: parts.nameSpan, + span: parts.name.span, }); } if (parts.parts === "BUILTIN") { - return getBuiltinChip(parts.name.toString()); + return getBuiltinChip(parts.name.value); } const buildChip = new Chip( parts.ins.map(({ pin, width }) => ({ pin: pin.toString(), width })), parts.outs.map(({ pin, width }) => ({ pin: pin.toString(), width })), - parts.name.toString(), + parts.name.value, [], parts.clocked ); @@ -209,7 +209,7 @@ export async function build( const outPins: Map> = new Map(); for (const part of parts.parts) { - const builtin = await loadChip(part.name.toString(), fs); + const builtin = await loadChip(part.name, fs); if (isErr(builtin)) { return Err({ message: `Undefined chip name: ${part.name}`, diff --git a/simulator/src/languages/hdl.test.ts b/simulator/src/languages/hdl.test.ts index 3d1c19670..8196d0086 100644 --- a/simulator/src/languages/hdl.test.ts +++ b/simulator/src/languages/hdl.test.ts @@ -265,8 +265,7 @@ describe("HDL w/ Ohm", () => { const match = grammar.match(AND_BUILTIN); expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ - name: "And", - nameSpan: { start: 5, end: 8, line: 1 }, + name: { value: "And", span: { start: 5, end: 8, line: 1 } }, ins: [ { pin: "a", width: 1 }, { pin: "b", width: 1 }, @@ -280,8 +279,7 @@ describe("HDL w/ Ohm", () => { const match = grammar.match(NOT_PARTS); expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ - name: "Not", - nameSpan: { start: 5, end: 8, line: 1 }, + name: { value: "Not", span: { start: 5, end: 8, line: 1 } }, ins: [{ pin: "in", width: 1 }], outs: [{ pin: "out", width: 1 }], parts: [ @@ -312,8 +310,7 @@ describe("HDL w/ Ohm", () => { expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ - name: "Not", - nameSpan: { start: 5, end: 8, line: 1 }, + name: { value: "Not", span: { start: 5, end: 8, line: 1 } }, ins: [{ pin: "in", width: 1 }], outs: [{ pin: "out", width: 1 }], parts: [], @@ -324,8 +321,7 @@ describe("HDL w/ Ohm", () => { const match = grammar.match(AND_16_BUILTIN); expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ - name: "And16", - nameSpan: { start: 5, end: 10, line: 1 }, + name: { value: "And16", span: { start: 5, end: 10, line: 1 } }, ins: [ { pin: "a", width: 16 }, { pin: "b", width: 16 }, @@ -339,8 +335,7 @@ describe("HDL w/ Ohm", () => { const match = grammar.match(CLOCKED); expect(match).toHaveSucceeded(); expect(hdlSemantics(match).Chip).toEqual({ - name: "Foo", - nameSpan: { start: 5, end: 8, line: 1 }, + name: { value: "Foo", span: { start: 5, end: 8, line: 1 } }, ins: [{ pin: "in", width: 1 }], outs: [], parts: [], diff --git a/simulator/src/languages/hdl.ts b/simulator/src/languages/hdl.ts index 08394b742..8dcd1c934 100644 --- a/simulator/src/languages/hdl.ts +++ b/simulator/src/languages/hdl.ts @@ -29,8 +29,7 @@ export interface Part { } export interface HdlParse { - name: string; - nameSpan: Span; + name: { value: string; span?: Span }; ins: PinDeclaration[]; outs: PinDeclaration[]; clocked: string[]; @@ -132,8 +131,7 @@ hdlSemantics.addAttribute("PinList", { hdlSemantics.addAttribute("Chip", { Chip(_a, name, _b, body, _c) { return { - name: name.sourceString, - nameSpan: span(name.source), + name: { value: name.sourceString, span: span(name.source) }, ins: body.child(0).child(0)?.child(1)?.PinList ?? [], outs: body.child(1).child(0)?.child(1)?.PinList ?? [], parts: body.child(2).PartList ?? [],