200 lines
12 KiB
Markdown
200 lines
12 KiB
Markdown
# MinetestPnR
|
|
Simple 2D placer & router for [minetest] developed as part of a [Google CTF] challenge ([video writeup](https://www.youtube.com/watch?v=nI8Q1bqT8QU)).
|
|
This also means that the project is mostly aimed towards creating this challenge,
|
|
not towards being feature complete and being well engineered, so don't expect
|
|
optimal/minimal output.
|
|
|
|
It currently supports only basic gates (i.e. **no DFF / latches**), so I'm afraid you
|
|
can't just place your RISC-V CPU in minetest, feel free to send pull requests though ;).
|
|
|
|
Supported gates:
|
|
- AND
|
|
- NAND
|
|
- ANDNOT
|
|
- OR
|
|
- NOR
|
|
- ORNOT
|
|
- NOT
|
|
- XNOR
|
|
- XOR
|
|
|
|
The result will look similar to this (text version):
|
|
```Text
|
|
░──────────────────¬─────────────┐┌───┐ ┌───────┐ ┌─────┐ ┌──┐ ┌─┐
|
|
░────────┬─────────¬─────────┐ ││ ^──┘ ^──┘ ^─────»──┘ ^──┘ ^────▓
|
|
░────────╂──────┬──¬─────────╂───╂┘┌──┘ ┌───────┘ ┌─────┘ ┌──┘ ┌─┘
|
|
░────────╂──────╂┬─¬─────┬───╂───╂─╂──┐ │┌──────┐ │┌────┐ │┌─┐ │
|
|
░──────┬─╂──────╂╂─¬─────╂───╂───╂─╂┐ ^──┘│ ^──┘│ v─────¬──┘│ ^──┘
|
|
░──────╂─╂─┬────╂╂─»──┐ │ │ │ │├─┘ │┌─────┘ ┌╂────┘ ┌╂─┘
|
|
░──────╂─╂─╂┬───╂╂─¬──╂──╂───╂──┐└─╂╂─┐ ┌╂╂─────┐ ││┌───┐ ││
|
|
░──────╂┐└─╂╂───╂╂─»──╂──╂───╂──╂──┤│ ^──╂┘│ v──╂┘│ ^─────»──╂┘
|
|
░──────╂╂──╂╂───╂╂─»──╂──╂───╂──╂──╂╂─┘ │ │┌────┘ │ │┌──┘ ┌──┐ │
|
|
░──────╂╂──╂╂──┬╂╂─¬──╂──╂───╂┐┌╂──╂╂─┐ │┌╂╂────┐ │┌╂╂──┐ │ ^──┘
|
|
░──────╂╂──╂╂─┐└╂╂─»──╂┐ │ ││││ ││ v──┘│││ v──┘│││ ^──┘┌─┘
|
|
░────┬─╂╂──╂╂─╂─╂╂─¬──╂╂─╂───╂╂╂╂┐┌╂╂─┘ │││┌───┘ ┌╂╂╂──┘ │
|
|
░────╂─╂╂──╂╂┬╂─╂╂─¬──╂╂┐│ ││││││││ ││││┌──┐ ││││┌─┐ │
|
|
░────╂─╂╂┐ ├╂╂╂─╂╂─┐ ││││ ││││││││ │││││ ^──╂╂┘││ ^───┘
|
|
░────╂─╂╂╂┐││││ ││ v──╂╂╂╂───╂╂╂╂╂╂╂╂─»───╂┘│││┌─┘ ││┌╂╂─┘
|
|
░────╂┐││├╂╂╂╂╂─╂╂─┘ └╂╂╂───╂╂╂╂╂╂╂╂─┐ ┌╂─╂╂╂╂─┐ │││││
|
|
░───┐└╂╂╂╂╂╂╂╂╂─╂╂─»───╂╂╂─┐ ││││││││ v──╂┘ ││││ ^──╂┘│││
|
|
░───╂─╂╂╂╂╂╂╂╂╂─╂╂─»───╂╂╂─╂─╂╂┘└╂╂╂╂─┘ │ ┌╂╂╂╂─┘ │ │││
|
|
░──┐│ │└╂╂╂╂╂╂╂─╂╂─»───╂╂╂─╂┐└╂──╂╂╂╂─┐ │ │││││ │ │││
|
|
░──╂╂─╂─╂╂╂╂╂╂╂─╂╂─¬───╂╂╂─╂╂─╂──╂┘││ v──╂─╂╂╂╂╂─»──╂─╂┘│
|
|
│└─╂─╂╂╂╂╂╂╂─╂╂─┐ └╂╂─╂╂─╂──╂─╂╂─┘ │┌╂╂╂╂╂─┐ │ │ │
|
|
│ │ │││││││ ││ v────╂╂─╂╂─╂──╂─╂╂─»──╂╂╂┘│││ ^──┘ │ │
|
|
└──╂─╂╂╂╂╂╂╂─╂╂─┘ ││ ││ │ │ ││ │││┌╂╂╂─┘ │ │
|
|
│ └╂╂╂╂╂╂─╂╂─┐ ││ ││ │ │ ││ │││││││ │ │
|
|
│ ││││││ ││ v────╂╂─╂╂─╂──╂─╂╂─»──╂╂╂╂┘││ │ │
|
|
└──╂╂╂╂╂╂─╂╂─┘ ││ ││ │ │ ││ ││││ ││ │ │
|
|
││││││ ├╂─┐ ││ ││ │ │ ││ ││││ ││ │ │
|
|
││││││ ││ ^────╂╂─╂╂─╂──╂─╂╂─¬──┘│││ ││ │ │
|
|
│││││├─╂╂─┘ │└─╂╂─╂──╂─╂╂─┐ │││ ││ │ │
|
|
││└╂╂╂─╂╂─┐ │ ││ │ │ ││ v───╂╂╂─┘│ │ │
|
|
││ │││ ││ ^────╂─┐└╂─╂──╂─╂╂─┘ │││ │ │ │
|
|
└╂─╂╂╂─╂╂─┘ │ │ │ │ │ └╂─┐ │││ │ │ │
|
|
│ ├╂╂─╂╂─┐ │ │ │ │ │ │ v───╂╂╂──┘ │ │
|
|
│ │││ ││ ^────╂┐│ │ └──╂──╂─┘ │││ │ │
|
|
├─╂╂╂─╂╂─┘ └╂╂─╂────╂──╂─┐ │││ │ │
|
|
│ └╂╂─╂╂─┐ ││ │ │ │ v───╂┘│ │ │
|
|
│ ││ ││ v────┐││ └────╂──╂─┘ │ │ │ │
|
|
└──╂╂─╂╂─┘ │││ │ └─┐ │ │ │ │
|
|
││ └╂─┐ │││ │ v───┘ │ │ │
|
|
││ │ v──┐┌╂╂╂──────╂────┘ │ │ │
|
|
│└──╂─┘ │││││ └────┐ │ │ │
|
|
└───╂─»──╂┘│││ v─────┘ │ │
|
|
└─»──╂─╂╂╂───────────┘ │ │
|
|
│ ││└───────────┐ │ │
|
|
│ ││ v──────────¬────╂─┘
|
|
│ │└────────────┘ │
|
|
│ └─────────────┐ │
|
|
│ ^──────────»────┘
|
|
└───────────────┘
|
|
```
|
|
|
|
For ingame footage, look at the CTF writeup video mentioned above.
|
|
|
|
## What does it do?
|
|
MinetestPnR takes a synthesized circuit, places the components and routes them (= wires things together).
|
|
This means that it'll allow you to write your circuit in a HDL (e.g. `verilog`), synthesize it (e.g. using `yosys`)
|
|
and then use it inside your minetest world.
|
|
|
|
## How can I use it?
|
|
Putting your circuit in minetest takes three steps:
|
|
- Generate the minetest schematic file
|
|
- Place it in minetest
|
|
- Fix the mesecon parts. More on this below.
|
|
|
|
### Generating minetest schematic
|
|
- Synthesize your circuit and create a json file containing the basic blocks using [yosys].
|
|
Example command: `yosys -p 'synth; abc -g AND,OR,XOR,XNOR,ANDNOT,ORNOT; write_json schematic.json' schematic.v`
|
|
- Place & route the resulting `schematic.json` file, creating a MTS(minetest schematic) file using this project:
|
|
`cargo run --release -- ./schematic.json --write_mts schematic.mts`
|
|
|
|
### Placing MTS in minetest using worldedit
|
|
- Install [mesecons] + [worldedit]
|
|
- Create world in minetest (`type=single node` if you only want to have the circuit in the world)
|
|
- Remember to enable both mods
|
|
- Create `schems` directory in the world directory (`worlds/${worldname}/schems`)
|
|
- Generate MTS file using the project, place it in that folder
|
|
- Start minetest + load your map
|
|
- Set pos1 (`//1` / `//fixedpos set1 0 1 0`) where the schematic should be placed
|
|
- `//mtschemplace <name without mts>` to place the schematic
|
|
|
|
### Fixing mesecon wires / gates
|
|
- Fix mesecon wires + gates - those blocks introduce some internal state that is not created when
|
|
placed using worldedit. This can be done by adding a function to the mesecons code that will
|
|
set up that internal state. This can be done by adding this function:
|
|
|
|
```lua
|
|
local function fix_single_chunk(pos)
|
|
local found_nodes = minetest.find_nodes_in_area(pos, vector.add(pos, { x = 16, y = 16, z = 16 }), {
|
|
"mesecons_gates:diode_off",
|
|
"mesecons_gates:not_off",
|
|
"mesecons_gates:and_off",
|
|
"mesecons_gates:nand_off",
|
|
"mesecons_gates:xor_off",
|
|
"mesecons_gates:nor_off",
|
|
"mesecons_gates:or_off",
|
|
"mesecons:mesecons_off",
|
|
"mesecons:wire_00000000_off",
|
|
})
|
|
local cnt = 0
|
|
|
|
for i=0, #found_nodes do
|
|
if (found_nodes[i] ~= nil) then
|
|
mesecon.on_placenode(found_nodes[i], minetest.get_node(found_nodes[i]))
|
|
cnt = cnt + 1
|
|
end
|
|
end
|
|
|
|
return cnt
|
|
end
|
|
|
|
-- Adapted from:
|
|
-- https://rubenwardy.com/minetest_modding_book/en/map/environment.html
|
|
local function emerge_callback(pos, action,
|
|
num_calls_remaining, context)
|
|
-- On first call, record number of blocks
|
|
if not context.total_blocks then
|
|
context.total_blocks = num_calls_remaining + 1
|
|
context.loaded_blocks = 0
|
|
context.nodes_fixed = 0
|
|
end
|
|
|
|
context.loaded_blocks = context.loaded_blocks + 1
|
|
local perc = 100 * context.loaded_blocks / context.total_blocks
|
|
local msg = string.format("Handling block %d/%d (%.2f%%)",
|
|
context.loaded_blocks, context.total_blocks, perc)
|
|
context.nodes_fixed = context.nodes_fixed + fix_single_chunk(pos)
|
|
minetest.chat_send_all(msg)
|
|
|
|
-- Are we done yet?
|
|
if context.total_blocks == context.loaded_blocks then
|
|
minetest.chat_send_all("Done, " .. context.nodes_fixed .. " nodes fixed")
|
|
end
|
|
end
|
|
|
|
minetest.register_chatcommand("fix_gates", {
|
|
params = "",
|
|
description = "Fix gates by triggering I/O reevaluation",
|
|
func = function(name, param)
|
|
local context = {}
|
|
minetest.emerge_area({x=0, y=2, z=0}, {x=2000, y=2, z=2000}, emerge_callback, context)
|
|
return true, "Emerge started"
|
|
end,
|
|
})
|
|
```
|
|
|
|
e.g. at the end of `mesecons_gates/init.lua`.
|
|
Then load up the map and send `/fix_gates` in chat.
|
|
This sometimes does not seem to work reliably, so you might need to use it multiple
|
|
times when standing at different locations to cover different chunks.
|
|
|
|
**Note that the snippet contains the area where the wires should be fixed** (`minetest.emerge_area({x=0, y=2, z=0}, {x=2000, y=2, z=2000}`)
|
|
so you might want to adjust this.
|
|
|
|
## Caveats
|
|
### My circuit is too large!
|
|
The application uses a 8k * 8k tiles canvas where the circuit gets placed onto.
|
|
The maximum size that the MTS format supports is 64k * 64k, so depending on how
|
|
big your circuit is you can place your bigger circuit by changing the canvas
|
|
size in `src/canvas.rs`:
|
|
```rust
|
|
const CANVAS_MAX_W: usize = 8 * 1024;
|
|
const CANVAS_MAX_H: usize = 8 * 1024;
|
|
```
|
|
|
|
Why not using 64k * 64k by default? 64k * 64k = 4G - that's a lot of ram ;).
|
|
**NOTE**: The minetest source code is not consistent whether the dimensions are
|
|
stored as `u16` or `i16`, so the code might need additional patches in
|
|
`serialize_to_mts` if your circuit is bigger than 32k * 32k.
|
|
|
|
## Disclaimer
|
|
This is not an officially supported Google product.
|
|
|
|
[minetest]: http://www.minetest.net/
|
|
[worldedit]: https://github.com/Uberi/Minetest-WorldEdit
|
|
[mesecons]: http://mesecons.net/
|
|
[yosys]: http://www.clifford.at/yosys/
|
|
[Google CTF]: https://capturetheflag.withgoogle.com
|