Metadata-Version: 2.5
Name: mak-ide
Version: 0.3.0
Summary: An instrument workbench for the MAK8u dual-core microcontroller
Project-URL: Homepage, https://github.com/Manikanta25055/MAK8u
Project-URL: Source, https://github.com/Manikanta25055/MAK8u/tree/main/tools/maku-tui
License-Expression: Apache-2.0
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: pyserial>=3.5
Requires-Dist: textual<1.0,>=0.79
Provides-Extra: assistant
Requires-Dist: openai>=1.40; extra == 'assistant'
Description-Content-Type: text/markdown

# MAK IDE

An instrument workbench for the **MAK8u**, a dual-core deterministic
microcontroller on a Nexys A7 (Artix-7). It runs in a terminal.

```
mak
```

---

## Read this first: there is no debugger, and there cannot be one

The obvious ask for a microcontroller IDE is Keil, and Keil's value is
overwhelmingly its **debugger** — halt the core, single-step, set a breakpoint,
watch registers and memory live.

> **The MAK8u has no debug interface.** There is no halt, no single-step, no
> breakpoint, no register readback, and no way for the host to read target
> memory. The only host-to-target channel is one UART.

That is the hardware as built, not a gap in this tool. A tool that implied
otherwise would be promising a product that cannot exist on this silicon.

What the machine does offer, and what this workbench is built on:

| channel | direction | carries |
|---|---|---|
| UART bootloader protocol | host → target | a whole program image, magic-framed and checksummed |
| UART, program-driven | target → host | whatever the running program chooses to transmit |
| 16 LEDs | target → eye | heartbeats, lock, reset, per-core retire, error stickies |
| 8-digit seven-segment | target → eye | one of eight values, selected by `sw[5:3]` |
| `sw[7]`, `sw[6]` | hand → target | boot-witness probe; priming defeat |
| boot witness | target → program | what the flash reader saw, readable only *by a program on the target* |

**Every observation of internal state is made by a program you load.** The host
never reads the machine; it reads what a program on the machine says. So this is
an instrument workbench, not a debugger — which turns out to suit the device,
because its whole method is purpose-built witness images.

---

## What works

| feature | state |
|---|---|
| Editor with live assembler diagnostics | **works** — line and, where the assembler quotes a token, column |
| Build to a `.mem` image | **works** |
| Image inspector and disassembler | **works** — what is at word N, and which source line put it there |
| Load over UART, run, monitor | **works** — hex/ascii, pause, save log |
| Instruments pane, seventeen `read_*_hw.py` readers | **works**, one command each, globbed from disk |
| Boot-witness reader | **works** — refuses a short dump rather than decoding it |
| Self-boot regression checklist | **works** — and reports INCONCLUSIVE, never green, on a one-sided run |
| Command line, three layout modes | **works** — `/` lists everything, by name |
| Program the FPGA or its flash from macOS | **not shipped** — see below |
| RTL regression suite | **impossible here** — Vivado has no macOS build |
| Source-level debugger | **impossible** — see above |
| Assistant pane | **works** — NVIDIA NIM, grounded in the repository, citing every fact |
| Project window, file operations, documentation panel | **works** |

### What it looks like

Work mode, with the assembler's verdict under the token it quoted — `R9` is
not a register this machine has:

![Work mode: the editor, the explorer, and one diagnostic](docs/work.svg)

`/` on its own, listing every command this project has, including one per
`read_*_hw.py` found on disk:

![The command line, open and listing](docs/commands.svg)

The Welcome screen:

![The Welcome screen: recent projects, documentation, the port](docs/welcome.svg)

These are exported from a real run of the app, not drawn by hand, by
`docs/screenshots.py` — and `tests/test_screenshots.py` re-renders them and
fails when the app stops drawing what they show. A screenshot is the one claim
in a repository that rots without anyone noticing, because a picture cannot
fail a test.

### Programming the board

