driverge-mcp 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/codegen/esp32.d.ts +4 -0
- package/dist/codegen/esp32.js +78 -0
- package/dist/codegen/esp32.js.map +1 -0
- package/dist/codegen/ident.d.ts +10 -0
- package/dist/codegen/ident.js +31 -0
- package/dist/codegen/ident.js.map +1 -0
- package/dist/codegen/index.d.ts +15 -0
- package/dist/codegen/index.js +30 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/lint.d.ts +4 -0
- package/dist/codegen/lint.js +112 -0
- package/dist/codegen/lint.js.map +1 -0
- package/dist/codegen/portable.d.ts +4 -0
- package/dist/codegen/portable.js +258 -0
- package/dist/codegen/portable.js.map +1 -0
- package/dist/codegen/stm32.d.ts +4 -0
- package/dist/codegen/stm32.js +64 -0
- package/dist/codegen/stm32.js.map +1 -0
- package/dist/codegen/types.d.ts +20 -0
- package/dist/codegen/types.js +11 -0
- package/dist/codegen/types.js.map +1 -0
- package/dist/mcp/cache.d.ts +12 -0
- package/dist/mcp/cache.js +24 -0
- package/dist/mcp/cache.js.map +1 -0
- package/dist/mcp/register.d.ts +6 -0
- package/dist/mcp/register.js +195 -0
- package/dist/mcp/register.js.map +1 -0
- package/dist/mcp/summary.d.ts +2 -0
- package/dist/mcp/summary.js +36 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/pdf/analyze.d.ts +4 -0
- package/dist/pdf/analyze.js +29 -0
- package/dist/pdf/analyze.js.map +1 -0
- package/dist/pdf/classify.d.ts +8 -0
- package/dist/pdf/classify.js +35 -0
- package/dist/pdf/classify.js.map +1 -0
- package/dist/pdf/command.d.ts +12 -0
- package/dist/pdf/command.js +96 -0
- package/dist/pdf/command.js.map +1 -0
- package/dist/pdf/extract.d.ts +2 -0
- package/dist/pdf/extract.js +47 -0
- package/dist/pdf/extract.js.map +1 -0
- package/dist/pdf/index.d.ts +11 -0
- package/dist/pdf/index.js +13 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pdf/interface-kind.d.ts +2 -0
- package/dist/pdf/interface-kind.js +46 -0
- package/dist/pdf/interface-kind.js.map +1 -0
- package/dist/pdf/manufacturer.d.ts +2 -0
- package/dist/pdf/manufacturer.js +86 -0
- package/dist/pdf/manufacturer.js.map +1 -0
- package/dist/pdf/page-map.d.ts +5 -0
- package/dist/pdf/page-map.js +52 -0
- package/dist/pdf/page-map.js.map +1 -0
- package/dist/pdf/part.d.ts +3 -0
- package/dist/pdf/part.js +41 -0
- package/dist/pdf/part.js.map +1 -0
- package/dist/pdf/register-table.d.ts +5 -0
- package/dist/pdf/register-table.js +234 -0
- package/dist/pdf/register-table.js.map +1 -0
- package/dist/pdf/table.d.ts +18 -0
- package/dist/pdf/table.js +33 -0
- package/dist/pdf/table.js.map +1 -0
- package/dist/pdf/types.d.ts +82 -0
- package/dist/pdf/types.js +3 -0
- package/dist/pdf/types.js.map +1 -0
- package/dist/schema/assemble.d.ts +4 -0
- package/dist/schema/assemble.js +48 -0
- package/dist/schema/assemble.js.map +1 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +4 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +51 -0
- package/dist/schema/types.js +7 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/validate.d.ts +2 -0
- package/dist/schema/validate.js +84 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +49 -0
- package/dist/server.js.map +1 -0
- package/package.json +59 -0
- package/schemas/.gitkeep +0 -0
- package/schemas/datasheet.schema.json +109 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mehmet Topuz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/Driverge-lockup.svg" alt="Driverge" width="360">
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center"><em>Datasheet PDF → embedded C/C++ driver, from any MCP client.</em></p>
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<img alt="license" src="https://img.shields.io/badge/license-MIT-blue">
|
|
8
|
+
<img alt="status" src="https://img.shields.io/badge/status-pre--release-orange">
|
|
9
|
+
<img alt="mcp" src="https://img.shields.io/badge/MCP-server-black">
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
> 🚧 **Pre-release** — under active development; not yet published to npm. The
|
|
13
|
+
> `npx driverge-mcp` command below is the intended install; until it's published,
|
|
14
|
+
> [run from source](#run-from-source-pre-release).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## What is Driverge?
|
|
19
|
+
|
|
20
|
+
**Driverge is a client-agnostic [MCP](https://modelcontextprotocol.io) server**
|
|
21
|
+
that turns an IC datasheet PDF into an embedded C/C++ driver. It plugs into any
|
|
22
|
+
MCP-capable host — Claude Desktop, Claude Code (VS Code), Cursor, and others.
|
|
23
|
+
|
|
24
|
+
Its guiding principle: **deterministic code parses and validates; the host AI
|
|
25
|
+
reasons.** Driverge itself contains **no internal LLM and needs no API keys** — a
|
|
26
|
+
TypeScript pipeline extracts a *validated, structured JSON* model of the chip, and
|
|
27
|
+
the host AI you're already talking to fills in the reasoning-heavy parts (init
|
|
28
|
+
sequence, vendor quirks, docs). Your datasheet never leaves your machine.
|
|
29
|
+
|
|
30
|
+
## What it does
|
|
31
|
+
|
|
32
|
+
1. **Analyze** a datasheet PDF → detect format, manufacturer, and interface kind
|
|
33
|
+
(register-map vs. command-set), then extract registers / bit-fields (or
|
|
34
|
+
commands + CRC) and the bus protocol into a **frozen JSON contract**, gated by
|
|
35
|
+
a validator.
|
|
36
|
+
2. **Generate** a driver for a target platform: a deterministic **thin-HAL
|
|
37
|
+
skeleton** — register/bit-field constants, the 5-function HAL seam, function
|
|
38
|
+
stubs — with every reasoning gap marked `TODO(driverge)` plus a `fill_in_brief`
|
|
39
|
+
telling the host AI exactly what to complete.
|
|
40
|
+
3. **Validate** the completed driver: thin-HAL purity, no leftover TODOs, register
|
|
41
|
+
references exist, bit-field masks match the JSON.
|
|
42
|
+
|
|
43
|
+
### Supported targets
|
|
44
|
+
|
|
45
|
+
Every target specializes the same portable **[thin-HAL](https://en.wikipedia.org/wiki/Hardware_abstraction_layer)**
|
|
46
|
+
seam — the driver core is identical across platforms; only the seam implementation
|
|
47
|
+
changes.
|
|
48
|
+
|
|
49
|
+
| Target | Bus binding | Language | Status |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| **Portable (thin-HAL)** | user-implemented `hal_i2c_*` / `hal_spi_*` / `hal_delay_ms` | C | ✅ |
|
|
52
|
+
| **ESP32** | ESP-IDF `i2c_master_*` | C | ✅ |
|
|
53
|
+
| **STM32** | CubeHAL `HAL_I2C_Mem_Read/Write` | C | ✅ |
|
|
54
|
+
| **Arduino** | `Wire` / `SPI` | C++ | planned |
|
|
55
|
+
|
|
56
|
+
- **Offline & private** — the datasheet is parsed locally; nothing is uploaded.
|
|
57
|
+
- **Deterministic** — the same PDF yields the same JSON and the same skeleton.
|
|
58
|
+
- **Cross-platform** — one parsed model, many target bindings.
|
|
59
|
+
|
|
60
|
+
> ⚠️ **Generated code is a strong draft, not a certified driver.** Init sequences,
|
|
61
|
+
> compensation formulas, and timing quirks are completed by the host AI and
|
|
62
|
+
> **must be reviewed** before use on hardware. Not safety-certified.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
**Prerequisites:** Node.js LTS (≥ 18).
|
|
67
|
+
|
|
68
|
+
Once published, add Driverge to your MCP client (no build step —
|
|
69
|
+
[npx](https://docs.npmjs.com/cli/commands/npx) fetches and runs it):
|
|
70
|
+
|
|
71
|
+
**Claude Desktop** — `claude_desktop_config.json`:
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Claude Code (VS Code)** — `.mcp.json` in your workspace root:
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Cursor** — `.cursor/mcp.json`:
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"driverge": { "command": "npx", "args": ["-y", "driverge-mcp"] }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Other clients (Codex, Gemini CLI, …) take the same `command` + `args` pair in
|
|
99
|
+
their own MCP config.
|
|
100
|
+
|
|
101
|
+
### Run from source (pre-release)
|
|
102
|
+
|
|
103
|
+
Until the npm package is published:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/MehmetTopuz/driverge-mcp.git
|
|
107
|
+
cd driverge-mcp
|
|
108
|
+
npm install
|
|
109
|
+
npm run build
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Then point your client at the built entry point:
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"mcpServers": {
|
|
116
|
+
"driverge": { "command": "node", "args": ["/absolute/path/to/dist/server.js"] }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Usage
|
|
122
|
+
|
|
123
|
+
Give your MCP client a datasheet and ask it to build a driver. The typical flow:
|
|
124
|
+
|
|
125
|
+
1. **`analyze_datasheet`** — `{ "pdf_path": "/abs/path/bme280.pdf" }` → returns a
|
|
126
|
+
compact summary and a `ref` handle; the full JSON is available as a resource.
|
|
127
|
+
2. **`generate_driver`** — `{ "ref": "…", "target": "portable" }` → returns the
|
|
128
|
+
driver files + a `fill_in_brief`. (`out_dir` also writes them to disk.)
|
|
129
|
+
3. The host AI completes the `TODO(driverge)` markers using the brief and the
|
|
130
|
+
`driverge://datasheet/<ref>` resource.
|
|
131
|
+
4. **`validate_driver`** — `{ "ref": "…", "files": [...] }` → static checks; loop
|
|
132
|
+
until it passes.
|
|
133
|
+
|
|
134
|
+
Reusing the same `ref` with a different `target` re-renders with **no re-parse**.
|
|
135
|
+
|
|
136
|
+
### Worked example — BME280 → portable driver
|
|
137
|
+
|
|
138
|
+
> "Analyze the datasheet at `C:/ds/bme280.pdf` with driverge, then generate a
|
|
139
|
+
> portable driver."
|
|
140
|
+
|
|
141
|
+
Driverge parses the BME280 memory map (14 registers, bit-fields, I²C address),
|
|
142
|
+
validates it, and emits `bme280.h` / `bme280.c` — register `#define`s, bit-field
|
|
143
|
+
`MASK`/`SHIFT` macros, the thin-HAL seam, and `bme280_init/read_register/
|
|
144
|
+
write_register` stubs. The host AI then fills the init sequence and compensation
|
|
145
|
+
docs from the datasheet prose.
|
|
146
|
+
|
|
147
|
+
### MCP surface
|
|
148
|
+
|
|
149
|
+
| Kind | Name | Purpose |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| Tool | `analyze_datasheet` | PDF → validated JSON, cached under a `ref` |
|
|
152
|
+
| Tool | `generate_driver` | `ref` + `target` → driver skeleton + `fill_in_brief` |
|
|
153
|
+
| Tool | `validate_driver` | static-lint a completed driver against its `ref` |
|
|
154
|
+
| Tool | `validate_datasheet` | re-run the L5 validator over a `ref` or JSON |
|
|
155
|
+
| Resource | `driverge://datasheet/<ref>` | full parsed JSON for an analyzed datasheet |
|
|
156
|
+
| Resource | `driverge://schema` | the frozen datasheet JSON-Schema contract |
|
|
157
|
+
| Prompt | `generate-driver` | guided analyze → generate → fill → validate flow |
|
|
158
|
+
|
|
159
|
+
## Roadmap
|
|
160
|
+
|
|
161
|
+
- **v0.x** — one reference sensor (BME280), portable thin-HAL core, MCP surface,
|
|
162
|
+
multiple clients. *(current)*
|
|
163
|
+
- **v0.y** — native targets: ESP32 ✅, STM32 ✅, Arduino (next).
|
|
164
|
+
- **v1.0** — multi-manufacturer coverage and a stable, versioned JSON schema.
|
|
165
|
+
|
|
166
|
+
## Contributing
|
|
167
|
+
|
|
168
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, the commit convention, and
|
|
169
|
+
the test-driven workflow. Issues and PRs welcome.
|
|
170
|
+
|
|
171
|
+
## Security & disclaimer
|
|
172
|
+
|
|
173
|
+
Generated drivers are drafts intended for human review, not certified firmware —
|
|
174
|
+
see [SECURITY.md](SECURITY.md). Driverge runs locally and does not transmit your
|
|
175
|
+
datasheets.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
[MIT](LICENSE) © Mehmet Topuz
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// ESP32 (ESP-IDF) target. Per platform-code-generation-targets, a native target
|
|
2
|
+
// does NOT replace the thin HAL — it *pre-fills* the seam. So the ESP32 driver is
|
|
3
|
+
// the exact portable core (unchanged, still platform-agnostic) PLUS a
|
|
4
|
+
// <part>_hal_esp32.c that implements hal_i2c_*/hal_delay_ms with the ESP-IDF v5
|
|
5
|
+
// i2c_master driver. The user drops all three files into an ESP-IDF component;
|
|
6
|
+
// only the board's bus/pin bring-up stays a TODO(driverge).
|
|
7
|
+
import { prefixOf, slug } from "./ident.js";
|
|
8
|
+
import { generatePortableDriver } from "./portable.js";
|
|
9
|
+
function halImpl(name, prefix) {
|
|
10
|
+
const content = [
|
|
11
|
+
"/*",
|
|
12
|
+
` * ${prefix} — ESP-IDF (i2c_master) HAL seam implementation (generated by Driverge).`,
|
|
13
|
+
" *",
|
|
14
|
+
" * Drop this beside the portable core; it fills the thin-HAL seam with the",
|
|
15
|
+
" * ESP-IDF v5 i2c_master driver. In your app: create an I2C master bus with",
|
|
16
|
+
` * i2c_new_master_bus() for your SDA/SCL pins, then call ${name}_esp32_bind()`,
|
|
17
|
+
" * once before using the driver. The driver core stays byte-for-byte portable.",
|
|
18
|
+
" */",
|
|
19
|
+
`#include "${name}.h"`,
|
|
20
|
+
"#include <string.h>",
|
|
21
|
+
'#include "driver/i2c_master.h"',
|
|
22
|
+
'#include "freertos/FreeRTOS.h"',
|
|
23
|
+
'#include "freertos/task.h"',
|
|
24
|
+
"",
|
|
25
|
+
`#ifndef ${prefix}_I2C_TIMEOUT_MS`,
|
|
26
|
+
`#define ${prefix}_I2C_TIMEOUT_MS 1000`,
|
|
27
|
+
"#endif",
|
|
28
|
+
"",
|
|
29
|
+
`static i2c_master_dev_handle_t s_${name}_dev;`,
|
|
30
|
+
"",
|
|
31
|
+
`/* Add the device (at ${prefix}_I2C_ADDR) to an existing I2C master bus and keep`,
|
|
32
|
+
" * the handle for the hal_* calls. Call once after i2c_new_master_bus(). */",
|
|
33
|
+
`esp_err_t ${name}_esp32_bind(i2c_master_bus_handle_t bus, uint32_t scl_speed_hz) {`,
|
|
34
|
+
" i2c_device_config_t cfg = {",
|
|
35
|
+
" .dev_addr_length = I2C_ADDR_BIT_LEN_7,",
|
|
36
|
+
` .device_address = ${prefix}_I2C_ADDR,`,
|
|
37
|
+
" .scl_speed_hz = scl_speed_hz,",
|
|
38
|
+
" };",
|
|
39
|
+
` return i2c_master_bus_add_device(bus, &cfg, &s_${name}_dev);`,
|
|
40
|
+
"}",
|
|
41
|
+
"",
|
|
42
|
+
"void hal_i2c_write(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len) {",
|
|
43
|
+
" uint8_t buf[33];",
|
|
44
|
+
" (void)addr; /* the device handle already encodes the bus address */",
|
|
45
|
+
" if ((size_t)len + 1u > sizeof buf) {",
|
|
46
|
+
" return;",
|
|
47
|
+
" }",
|
|
48
|
+
" buf[0] = reg;",
|
|
49
|
+
" memcpy(&buf[1], data, len);",
|
|
50
|
+
` i2c_master_transmit(s_${name}_dev, buf, (size_t)len + 1u, ${prefix}_I2C_TIMEOUT_MS);`,
|
|
51
|
+
"}",
|
|
52
|
+
"",
|
|
53
|
+
"void hal_i2c_read(uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len) {",
|
|
54
|
+
" (void)addr;",
|
|
55
|
+
` i2c_master_transmit_receive(s_${name}_dev, ®, 1, data, len, ${prefix}_I2C_TIMEOUT_MS);`,
|
|
56
|
+
"}",
|
|
57
|
+
"",
|
|
58
|
+
"void hal_delay_ms(uint32_t ms) {",
|
|
59
|
+
" vTaskDelay(pdMS_TO_TICKS(ms));",
|
|
60
|
+
"}",
|
|
61
|
+
"",
|
|
62
|
+
].join("\n");
|
|
63
|
+
return { path: `${name}_hal_esp32.c`, content };
|
|
64
|
+
}
|
|
65
|
+
/** Portable core + an ESP-IDF seam implementation for the same part. */
|
|
66
|
+
export function generateEsp32Driver(json) {
|
|
67
|
+
const name = slug(json.metadata.part);
|
|
68
|
+
const prefix = prefixOf(json.metadata.part);
|
|
69
|
+
const base = generatePortableDriver(json);
|
|
70
|
+
return {
|
|
71
|
+
files: [...base.files, halImpl(name, prefix)],
|
|
72
|
+
fill_in_brief: {
|
|
73
|
+
...base.fill_in_brief,
|
|
74
|
+
hal_setup_todo: `Bring up the ESP-IDF I2C master bus (i2c_new_master_bus with your SDA/SCL GPIOs and port), then call ${name}_esp32_bind(bus, 400000) before the first driver call. Tune ${prefix}_I2C_TIMEOUT_MS if needed.`,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=esp32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esp32.js","sourceRoot":"","sources":["../../src/codegen/esp32.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,kFAAkF;AAClF,sEAAsE;AACtE,gFAAgF;AAChF,+EAA+E;AAC/E,4DAA4D;AAG5D,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAGvD,SAAS,OAAO,CAAC,IAAY,EAAE,MAAc;IAC3C,MAAM,OAAO,GAAG;QACd,IAAI;QACJ,MAAM,MAAM,0EAA0E;QACtF,IAAI;QACJ,4EAA4E;QAC5E,6EAA6E;QAC7E,4DAA4D,IAAI,eAAe;QAC/E,gFAAgF;QAChF,KAAK;QACL,aAAa,IAAI,KAAK;QACtB,qBAAqB;QACrB,gCAAgC;QAChC,gCAAgC;QAChC,4BAA4B;QAC5B,EAAE;QACF,WAAW,MAAM,iBAAiB;QAClC,WAAW,MAAM,sBAAsB;QACvC,QAAQ;QACR,EAAE;QACF,oCAAoC,IAAI,OAAO;QAC/C,EAAE;QACF,yBAAyB,MAAM,mDAAmD;QAClF,6EAA6E;QAC7E,aAAa,IAAI,mEAAmE;QACpF,iCAAiC;QACjC,gDAAgD;QAChD,6BAA6B,MAAM,YAAY;QAC/C,uCAAuC;QACvC,QAAQ;QACR,sDAAsD,IAAI,QAAQ;QAClE,GAAG;QACH,EAAE;QACF,8EAA8E;QAC9E,sBAAsB;QACtB,yEAAyE;QACzE,0CAA0C;QAC1C,iBAAiB;QACjB,OAAO;QACP,mBAAmB;QACnB,iCAAiC;QACjC,6BAA6B,IAAI,gCAAgC,MAAM,mBAAmB;QAC1F,GAAG;QACH,EAAE;QACF,6EAA6E;QAC7E,iBAAiB;QACjB,qCAAqC,IAAI,6BAA6B,MAAM,mBAAmB;QAC/F,GAAG;QACH,EAAE;QACF,kCAAkC;QAClC,oCAAoC;QACpC,GAAG;QACH,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,cAAc,EAAE,OAAO,EAAE,CAAC;AAClD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,IAAmB;IACrD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAE1C,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,aAAa,EAAE;YACb,GAAG,IAAI,CAAC,aAAa;YACrB,cAAc,EAAE,wGAAwG,IAAI,+DAA+D,MAAM,4BAA4B;SAC9N;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const HEX_ONLY: RegExp;
|
|
2
|
+
/** Lower-snake identifier safe for filenames/symbols; "device" when empty. */
|
|
3
|
+
export declare function slug(part: string): string;
|
|
4
|
+
/** UPPER token safe for a macro fragment. */
|
|
5
|
+
export declare function macro(s: string): string;
|
|
6
|
+
/** 8-bit mask for a [msb:lsb] field. */
|
|
7
|
+
export declare function fieldMask(msb: number, lsb: number): number;
|
|
8
|
+
export declare function hex2(n: number): string;
|
|
9
|
+
/** Macro prefix for a part, e.g. "BME280". */
|
|
10
|
+
export declare function prefixOf(part: string): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Shared naming/geometry helpers so codegen and the driver lint agree on exactly
|
|
2
|
+
// how a part name and its fields become C identifiers and mask values.
|
|
3
|
+
export const HEX_ONLY = /^0x[0-9a-f]+$/i;
|
|
4
|
+
/** Lower-snake identifier safe for filenames/symbols; "device" when empty. */
|
|
5
|
+
export function slug(part) {
|
|
6
|
+
const s = part
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
9
|
+
.replace(/^_+|_+$/g, "");
|
|
10
|
+
return s || "device";
|
|
11
|
+
}
|
|
12
|
+
/** UPPER token safe for a macro fragment. */
|
|
13
|
+
export function macro(s) {
|
|
14
|
+
return s
|
|
15
|
+
.toUpperCase()
|
|
16
|
+
.replace(/[^A-Z0-9]+/g, "_")
|
|
17
|
+
.replace(/^_+|_+$/g, "");
|
|
18
|
+
}
|
|
19
|
+
/** 8-bit mask for a [msb:lsb] field. */
|
|
20
|
+
export function fieldMask(msb, lsb) {
|
|
21
|
+
const width = msb - lsb + 1;
|
|
22
|
+
return (((1 << width) - 1) << lsb) & 0xff;
|
|
23
|
+
}
|
|
24
|
+
export function hex2(n) {
|
|
25
|
+
return `0x${n.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
26
|
+
}
|
|
27
|
+
/** Macro prefix for a part, e.g. "BME280". */
|
|
28
|
+
export function prefixOf(part) {
|
|
29
|
+
return macro(slug(part));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ident.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ident.js","sourceRoot":"","sources":["../../src/codegen/ident.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,uEAAuE;AAEvE,MAAM,CAAC,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,MAAM,CAAC,GAAG,IAAI;SACX,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,QAAQ,CAAC;AACvB,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,KAAK,CAAC,CAAS;IAC7B,OAAO,CAAC;SACL,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW;IAChD,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,CAAS;IAC5B,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DatasheetJson } from "../schema/types.js";
|
|
2
|
+
import type { CodegenTarget, DriverArtifact } from "./types.js";
|
|
3
|
+
export * from "./types.js";
|
|
4
|
+
export { generatePortableDriver } from "./portable.js";
|
|
5
|
+
export { generateEsp32Driver } from "./esp32.js";
|
|
6
|
+
export { generateStm32Driver } from "./stm32.js";
|
|
7
|
+
export { lintDriver } from "./lint.js";
|
|
8
|
+
export { slug, prefixOf } from "./ident.js";
|
|
9
|
+
/** arduino lands in a later session; portable + esp32 + stm32 render today. */
|
|
10
|
+
export declare class UnsupportedTargetError extends Error {
|
|
11
|
+
readonly target: string;
|
|
12
|
+
constructor(target: string);
|
|
13
|
+
}
|
|
14
|
+
/** Render a driver for a target, dispatching to the right generator. */
|
|
15
|
+
export declare function generateDriver(json: DatasheetJson, target: CodegenTarget): DriverArtifact;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Public surface of the codegen layer.
|
|
2
|
+
import { generateEsp32Driver } from "./esp32.js";
|
|
3
|
+
import { generatePortableDriver } from "./portable.js";
|
|
4
|
+
import { generateStm32Driver } from "./stm32.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
export { generatePortableDriver } from "./portable.js";
|
|
7
|
+
export { generateEsp32Driver } from "./esp32.js";
|
|
8
|
+
export { generateStm32Driver } from "./stm32.js";
|
|
9
|
+
export { lintDriver } from "./lint.js";
|
|
10
|
+
export { slug, prefixOf } from "./ident.js";
|
|
11
|
+
/** arduino lands in a later session; portable + esp32 + stm32 render today. */
|
|
12
|
+
export class UnsupportedTargetError extends Error {
|
|
13
|
+
target;
|
|
14
|
+
constructor(target) {
|
|
15
|
+
super(`codegen target "${target}" is not available yet — supported targets: portable, esp32, stm32`);
|
|
16
|
+
this.target = target;
|
|
17
|
+
this.name = "UnsupportedTargetError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Render a driver for a target, dispatching to the right generator. */
|
|
21
|
+
export function generateDriver(json, target) {
|
|
22
|
+
if (target === "portable")
|
|
23
|
+
return generatePortableDriver(json);
|
|
24
|
+
if (target === "esp32")
|
|
25
|
+
return generateEsp32Driver(json);
|
|
26
|
+
if (target === "stm32")
|
|
27
|
+
return generateStm32Driver(json);
|
|
28
|
+
throw new UnsupportedTargetError(target);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/codegen/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAGvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE5C,+EAA+E;AAC/E,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IACnB;IAA5B,YAA4B,MAAc;QACxC,KAAK,CACH,mBAAmB,MAAM,oEAAoE,CAC9F,CAAC;QAHwB,WAAM,GAAN,MAAM,CAAQ;QAIxC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAC5B,IAAmB,EACnB,MAAqB;IAErB,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DatasheetJson, ValidationResult } from "../schema/types.js";
|
|
2
|
+
import type { GeneratedFile } from "./types.js";
|
|
3
|
+
/** Static validation of a completed driver against its source datasheet JSON. */
|
|
4
|
+
export declare function lintDriver(files: GeneratedFile[], json: DatasheetJson): ValidationResult;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// validate_driver — static lint over the COMPLETED driver files (see wiki:
|
|
2
|
+
// mcp-tool-usage-flow, thin-hal-non-negotiable). Complements the generated
|
|
3
|
+
// skeleton: it confirms the host AI finished its work (no leftover TODOs), stayed
|
|
4
|
+
// on the thin-HAL seam, didn't hallucinate registers/commands, and didn't corrupt
|
|
5
|
+
// the deterministic bit-field masks. Pure/text-based — no compiler needed.
|
|
6
|
+
import { fieldMask, hex2, macro, prefixOf } from "./ident.js";
|
|
7
|
+
// Vendor peripheral APIs that must NEVER appear in a portable/thin-HAL driver —
|
|
8
|
+
// all bus access goes through the hal_* seam instead.
|
|
9
|
+
const FORBIDDEN = [
|
|
10
|
+
{ re: /\bHAL_(?:I2C|SPI|UART)_\w+/, api: "STM32 CubeHAL" },
|
|
11
|
+
{ re: /\bLL_(?:I2C|SPI)_\w+/, api: "STM32 LL" },
|
|
12
|
+
{ re: /\bi2c_master_\w+|\bspi_device_\w+|\bi2c_cmd_\w+/, api: "ESP-IDF" },
|
|
13
|
+
{ re: /\bWire\.\w+|\bSPI\.(?:transfer|begin)\w*/, api: "Arduino" },
|
|
14
|
+
];
|
|
15
|
+
// Allowed thin-HAL seam functions (families).
|
|
16
|
+
const HAL_ALLOWED = /^hal_(?:i2c_(?:read|write)|spi_(?:read|write)|delay_ms)$/;
|
|
17
|
+
function balanced(text, open, close) {
|
|
18
|
+
let depth = 0;
|
|
19
|
+
for (const ch of text) {
|
|
20
|
+
if (ch === open)
|
|
21
|
+
depth++;
|
|
22
|
+
else if (ch === close) {
|
|
23
|
+
depth--;
|
|
24
|
+
if (depth < 0)
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return depth === 0;
|
|
29
|
+
}
|
|
30
|
+
/** Static validation of a completed driver against its source datasheet JSON. */
|
|
31
|
+
export function lintDriver(files, json) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
const warnings = [];
|
|
34
|
+
const prefix = prefixOf(json.metadata.part);
|
|
35
|
+
const all = files.map((f) => `/* ${f.path} */\n${f.content}`).join("\n\n");
|
|
36
|
+
// Comments carry the TODO markers and prose; strip them before code-shape checks.
|
|
37
|
+
const strip = (s) => s.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
|
|
38
|
+
const code = strip(all);
|
|
39
|
+
// Thin-HAL purity applies to the driver CORE, not the seam implementation:
|
|
40
|
+
// a native target's <part>_hal_<target>.c is *where* platform calls belong.
|
|
41
|
+
const isHalImpl = (p) => /_hal_[a-z0-9]+\.(?:c|cpp)$/i.test(p);
|
|
42
|
+
const coreCode = strip(files.filter((f) => !isHalImpl(f.path)).map((f) => f.content).join("\n"));
|
|
43
|
+
// 1. No leftover TODO(driverge) — the host AI must complete every marker.
|
|
44
|
+
const todos = all.match(/TODO\(driverge\)/g);
|
|
45
|
+
if (todos) {
|
|
46
|
+
errors.push(`${todos.length} unfinished TODO(driverge) marker(s) remain`);
|
|
47
|
+
}
|
|
48
|
+
// 2. Parse-sanity: balanced braces/parens (over comment-stripped code).
|
|
49
|
+
if (!balanced(code, "{", "}"))
|
|
50
|
+
errors.push("unbalanced braces { }");
|
|
51
|
+
if (!balanced(code, "(", ")"))
|
|
52
|
+
errors.push("unbalanced parentheses ( )");
|
|
53
|
+
// 3. Thin-HAL purity — no vendor peripheral APIs in the core; only the hal_* seam.
|
|
54
|
+
for (const { re, api } of FORBIDDEN) {
|
|
55
|
+
if (re.test(coreCode))
|
|
56
|
+
errors.push(`direct ${api} call — bus access must go through the hal_* seam`);
|
|
57
|
+
}
|
|
58
|
+
for (const m of coreCode.matchAll(/\bhal_[a-z0-9_]+/g)) {
|
|
59
|
+
if (!HAL_ALLOWED.test(m[0])) {
|
|
60
|
+
errors.push(`unknown HAL function "${m[0]}" — thin HAL is hal_i2c_*/hal_spi_*/hal_delay_ms only`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 4. Register/command reference existence: any PREFIX_REG_* / PREFIX_CMD_* used
|
|
64
|
+
// in code must be defined (in the header) — catches hallucinated symbols.
|
|
65
|
+
const defined = new Set();
|
|
66
|
+
for (const m of all.matchAll(/#define\s+([A-Z0-9_]+)/g))
|
|
67
|
+
defined.add(m[1]);
|
|
68
|
+
const refRe = new RegExp(`\\b${prefix}_(?:REG|CMD)_[A-Z0-9_]+`, "g");
|
|
69
|
+
const referenced = new Set();
|
|
70
|
+
for (const m of code.matchAll(refRe))
|
|
71
|
+
referenced.add(m[0]);
|
|
72
|
+
for (const ref of referenced) {
|
|
73
|
+
if (!defined.has(ref))
|
|
74
|
+
errors.push(`undefined register/command constant "${ref}"`);
|
|
75
|
+
}
|
|
76
|
+
// 5. Bit-field mask/shift consistency with the JSON geometry.
|
|
77
|
+
if (json.interface.kind === "register_map") {
|
|
78
|
+
checkMasks(json.interface.registers, prefix, all, errors);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
checkCommandCodes(json.interface.commands, prefix, all, warnings);
|
|
82
|
+
}
|
|
83
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
84
|
+
}
|
|
85
|
+
function checkMasks(registers, prefix, text, errors) {
|
|
86
|
+
for (const r of registers) {
|
|
87
|
+
for (const f of r.bitFields) {
|
|
88
|
+
const base = `${prefix}_${macro(r.name)}_${macro(f.name)}`;
|
|
89
|
+
const maskDef = new RegExp(`#define\\s+${base}_MASK\\s+(0x[0-9a-fA-F]+)`).exec(text);
|
|
90
|
+
if (maskDef) {
|
|
91
|
+
const got = Number.parseInt(maskDef[1], 16);
|
|
92
|
+
const want = fieldMask(f.msb, f.lsb);
|
|
93
|
+
if (got !== want) {
|
|
94
|
+
errors.push(`${base}_MASK is ${maskDef[1]} but ${r.name}.${f.name} [${f.msb}:${f.lsb}] implies ${hex2(want)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const shiftDef = new RegExp(`#define\\s+${base}_SHIFT\\s+(\\d+)`).exec(text);
|
|
98
|
+
if (shiftDef && Number(shiftDef[1]) !== f.lsb) {
|
|
99
|
+
errors.push(`${base}_SHIFT is ${shiftDef[1]} but ${r.name}.${f.name} lsb is ${f.lsb}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function checkCommandCodes(commands, prefix, text, warnings) {
|
|
105
|
+
for (const c of commands) {
|
|
106
|
+
const def = new RegExp(`#define\\s+${prefix}_CMD_${macro(c.name)}\\s+(0x[0-9a-fA-F]+)`).exec(text);
|
|
107
|
+
if (def && def[1].toUpperCase() !== c.code.toUpperCase()) {
|
|
108
|
+
warnings.push(`${prefix}_CMD_${macro(c.name)} is ${def[1]} but JSON code is ${c.code}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/codegen/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DatasheetJson } from "../schema/types.js";
|
|
2
|
+
import type { DriverArtifact } from "./types.js";
|
|
3
|
+
/** Render the portable thin-HAL driver skeleton for a validated datasheet. */
|
|
4
|
+
export declare function generatePortableDriver(json: DatasheetJson): DriverArtifact;
|