Compile FPGA configurations to Lua

This commit is contained in:
Jude Melton-Houghton 2022-12-06 11:53:24 -05:00
parent c10ce2dbc5
commit 776c7e0b23
2 changed files with 84 additions and 36 deletions

View File

@ -312,6 +312,37 @@ plg.red_box_around = function(i)
end
local compile_cache = {}
plg.compile_with_cache = function(instr)
local entry = compile_cache[instr]
if entry then
-- Prevent removal from the cache
entry.age = 0
else
entry = {
func = lcore.compile(lcore.deserialize(instr)),
age = 0,
}
compile_cache[instr] = entry
end
return entry.func
end
-- Entries are removed from the cache if they are unused for at least a minute.
local function clean_compile_cache()
for instr, entry in pairs(compile_cache) do
if entry.age < 1 then
entry.age = entry.age + 1
else
compile_cache[instr] = nil
end
end
minetest.after(60, clean_compile_cache)
end
minetest.after(60, clean_compile_cache)
plg.update = function(pos)
local meta = minetest.get_meta(pos)
if meta:get_int("valid") ~= 1 then
@ -323,9 +354,9 @@ plg.update = function(pos)
return
end
local is = lcore.deserialize(meta:get_string("instr"))
local func = plg.compile_with_cache(meta:get_string("instr"))
local A, B, C, D = plg.getports(pos)
A, B, C, D = lcore.interpret(is, A, B, C, D)
A, B, C, D = func(A, B, C, D)
plg.setports(pos, A, B, C, D)
end

View File

@ -6,16 +6,16 @@ local operations = {
-- gate: Internal name
-- short: Serialized form, single character
-- fs_name: Display name, padded to 4 characters
-- func: Function that applies the operation
-- func: Function that returns tokens representing the operation
-- unary: Whether this gate only has one input
{ gate = "and", short = "&", fs_name = " AND", func = function(a, b) return a and b end },
{ gate = "or", short = "|", fs_name = " OR", func = function(a, b) return a or b end },
{ gate = "not", short = "~", fs_name = " NOT", func = function(_, b) return not b end, unary = true },
{ gate = "xor", short = "^", fs_name = " XOR", func = function(a, b) return a ~= b end },
{ gate = "nand", short = "?", fs_name = "NAND", func = function(a, b) return not (a and b) end },
{ gate = "and", short = "&", fs_name = " AND", func = function(a, b) return a, "and", b end },
{ gate = "or", short = "|", fs_name = " OR", func = function(a, b) return a, "or", b end },
{ gate = "not", short = "~", fs_name = " NOT", func = function(_, b) return "not", b end, unary = true },
{ gate = "xor", short = "^", fs_name = " XOR", func = function(a, b) return a, "~=", b end },
{ gate = "nand", short = "?", fs_name = "NAND", func = function(a, b) return "not (", a, "and", b, ")" end },
{ gate = "buf", short = "_", fs_name = " =", func = function(_, b) return b end, unary = true },
{ gate = "xnor", short = "=", fs_name = "XNOR", func = function(a, b) return a == b end },
{ gate = "nor", short = "!", fs_name = " NOR", func = function(a, b) return not (a or b) end },
{ gate = "xnor", short = "=", fs_name = "XNOR", func = function(a, b) return a, "==", b end },
{ gate = "nor", short = "!", fs_name = " NOR", func = function(a, b) return "not (", a, "or", b, ")" end },
}
lg.get_operations = function()
@ -177,46 +177,63 @@ lg.validate = function(t)
return nil
end
-- interpreter
lg.interpret = function(t, a, b, c, d)
local function _action(s, v1, v2)
local fpga_env = setmetatable({}, {
__index = function(_, var)
error("FPGA code tried to read undeclared variable " .. var)
end,
__newindex = function(_, var)
error("FPGA code tried to set undeclared variable " .. var)
end,
})
-- compiler
lg.compile = function(t)
-- Get token generation function from action gate string
local function _action(s)
for i, data in ipairs(operations) do
if data.gate == s then
return data.func(v1, v2)
return data.func
end
end
return false -- unknown gate
return nil -- unknown gate
end
local function _op(t, regs, io_in)
if t.type == "reg" then
return regs[t.n]
-- Serialize input operand
local function _op(t)
if t == nil then
return nil
elseif t.type == "reg" then
return "r" .. t.n
else -- t.type == "io"
return io_in[t.port]
return "i" .. t.port
end
end
-- Serialize destination
local function _dst(t)
if t.type == "reg" then
return "local r" .. t.n
else -- t.type == "io"
return "o" .. t.port
end
end
local io_in = {A=a, B=b, C=c, D=d}
local regs = {}
local io_out = {}
local tokens = {
-- Declare inputs and outputs:
"return function(iA, iB, iC, iD) local oA, oB, oC, oD",
}
for i = 1, 14 do
local cur = t[i]
if next(cur) ~= nil then
local v1, v2
if cur.op1 ~= nil then
v1 = _op(cur.op1, regs, io_in)
end
v2 = _op(cur.op2, regs, io_in)
local result = _action(cur.action, v1, v2)
if cur.dst.type == "reg" then
regs[cur.dst.n] = result
else -- cur.dst.type == "io"
io_out[cur.dst.port] = result
end
table.insert(tokens, _dst(cur.dst))
table.insert(tokens, "=")
table.insert_all(tokens, {_action(cur.action)(_op(cur.op1), _op(cur.op2))})
end
end
return io_out.A, io_out.B, io_out.C, io_out.D
table.insert(tokens, "return oA, oB, oC, oD end")
local code = table.concat(tokens, " ")
local func = assert(loadstring(code))()
setfenv(func, fpga_env)
return func
end
return lg