Flashing is **not part of this tool on macOS**. Vivado has no macOS build, and
while `openFPGALoader` exists and installs cleanly, this checkout has never
programmed a board with it. Bitstreams are built on the Windows box, and
`*.bit` is not committed. Treat macOS flashing as unproven until there is a
measurement saying otherwise.

The board **boots from its own flash**, so day-to-day work needs no programming
at all: load an image over UART and run it.

---

## Install

**The command is `mak`. The distribution is `mak-ide`** — `mak` on PyPI is an
unrelated package, and a console script's name is independent of the
distribution that installs it.

**Published.** [pypi.org/project/mak-ide](https://pypi.org/project/mak-ide/).
`.github/workflows/release.yaml` builds, checks and clean-installs the wheel on
every push, and uploads only from a manual run with the word `publish` typed
into it, over Trusted Publishing with no stored token.

### Install

```bash
uv tool install mak-ide       # recommended: isolated, one command on PATH
pipx install mak-ide          # the same thing, if you already use pipx
pip install mak-ide           # only inside a virtualenv you already manage
```

macOS and Linux, in one line:

```bash
curl -LsSf https://raw.githubusercontent.com/Manikanta25055/MAK8u/main/tools/maku-tui/install.sh | sh
```

Windows PowerShell:

```powershell
irm https://raw.githubusercontent.com/Manikanta25055/MAK8u/main/tools/maku-tui/install.ps1 | iex
```

Both scripts find `uv` or install it, run `uv tool install mak-ide`, and tell
you if the directory they installed into is not on your `PATH` — rather than
leaving you with a command that "did not install". Neither needs `sudo` or
administrator rights. `uv tool uninstall mak-ide` undoes either.

**The one-liners fetch from a URL, and this repository is private.** They
return 404 for anyone who cannot read it, including you on a machine without
credentials, until the repository is public.

Making them work means making this repository public, which exposes the RTL,
the specs and the measurements — a much larger step than publishing the
package was. `uv tool install mak-ide` needs none of it and works today.

`mak --version` says which one you have. `mak --doctor` says why it will not
start.

### From a clone, today

Pick the path that matches what you are doing — they give you two different
things and the difference is the point.

### To use it

```bash
uv tool install ./tools/maku-tui
```

or, equivalently:

```bash
pipx install ./tools/maku-tui
```

Either one puts **`mak`** on your `PATH` — one command, not three. Both were
verified on this checkout — `uv` on Python 3.13, `pipx` on 3.14. This is an ordinary
wheel install: **source edits need a reinstall**, which is what installing
means. The NIM assistant is an extra and is left out unless you ask:

```bash
uv tool install "./tools/maku-tui[assistant]"
```

### To work on it

```bash
cd tools/maku-tui && python3 -m venv .venv && .venv/bin/pip install -e .
ln -s "$PWD/bin/mak" ~/.local/bin/mak
```

`bin/mak` is a shell script, not a generated console entry point, and it
resolves through however many symlinks put it on `PATH` back to this checkout
and its virtualenv. So **a source edit is live the next time `mak` starts**,
and there is nothing to reinstall. That is why the developer path exists at
all.

`mak` runs in whatever directory you start it from and finds that project's
`maku.toml` by searching upward, the way `git` finds a repository.

### If `mak` will not start

```bash
mak --doctor
```

It works on both installs and reports which one it is looking at; `mak --where`
prints the checkout, or the installed package when there is no checkout.

On **this** tree the usual cause is not a broken package. Every `.pth` file in
a venv under `~/Desktop` acquires the macOS `UF_HIDDEN` flag, and Python 3.13
**skips hidden `.pth` files** — so an editable install that `pip` reports as
installed is inert, and the symptom is `No module named maku_tui`. `chflags
nohidden` works and does not hold. The fix that holds is a symlink from
site-packages into `src/`, and `--doctor` prints it. Nothing outside that
situation needs the symlink, and the doctor says so rather than reporting a
fault.

---

## Using it

**There are no keyboard shortcuts, and that is deliberate.** There were
twenty-one. Ctrl bindings needed a modifier some terminals do not deliver,
F-keys carried actions nobody could discover, and one of them — `ctrl+i` —
could never fire at all, because 0x09 *is* the tab character. That binding
validated, appeared in the key bar and in the palette, and did nothing, for as
long as it existed. Automated tests could not see it either: a pilot presses
keys by name, so a binding no terminal can send passes every one of them.

Nine keys remain, and all nine are navigation:

| key | does |
|---|---|
| `/` | open the command line — every action is here, by name |
| arrows | move between sections, or move the caret inside the editor |
| `tab` / `shift+tab` | next / previous section, in reading order |
| `enter` | activate what has focus — open a file, enter edit mode, answer a question |
| `esc` | close what is open, one layer at a time: a question, the command line, edit mode |

Anything else is typed on the command line. `/` on its own lists every command
this project has, including one per `read_*_hw.py` on disk, and an argument is
completed from the kind it declares — `/load ` offers `.mem` images and nothing
else. **An argument that does not resolve is refused with a reason and your
text is kept**, never rounded to the nearest plausible file.

A few worth knowing:

| command | does |
|---|---|
| `/open` `/save` `/build` `/load` | the working loop |
| `/new` `/newfolder` `/rename` `/delete` | files; `/delete` asks first |
| `/key` | store the NVIDIA NIM key for the assistant, in a masked field |
| `/witness` `/self-boot` | read the boot witness; run the regression |
| `/focus` `/work` `/bench` | the three layouts |

### The mouse

Click to focus a pane; click a line in the editor to put the caret there;
click a folder in the explorer to open or shut it. The wheel scrolls whatever
is under the pointer, not whatever had focus last. While a question is open it
owns the pointer as well as the keyboard — a click outside the box does not
focus what is behind it.

### The key is never echoed

`/key` opens a field that is drawn as dots. The characters never enter the
drawing path at all, so the masking is not an overlay a redraw could get
wrong. The key is written to `~/.config/maku/credentials.toml` at mode 0600 in
a 0700 directory, with the mode set as the file is created rather than
tightened afterwards, and it is **never** written to `maku.toml`, which is
committed. `/key clear` removes it.

---

## Testing

```bash
tools/maku-tui/.venv/bin/python tools/maku-tui/tests/run_all.py
```

One `PASS`/`FAIL` per file and a single `RESULT:` line. A suite with no
`RESULT:` line reads as unverified, and this one has none such. A check that
cannot be *built* on the platform it is running on — there is one, and it needs
the macOS hidden-flag — reports `SKIP` with its reason, and `run_all.py` lifts
those lines into its own output so a check cannot stop running quietly.

`.github/workflows/maku-tui.yml` runs all of it on macOS and Linux, on Python
3.11 and 3.13. Linux is there for a reason rather than for symmetry: the
hidden-`.pth` failure this tool works around is a macOS behaviour, so Linux is
where a check that quietly depends on the workaround shows up.

Three gates are not in `run_all.py`. Two need the board —
`gate_eight_images.py` and `gate_witness.py` — and one needs a real terminal:

```bash
tools/maku-tui/.venv/bin/python tools/maku-tui/tests/gate_pty.py
```

`gate_pty.py` forks a pseudo-terminal and sends **bytes**. Textual's test
pilot presses keys *by name*, which is why `ctrl+i` survived a suite that was
green — no terminal can send it, and a pilot does not have to. It is also why a
key that was handled twice, once by the router and once by its binding, passed
everything: no test asked what happens when the bytes for `/` arrive while the
command line is open. The answer was that the line re-opened at that character,
so **no path argument could be typed at all**. The gate asserts what is on the
wire, including the one property that is only meaningful there: the NIM key
never appears in the byte stream.

---

## Licence

**Apache-2.0** — the full text is in [LICENSE](LICENSE). Chosen over MIT for the
patent grant, and in place before the first public commit rather than after.
