Skip to content
Merged
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
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ them on source ports:
| `isUsingInternalPulldown: true` | `is_using_internal_pulldown: true` | Input `pull = "Pull Down"` |
| `activeCapability: "i2c_sda"` / `"i2c_scl"` | `is_configured_for_i2c_sda` / `is_configured_for_i2c_scl` | I2C0 SDA/SCL on physical RGE pins 3/19 |

`isGpio`, `supports_*`, and `can_use_*` describe capabilities; they do not activate
an instance. A GPIO requires exactly one input/output flag and no bidirectional
declaration. I2C accepts open-drain and bidirectional declarations, but rejects
`isGpio`, `isBidirectional`, `supports_*`, and `can_use_*` describe capabilities;
they do not activate an instance. Bidirectionality can coexist with an explicit
input/output choice; a GPIO still requires exactly one input/output flag.
Unselected SWD and connected LF crystal pins retain their native ownership when
bidirectional capability metadata is present. I2C accepts open-drain and
bidirectional declarations, but rejects
a conflicting GPIO direction. Selected electrical modes must agree with the
native driver. Tri-state, open-collector/emitter, open-drain GPIO, SPI and UART
are currently unsupported and fail instead of being dropped.
Expand All @@ -45,10 +48,11 @@ Output names are deterministic (`CONFIG_U1_PIN5`, `CONFIG_U1_I2C0`), and pins ar
sorted by physical identity. No behavior is inferred from signal or net names.
Unconnected capability-only ports remain unallocated. Connected physical GPIO
pins without a supported function are reported together, even if all their
attributes (including `is_gpio`) are missing. Errors identify the MCU name/part number and physical pin labels (for example,
`U1 pin 4 (DIO11)`), without generated record IDs. They explain which existing
TSX attributes to set and distinguish datasheet capabilities from the board
function that must be selected.
attributes (including `is_gpio`) are missing. Missing-direction errors contain
one short line per pin with the component name, physical pin number, label and
problem, for example `- U1 pin 4 (DIO11): GPIO direction is missing`.
These diagnostics omit manufacturer part numbers, generated record IDs and TSX
setup instructions.
An MCU with no source ports, or a trace referencing a missing source-port record,
fails before any output is available. An incomplete I2C selection reports the
selected endpoints and asks for both SDA and SCL declarations. Passives and other
Expand Down
12 changes: 3 additions & 9 deletions lib/cc2340/derive-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ export function deriveCc2340Requests(
)
const isSda = port.is_configured_for_i2c_sda === true
const isScl = port.is_configured_for_i2c_scl === true
const selectedGpio =
port.is_input === true ||
port.is_output === true ||
port.is_bidirectional === true
const selectedGpio = port.is_input === true || port.is_output === true
const selectedElectricalMode = selectedElectricalModes.some(
(attribute) => port[attribute] === true,
)
Expand Down Expand Up @@ -149,12 +146,11 @@ export function deriveCc2340Requests(
}
if (!connected && !selectedGpio && !selectedElectricalMode) continue
if (
port.is_bidirectional ||
port.is_input === port.is_output ||
(!port.is_input && !port.is_output)
) {
missing.push(
`${identity}: ${port.is_bidirectional ? "bidirectional GPIO is unsupported" : port.is_input && port.is_output ? "both isInput and isOutput are selected" : "no GPIO direction or supported peripheral selected"}`,
`${identity}: ${port.is_input && port.is_output ? "both isInput and isOutput are selected" : "GPIO direction is missing"}`,
)
continue
}
Expand All @@ -174,9 +170,7 @@ export function deriveCc2340Requests(
resolved.gpios.push({ request, pin: physical.pin })
}
if (missing.length)
throw new Error(
`Unresolved CC2340 pin configuration for ${componentIdentity}:\n${missing.map((problem) => `- ${problem}`).join("\n")}\nUpdate ${component.name}'s TSX pinAttributes with the intended function for each listed pin: set exactly one of isInput: true or isOutput: true for GPIO, or activeCapability: "i2c_sda" / "i2c_scl" for I2C.\nDatasheet capabilities such as isGpio describe what a pin supports; they do not select how this board uses it.`,
)
throw new Error(missing.map((problem) => `- ${problem}`).join("\n"))
if (sdaPorts.length || sclPorts.length) {
const sda = sdaPorts[0]
const scl = sclPorts[0]
Expand Down
2 changes: 1 addition & 1 deletion lib/cc2340/resolve-port.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function checkCc2340Function(
// TI's I2CLPF3 driver configures both pins with GPIO_CFG_OUT_OD_PU.
// Open drain is compatible with I2C, but not our standard GPIO output mode.
if (role === "gpio") {
forbidden.push("is_using_open_drain", "is_bidirectional")
forbidden.push("is_using_open_drain")
if (typeof request !== "string")
forbidden.push(request.direction === "input" ? "is_output" : "is_input")
} else {
Expand Down
14 changes: 14 additions & 0 deletions tests/cc2340-conversion.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ test("actual board emits three GPIOs and the exact I2C identifiers", () => {
)
})

test("explicit requests preserve GPIO and I2C settings with bidirectional capabilities", () => {
const enriched = circuit.map((element) =>
element.type === "source_port" &&
element.source_component_id === pedometerOptions.source_component_id
? { ...element, is_bidirectional: true }
: element,
)
const before = structuredClone(enriched)
expect(
convertCircuitJsonToSysConfig(enriched, pedometerOptions).getString(),
).toBe(output().getString())
expect(enriched).toEqual(before)
})

test("100000 bits/s becomes numeric 100 kbit/s through serialization and re-parsing", async () => {
const options = structuredClone(pedometerOptions)
expect(options.i2c?.max_bit_rate).toBe(100000)
Expand Down
11 changes: 10 additions & 1 deletion tests/derived-cc2340-lf-crystal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ test("direct traces and swapped interchangeable crystal terminals resolve the sa
)
})

test("bidirectional capabilities do not claim connected LF crystal pins", () => {
const circuitJson = crystalCircuit()
const baseline = convertCircuitJsonToSysConfig(circuitJson).getString()
for (const pin_number of [4, 14, 15])
mcuPort(circuitJson, pin_number).is_bidirectional = true
const before = structuredClone(circuitJson)
expect(convertCircuitJsonToSysConfig(circuitJson).getString()).toBe(baseline)
expect(circuitJson).toEqual(before)
})

test("missing crystal terminals and incomplete wiring remain unresolved", () => {
for (const source_trace_id of [
"crystal_p_trace",
Expand Down Expand Up @@ -202,7 +212,6 @@ test("multiple connected crystals are ambiguous", () => {
for (const declarations of [
{ is_output: true },
{ is_input: true },
{ is_bidirectional: true },
{ is_configured_for_i2c_sda: true },
{ is_using_internal_pullup: true },
{ is_using_open_drain: true },
Expand Down
59 changes: 44 additions & 15 deletions tests/derived-cc2340.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,12 @@ test("an MCU with no pin records gets an actionable component error", () => {
})

test("pins with no attributes report every connected physical pin", () => {
expect(() =>
convertCircuitJsonToSysConfig(
connected([{ pin_number: 5 }, { pin_number: 6 }]),
),
).toThrow(
/U1 \(CC2340R52E0RGER\)[\s\S]*U1 pin 5 \(signal_0\)[\s\S]*U1 pin 6 \(signal_1\)[\s\S]*isInput: true or isOutput: true/,
)
expect(
conversionError(connected([{ pin_number: 5 }, { pin_number: 6 }])),
).toMatchInlineSnapshot(`
"- U1 pin 5 (signal_0): GPIO direction is missing
- U1 pin 6 (signal_1): GPIO direction is missing"
`)
})

test("one configured pin cannot hide another pin's missing attributes", () => {
Expand Down Expand Up @@ -215,6 +214,7 @@ test("unused capabilities do not activate GPIO or I2C", () => {
{
pin_number: 6,
is_gpio: true,
is_bidirectional: true,
supports_i2c_sda: true,
can_use_open_drain: true,
},
Expand All @@ -227,8 +227,7 @@ test("unused capabilities do not activate GPIO or I2C", () => {

for (const flags of [
{ is_input: true, is_output: true },
{ is_bidirectional: true },
{ is_output: true, is_bidirectional: true },
{ is_input: true, is_output: true, is_bidirectional: true },
{ is_output: true, is_using_open_drain: true },
{ is_output: true, is_using_tri_state: true },
{ is_output: true, is_using_open_collector: true },
Expand Down Expand Up @@ -278,6 +277,39 @@ test("unpaired, duplicate, conflicting and unsupported I2C routes fail", () => {
expect(() => convertCircuitJsonToSysConfig(circuit(ports))).toThrow()
})

test("bidirectional capabilities preserve selected GPIO directions and SWD ownership", () => {
const input = connected([
{ pin_number: 4, name: "DIO11", is_output: true },
{ pin_number: 5, name: "DIO12", is_input: true },
{ pin_number: 7, name: "DIO16_SWDIO", is_gpio: true },
{ pin_number: 8, name: "DIO17_SWDCK", is_gpio: true },
])
const baseline = convertCircuitJsonToSysConfig(input).getString()
const enriched = input.map((element) =>
element.type === "source_port"
? { ...element, is_bidirectional: true }
: element,
)
const before = structuredClone(enriched)
expect(convertCircuitJsonToSysConfig(enriched).getString()).toBe(baseline)
expect(enriched).toEqual(before)
})

test("a connected bidirectional capability still requires a board direction", () => {
const input = connected([
{ pin_number: 4, name: "DIO11", is_output: true },
{ pin_number: 5, name: "DIO12", is_bidirectional: true, is_gpio: true },
])
expect(conversionError(input)).toMatchInlineSnapshot(
`"- U1 pin 5 (DIO12): GPIO direction is missing"`,
)
const converter = new CircuitJsonToSysConfigConverter(input)
converter.step()
expect(() => converter.step()).toThrow(/U1 pin 5/)
expect(converter.finished).toBe(false)
expect(() => converter.getOutput()).toThrow("must finish")
})

test("SDK debug ownership and LF crystal conflicts are explicit", () => {
const debug = convertCircuitJsonToSysConfig(
connected([
Expand Down Expand Up @@ -368,7 +400,7 @@ test("three stages snapshot inputs, stay deterministic and expose no partial out

test("the unmodified pedometer cannot acquire firmware choices from its name", () => {
expect(() => convertCircuitJsonToSysConfig(pedometer)).toThrow(
/Unresolved CC2340 pin configuration/,
/GPIO direction is missing/,
)
})

Expand Down Expand Up @@ -446,11 +478,8 @@ test("pin errors use circuit labels and stay unchanged when generated IDs change
const message = conversionError(input)
expect(conversionError(renamed)).toBe(message)
expect(message).toMatchInlineSnapshot(`
"Unresolved CC2340 pin configuration for U1 (CC2340R52E0RGER):
- U1 pin 4 (DIO11): no GPIO direction or supported peripheral selected
- U1 pin 6 (DIO13): no GPIO direction or supported peripheral selected
Update U1's TSX pinAttributes with the intended function for each listed pin: set exactly one of isInput: true or isOutput: true for GPIO, or activeCapability: "i2c_sda" / "i2c_scl" for I2C.
Datasheet capabilities such as isGpio describe what a pin supports; they do not select how this board uses it."
"- U1 pin 4 (DIO11): GPIO direction is missing
- U1 pin 6 (DIO13): GPIO direction is missing"
`)
expect(message).not.toMatch(
/source_component_123|source_trace_456|mcu|port_0|port_1/,
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/derived-cc2340/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ firmware application preset. It declares output pin 5, input pin 6 with an
internal pull-up, and I2C SDA/SCL pins 3/19 using the existing pin attributes.
Physical identities use the converter's independently validated RGE target.
No startup level, interrupt, bitrate, LF clock or RTOS choice is declared.
The MCU ports also carry bidirectional capability flags, as published for
CC2340R52E0RGER. These coexist with the selected GPIO/I2C functions and do not
select a function themselves.

`bun run validate:cc2340` converts it without request options, runs TI SysConfig
1.28.1+4785 against official SimpleLink F3 SDK tag `lpf3-9.21.00.36_LTS`, and
Expand Down
12 changes: 8 additions & 4 deletions tests/fixtures/derived-cc2340/circuit.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"name": "SDA",
"pin_number": 3,
"is_configured_for_i2c_sda": true,
"is_using_open_drain": true
"is_using_open_drain": true,
"is_bidirectional": true
},
{
"type": "source_port",
Expand All @@ -23,7 +24,8 @@
"pin_number": 5,
"is_gpio": true,
"is_output": true,
"is_using_push_pull": true
"is_using_push_pull": true,
"is_bidirectional": true
},
{
"type": "source_port",
Expand All @@ -33,7 +35,8 @@
"pin_number": 6,
"is_gpio": true,
"is_input": true,
"is_using_internal_pullup": true
"is_using_internal_pullup": true,
"is_bidirectional": true
},
{
"type": "source_port",
Expand All @@ -42,6 +45,7 @@
"name": "SCL",
"pin_number": 19,
"is_configured_for_i2c_scl": true,
"is_using_open_drain": true
"is_using_open_drain": true,
"is_bidirectional": true
}
]
9 changes: 6 additions & 3 deletions tests/fixtures/derived-cc2340/lf-crystal.circuit.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@
"source_component_id": "mcu",
"name": "DIO11",
"pin_number": 4,
"is_output": true
"is_output": true,
"is_bidirectional": true
},
{
"type": "source_port",
"source_port_id": "lf_p",
"source_component_id": "mcu",
"name": "DIO3_X32P",
"pin_number": 14,
"is_gpio": true
"is_gpio": true,
"is_bidirectional": true
},
{
"type": "source_port",
"source_port_id": "lf_n",
"source_component_id": "mcu",
"name": "DIO4_X32N",
"pin_number": 15,
"is_gpio": true
"is_gpio": true,
"is_bidirectional": true
},
{
"type": "source_component",
Expand Down
Loading