summaryrefslogtreecommitdiff
path: root/mesecons_fpga/logic.lua
blob: 3dca154650f71c5ec9328c78518c5927b8ce73b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
local lg = {}

-- (de)serialize
lg.serialize = function(t)
	local function _op(t)
		if t == nil then
			return " "
		elseif t.type == "io" then
			return t.port
		else -- t.type == "reg"
			return tostring(t.n)
		end
	end
	local function _action(s)
		if s == nil then
			return " "
		end
		local mapping = {
			["and"] = "&",
			["or"] = "|",
			["not"] = "~",
			["xor"] = "^",
			["nand"] = "?", --dunno
			["buf"] = "_",
			["xnor"] = "=",
		}
		return mapping[s]
	end

	local s = ""
	for i = 1, 14 do
		local cur = t[i]
		if next(cur) ~= nil then
			s = s .. _op(cur.op1) .. _action(cur.action) .. _op(cur.op2) .. _op(cur.dst)
		end
		s = s .. "/"
	end
	return s
end

lg.deserialize = function(s)
	local function _op(c)
		if c == "A" or c == "B" or c == "C" or c == "D" then
			return {type = "io", port = c}
		elseif c == " " then
			return nil
		else
			return {type = "reg", n = tonumber(c)}
		end
	end
	local function _action(c)
		local mapping = {
			["&"] = "and",
			["|"] = "or",
			["~"] = "not",
			["^"] = "xor",
			["?"] = "nand",
			["_"] = "buf",
			["="] = "xnor",
			[" "] = nil,
		}
		return mapping[c]
	end

	local ret = {}
	for part in s:gmatch("(.-)/") do
		local parsed
		if part == "" then
			parsed = {}
		else
			parsed = {
				action = _action( part:sub(2,2) ),
				op1 = _op( part:sub(1,1) ),
				op2 = _op( part:sub(3,3) ),
				dst = _op( part:sub(4,4) ),
			}
		end
		ret[#ret + 1] = parsed
	end
	-- More than 14 instructions (write to all 10 regs + 4 outputs)
	-- will not pass the write-once requirement of the validator
	assert(#ret == 14)
	return ret
end

-- validation
lg.validate_single = function(t, i)
	local function is_reg_written_to(t, n, max)
		for i = 1, max-1 do
			if next(t[i]) ~= nil
					and t[i].dst and t[i].dst.type == "reg"
					and t[i].dst.n == n then
				return true
			end
		end
		return false
	end
	local function compare_op(t1, t2, allow_same_io)
		if t1 == nil or t2 == nil then
			return false
		elseif t1.type ~= t2.type then
			return false
		end
		if t1.type == "reg" and t1.n == t2.n then
			return true
		elseif t1.type == "io" and t1.port == t2.port then
			return not allow_same_io
		end
		return false
	end
	local elem = t[i]
	-- check for completeness
	if elem.action == nil then
		return {i = i, msg = "Gate type required"}
	elseif elem.action == "not" or elem.action == "buf" then
		if elem.op1 ~= nil or elem.op2 == nil or elem.dst == nil then
			return {i = i, msg = "Second operand (only) and destination required"}
		end
	else
		if elem.op1 == nil or elem.op2 == nil or elem.dst == nil then
			return {i = i, msg = "Operands and destination required"}
		end
	end
	-- check whether operands/destination are identical
	if compare_op(elem.op1, elem.op2) then
		return {i = i, msg = "Operands cannot be identical"}
	end
	if compare_op(elem.op1, elem.dst, true) or compare_op(elem.op2, elem.dst, true) then
		return {i = i, msg = "Destination and operands must be different"}
	end
	-- check whether operands point to defined registers
	if elem.op1 ~= nil and elem.op1.type == "reg"
			and not is_reg_written_to(t, elem.op1.n, i) then
		return {i = i, msg = "First operand is undefined register"}
	end
	if elem.op2.type == "reg" and not is_reg_written_to(t, elem.op2.n, i) then
		return {i = i, msg = "Second operand is undefined register"}
	end
	-- check whether destination points to undefined register
	if elem.dst.type == "reg" and is_reg_written_to(t, elem.dst.n, i) then
		return {i = i, msg = "Destination is already used register"}
	end

	return nil
end

lg.validate = function(t)
	for i = 1, 14 do
		if next(t[i]) ~= nil then
			local r = lg.validate_single(t, i)
			if r ~= nil then
				return r
			end
		end
	end
	return nil
end

-- interpreter
lg.interpret = function(t, a, b, c, d)
	local function _action(s, v1, v2)
		if s == "and" then
			return v1 and v2
		elseif s == "or" then
			return v1 or v2
		elseif s == "not" then
			return not v2
		elseif s == "xor" then
			return v1 ~= v2
		elseif s == "nand" then
			return not (v1 and v2)
		elseif s == "buf" then
			return v2
		else -- s == "xnor"
			return v1 == v2
		end
	end
	local function _op(t, regs, io_in)
		if t.type == "reg" then
			return regs[t.n]
		else -- t.type == "io"
			return io_in[t.port]
		end
	end

	local io_in = {A=a, B=b, C=c, D=d}
	local regs = {}
	local io_out = {}
	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
		end
	end
	return io_out.A, io_out.B, io_out.C, io_out.D
end

return lg