logicuit 0.1.5 → 0.3.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -1
- data/README.md +300 -18
- data/lib/logicuit/array_as_signal_group.rb +13 -0
- data/lib/logicuit/circuits/combinational/full_adder.rb +66 -0
- data/lib/logicuit/circuits/combinational/full_adder_4bit.rb +38 -0
- data/lib/logicuit/circuits/combinational/half_adder.rb +8 -10
- data/lib/logicuit/circuits/combinational/{multiplexer2to1.rb → multiplexer_2to1.rb} +7 -9
- data/lib/logicuit/circuits/combinational/{multiplexer4to1.rb → multiplexer_4to1.rb} +11 -15
- data/lib/logicuit/circuits/sequential/d_flip_flop.rb +3 -5
- data/lib/logicuit/circuits/sequential/one_bit_cpu.rb +39 -0
- data/lib/logicuit/circuits/sequential/program_counter.rb +39 -0
- data/lib/logicuit/circuits/sequential/register_4bit.rb +56 -0
- data/lib/logicuit/circuits/td4/cpu.rb +95 -0
- data/lib/logicuit/circuits/td4/decoder.rb +37 -0
- data/lib/logicuit/circuits/td4/rom.rb +61 -0
- data/lib/logicuit/dsl.rb +158 -0
- data/lib/logicuit/gates/and.rb +5 -7
- data/lib/logicuit/gates/nand.rb +5 -7
- data/lib/logicuit/gates/not.rb +3 -5
- data/lib/logicuit/gates/or.rb +5 -7
- data/lib/logicuit/gates/xor.rb +5 -7
- data/lib/logicuit/runner.rb +45 -0
- data/lib/logicuit/signals/clock.rb +22 -14
- data/lib/logicuit/signals/signal.rb +29 -22
- data/lib/logicuit/signals/signal_group.rb +26 -0
- data/lib/logicuit/version.rb +1 -1
- data/lib/logicuit.rb +15 -5
- metadata +16 -6
- data/lib/logicuit/base.rb +0 -172
- data/lib/logicuit/circuits/system_level/one_bit_cpu.rb +0 -41
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logicuit
|
4
|
+
module Circuits
|
5
|
+
module Sequential
|
6
|
+
# Program Counter
|
7
|
+
class ProgramCounter < DSL
|
8
|
+
diagram <<~DIAGRAM
|
9
|
+
(A)---| |---(QA)
|
10
|
+
(B)---| |---(QB)
|
11
|
+
(C)---|PC|---(QC)
|
12
|
+
(D)---| |---(QD)
|
13
|
+
(LD)--| |
|
14
|
+
DIAGRAM
|
15
|
+
|
16
|
+
inputs :a, :b, :c, :d, :ld, clock: :ck
|
17
|
+
|
18
|
+
outputs :qa, :qb, :qc, :qd
|
19
|
+
|
20
|
+
assembling do
|
21
|
+
# inputs :cin, :a0, :b0, :a1, :b1, :a2, :b2, :a3, :b3
|
22
|
+
fadd = Combinational::FullAdder4bit.new(0, 0, 1, 0, 0, 0, 0, 0, 0)
|
23
|
+
|
24
|
+
[[a, qa, :a0, :s0], [b, qb, :a1, :s1], [c, qc, :a2, :s2],
|
25
|
+
[d, qd, :a3, :s3]].each do |input, output, fadd_in, fadd_out|
|
26
|
+
dff = Sequential::DFlipFlop.new
|
27
|
+
mux = Combinational::Multiplexer2to1.new
|
28
|
+
input >> mux.c0
|
29
|
+
dff.q >> fadd.send(fadd_in)
|
30
|
+
fadd.send(fadd_out) >> mux.c1
|
31
|
+
ld >> mux.a
|
32
|
+
mux.y >> dff.d
|
33
|
+
dff.q >> output
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logicuit
|
4
|
+
module Circuits
|
5
|
+
module Sequential
|
6
|
+
# 4 bit register
|
7
|
+
class Register4bit < DSL
|
8
|
+
diagram <<~DIAGRAM
|
9
|
+
+---------------------+
|
10
|
+
+-| | |
|
11
|
+
(A)-------|MUX|-------|DFF|---+---(QA)
|
12
|
+
+---| | +---| |
|
13
|
+
| |
|
14
|
+
| +---------------------+
|
15
|
+
| +-| | | |
|
16
|
+
(B)-------|MUX|-------|DFF|---+---(QB)
|
17
|
+
+---| | +---| |
|
18
|
+
| |
|
19
|
+
| +---------------------+
|
20
|
+
| +-| | | |
|
21
|
+
(C)-------|MUX|-------|DFF|---+---(QC)
|
22
|
+
+---| | +---| |
|
23
|
+
| |
|
24
|
+
| +---------------------+
|
25
|
+
| +-| | | |
|
26
|
+
(D)-------|MUX|-------|DFF|---+---(QD)
|
27
|
+
+---| | +---| |
|
28
|
+
(LD)--+ (CK)--+
|
29
|
+
DIAGRAM
|
30
|
+
|
31
|
+
inputs :a, :b, :c, :d, :ld, clock: :ck
|
32
|
+
|
33
|
+
outputs :qa, :qb, :qc, :qd
|
34
|
+
|
35
|
+
assembling do
|
36
|
+
[[a, qa], [b, qb], [c, qc], [d, qd]].each do |input, output|
|
37
|
+
dff = Sequential::DFlipFlop.new
|
38
|
+
mux = Combinational::Multiplexer2to1.new
|
39
|
+
input >> mux.c0
|
40
|
+
dff.q >> mux.c1
|
41
|
+
ld >> mux.a
|
42
|
+
mux.y >> dff.d
|
43
|
+
dff.q >> output
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
truth_table <<~TRUTH_TABLE
|
48
|
+
| CK | A | B | C | D | LD | QA | QB | QC | QD |
|
49
|
+
| -- | - | - | - | - | -- | -- | -- | -- | -- |
|
50
|
+
| ^ | x | x | x | x | 0 | A | B | C | D |
|
51
|
+
| ^ | x | x | x | x | 1 | QA | QB | QC | QD |
|
52
|
+
TRUTH_TABLE
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logicuit
|
4
|
+
module Circuits
|
5
|
+
module Td4
|
6
|
+
# TD4 CPU
|
7
|
+
class Cpu < DSL
|
8
|
+
using Logicuit::ArrayAsSignalGroup
|
9
|
+
|
10
|
+
inputs :in0, :in1, :in2, :in3, clock: :ck
|
11
|
+
|
12
|
+
outputs :led1, :led2, :led3, :led4
|
13
|
+
|
14
|
+
assembling do
|
15
|
+
register_a, register_b, register_c = (:a..:c).map { Sequential::Register4bit.new }
|
16
|
+
pc = Sequential::ProgramCounter.new
|
17
|
+
rom = Rom.new
|
18
|
+
dec = Decoder.new
|
19
|
+
mux0, mux1, mux2, mux3 = (0..3).map { Combinational::Multiplexer4to1.new }
|
20
|
+
alu = Combinational::FullAdder4bit.new
|
21
|
+
dff = Sequential::DFlipFlop.new
|
22
|
+
|
23
|
+
alu.s0 >> [register_a.a, register_b.a, register_c.a, pc.a]
|
24
|
+
alu.s1 >> [register_a.b, register_b.b, register_c.b, pc.b]
|
25
|
+
alu.s2 >> [register_a.c, register_b.c, register_c.c, pc.c]
|
26
|
+
alu.s3 >> [register_a.d, register_b.d, register_c.d, pc.d]
|
27
|
+
alu.c >> dff.d
|
28
|
+
|
29
|
+
[register_a.qa, register_b.qa, in0] >> mux0[:c0, :c1, :c2]
|
30
|
+
[register_a.qb, register_b.qb, in1] >> mux1[:c0, :c1, :c2]
|
31
|
+
[register_a.qc, register_b.qc, in2] >> mux2[:c0, :c1, :c2]
|
32
|
+
[register_a.qd, register_b.qd, in3] >> mux3[:c0, :c1, :c2]
|
33
|
+
dec.sel_a >> [mux0.a, mux1.a, mux2.a, mux3.a]
|
34
|
+
dec.sel_b >> [mux0.b, mux1.b, mux2.b, mux3.b]
|
35
|
+
[mux0.y, mux1.y, mux2.y, mux3.y] >> [alu.a0, alu.a1, alu.a2, alu.a3]
|
36
|
+
|
37
|
+
register_c[:qa, :qb, :qc, :qd] >> [led4, led3, led2, led1]
|
38
|
+
pc[:qa, :qb, :qc, :qd] >> rom[:a0, :a1, :a2, :a3]
|
39
|
+
rom[:d0, :d1, :d2, :d3] >> alu[:b0, :b1, :b2, :b3]
|
40
|
+
rom[:d4, :d5, :d6, :d7] >> dec[:op0, :op1, :op2, :op3]
|
41
|
+
dec[:ld0, :ld1, :ld2, :ld3] >> [register_a.ld, register_b.ld, register_c.ld, pc.ld]
|
42
|
+
dff.q >> dec.c_flag
|
43
|
+
|
44
|
+
[register_a, register_b, pc, rom, dec]
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
p_a = "(#{@a || "0000"})"
|
49
|
+
p_b = "(#{@b || "0000"})"
|
50
|
+
|
51
|
+
register_a, register_b, pc, rom, dec = components
|
52
|
+
@a = a = register_a[:qd, :qc, :qb, :qa].to_s
|
53
|
+
@b = b = register_b[:qd, :qc, :qb, :qa].to_s
|
54
|
+
p = pc[:qd, :qc, :qb, :qa]
|
55
|
+
o = self[:led1, :led2, :led3, :led4]
|
56
|
+
i = self[:in3, :in2, :in1, :in0]
|
57
|
+
m = rom[:d3, :d2, :d1, :d0]
|
58
|
+
c = "-(#{dec.c_flag})"
|
59
|
+
loc = p.to_s.to_i(2)
|
60
|
+
|
61
|
+
l1 = led1.current ? "*" : " "
|
62
|
+
l2 = led2.current ? "*" : " "
|
63
|
+
l3 = led3.current ? "*" : " "
|
64
|
+
l4 = led4.current ? "*" : " "
|
65
|
+
|
66
|
+
<<~OUTPUT
|
67
|
+
|
68
|
+
#{l1 * 7}#{" " * 7}#{l2 * 7}#{" " * 7}#{l3 * 7}#{" " * 7}#{l4 * 7}
|
69
|
+
#{l1 * 9}#{" " * 5}#{l2 * 9}#{" " * 5}#{l3 * 9}#{" " * 5}#{l4 * 9}
|
70
|
+
#{l1 * 11}#{" " * 3}#{l2 * 11}#{" " * 3}#{l3 * 11}#{" " * 3}#{l4 * 11}
|
71
|
+
#{l1 * 9}#{" " * 5}#{l2 * 9}#{" " * 5}#{l3 * 9}#{" " * 5}#{l4 * 9}
|
72
|
+
#{l1 * 7}#{" " * 7}#{l2 * 7}#{" " * 7}#{l3 * 7}#{" " * 7}#{l4 * 7}
|
73
|
+
|
74
|
+
+-----------------------------------------------+ #{loc == 0 ? ">" : " "} OUT 0111
|
75
|
+
| | #{loc == 1 ? ">" : " "} ADD A,0001
|
76
|
+
+--->|rg_a|#{p_a}----->| | | #{loc == 2 ? ">" : " "} JNC 0001
|
77
|
+
| |#{a}| | | | #{loc == 3 ? ">" : " "} ADD A,0001
|
78
|
+
| | | | #{loc == 4 ? ">" : " "} JNC 0011
|
79
|
+
+--->|rg_b|#{p_b}----->| |----------->| |---+ #{loc == 5 ? ">" : " "} OUT 0110
|
80
|
+
| |#{b}| | | | | #{loc == 6 ? ">" : " "} ADD A,0001
|
81
|
+
| |SEL| |ALU| #{loc == 7 ? ">" : " "} JNC 0110
|
82
|
+
+--->| out| | in|--->| | | | #{loc == 8 ? ">" : " "} ADD A,0001
|
83
|
+
| |#{o}| |#{i}| | | | im|--->| |-#{c} #{loc == 9 ? ">" : " "} JNC 1000
|
84
|
+
| | | |#{m}| #{loc == 10 ? ">" : " "} OUT 0000
|
85
|
+
+--->| pc| (0000)--->| | #{loc == 11 ? ">" : " "} OUT 0100
|
86
|
+
|#{p}| #{loc == 12 ? ">" : " "} ADD A,0001
|
87
|
+
#{loc == 13 ? ">" : " "} JNC 1010
|
88
|
+
#{loc == 14 ? ">" : " "} OUT 1000
|
89
|
+
#{loc == 15 ? ">" : " "} JMP 1111
|
90
|
+
OUTPUT
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logicuit
|
4
|
+
module Circuits
|
5
|
+
module Td4
|
6
|
+
# Decoder class
|
7
|
+
class Decoder < DSL
|
8
|
+
inputs :op3, :op2, :op1, :op0, :c_flag
|
9
|
+
|
10
|
+
outputs sel_b: -> { op1 },
|
11
|
+
sel_a: -> { op3 || op0 },
|
12
|
+
ld0: -> { op3 || op2 },
|
13
|
+
ld1: -> { op3 || !op2 },
|
14
|
+
ld2: -> { !op3 || op2 },
|
15
|
+
ld3: -> { !op3 || !op2 || (!op0 && c_flag) }
|
16
|
+
|
17
|
+
truth_table <<~TRUTH_TABLE
|
18
|
+
| OP3 | OP2 | OP1 | OP0 | C_FLAG | SEL_B | SEL_A | LD0 | LD1 | LD2 | LD3 |
|
19
|
+
| --- | --- | --- | --- | ------ | ----- | ----- | --- | --- | --- | --- |
|
20
|
+
| 0 | 0 | 0 | 0 | x | 0 | 0 | 0 | 1 | 1 | 1 | # ADD A,Im
|
21
|
+
| 0 | 0 | 0 | 1 | x | 0 | 1 | 0 | 1 | 1 | 1 | # MOV A,B
|
22
|
+
| 0 | 0 | 1 | 0 | x | 1 | 0 | 0 | 1 | 1 | 1 | # IN A
|
23
|
+
| 0 | 0 | 1 | 1 | x | 1 | 1 | 0 | 1 | 1 | 1 | # MOV A,Im
|
24
|
+
| 0 | 1 | 0 | 0 | x | 0 | 0 | 1 | 0 | 1 | 1 | # MOV B,A
|
25
|
+
| 0 | 1 | 0 | 1 | x | 0 | 1 | 1 | 0 | 1 | 1 | # ADD B,Im
|
26
|
+
| 0 | 1 | 1 | 0 | x | 1 | 0 | 1 | 0 | 1 | 1 | # IN B
|
27
|
+
| 0 | 1 | 1 | 1 | x | 1 | 1 | 1 | 0 | 1 | 1 | # MOV B,Im
|
28
|
+
| 1 | 0 | 0 | 1 | x | 0 | 1 | 1 | 1 | 0 | 1 | # OUT B
|
29
|
+
| 1 | 0 | 1 | 1 | x | 1 | 1 | 1 | 1 | 0 | 1 | # OUT Im
|
30
|
+
| 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | # JNC(C=0)
|
31
|
+
| 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | # JNC(C=1)
|
32
|
+
| 1 | 1 | 1 | 1 | x | 1 | 1 | 1 | 1 | 1 | 0 | # JMP
|
33
|
+
TRUTH_TABLE
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Logicuit
|
4
|
+
module Circuits
|
5
|
+
module Td4
|
6
|
+
# Timer
|
7
|
+
class Rom < DSL
|
8
|
+
inputs :a3, :a2, :a1, :a0
|
9
|
+
|
10
|
+
outputs :d7, :d6, :d5, :d4, :d3, :d2, :d1, :d0
|
11
|
+
|
12
|
+
def evaluate
|
13
|
+
return unless initialized
|
14
|
+
|
15
|
+
output = case "#{a3}#{a2}#{a1}#{a0}"
|
16
|
+
in "0000" then "10110111"
|
17
|
+
in "0001" then "00000001"
|
18
|
+
in "0010" then "11100001"
|
19
|
+
in "0011" then "00000001"
|
20
|
+
in "0100" then "11100011"
|
21
|
+
in "0101" then "10110110"
|
22
|
+
in "0110" then "00000001"
|
23
|
+
in "0111" then "11100110"
|
24
|
+
in "1000" then "00000001"
|
25
|
+
in "1001" then "11101000"
|
26
|
+
in "1010" then "10110000"
|
27
|
+
in "1011" then "10110100"
|
28
|
+
in "1100" then "00000001"
|
29
|
+
in "1101" then "11101010"
|
30
|
+
in "1110" then "10111000"
|
31
|
+
in "1111" then "11111111"
|
32
|
+
end
|
33
|
+
output.split("").zip([d7, d6, d5, d4, d3, d2, d1, d0]).each do |v, o|
|
34
|
+
v == "1" ? o.on : o.off
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
truth_table <<~TRUTH_TABLE
|
39
|
+
| A3 | A2 | A1 | A0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
40
|
+
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
|
41
|
+
| 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | # OUT 0111
|
42
|
+
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
|
43
|
+
| 0 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | # JNC 0001
|
44
|
+
| 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
|
45
|
+
| 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | # JNC 0011
|
46
|
+
| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | # OUT 0110
|
47
|
+
| 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
|
48
|
+
| 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | # JNC 0110
|
49
|
+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
|
50
|
+
| 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | # JNC 1000
|
51
|
+
| 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | # OUT 0000
|
52
|
+
| 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | # OUT 0100
|
53
|
+
| 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | # ADD A,0001
|
54
|
+
| 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | # JNC 1010
|
55
|
+
| 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | # OUT 1000
|
56
|
+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | # JMP 1111
|
57
|
+
TRUTH_TABLE
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/logicuit/dsl.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Logicuit module
|
4
|
+
module Logicuit
|
5
|
+
# base class for all gates and circuits
|
6
|
+
class DSL
|
7
|
+
def initialize(*args)
|
8
|
+
@input_targets = []
|
9
|
+
@inputs_as_bool_struct = nil
|
10
|
+
@output_targets = []
|
11
|
+
@clock = false
|
12
|
+
@components = []
|
13
|
+
inputs(*args)
|
14
|
+
outputs
|
15
|
+
assembling
|
16
|
+
@initialized = true
|
17
|
+
evaluate
|
18
|
+
end
|
19
|
+
|
20
|
+
def inputs(*args); end
|
21
|
+
def outputs; end
|
22
|
+
def assembling; end
|
23
|
+
def evaluate(*args); end
|
24
|
+
|
25
|
+
attr_reader :input_targets, :output_targets, :clock, :components, :initialized
|
26
|
+
|
27
|
+
def self.inputs(*args, **kwargs)
|
28
|
+
# define getter methods for inputs
|
29
|
+
attr_reader(*args)
|
30
|
+
|
31
|
+
# define initializer for inputs
|
32
|
+
define_method(:inputs) do |*instance_method_args|
|
33
|
+
@clock = true if kwargs&.key?(:clock)
|
34
|
+
args.each_with_index do |input, index|
|
35
|
+
signal = Signals::Signal.new(instance_method_args[index] == 1)
|
36
|
+
signal >> self unless clock
|
37
|
+
instance_variable_set("@#{input}", signal)
|
38
|
+
@input_targets << input
|
39
|
+
end
|
40
|
+
Signals::Clock >> self if clock
|
41
|
+
@inputs_as_bool_struct = Struct.new(*@input_targets)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](*keys)
|
46
|
+
if keys.size == 1
|
47
|
+
send(keys.first)
|
48
|
+
elsif keys.size > 1
|
49
|
+
Signals::SignalGroup.new(*(keys.map { |key| send(key) }))
|
50
|
+
else
|
51
|
+
raise ArgumentError, "Invalid number of arguments"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.outputs(*args, **kwargs)
|
56
|
+
# define getter methods for outputs
|
57
|
+
attr_reader(*(args + kwargs.keys))
|
58
|
+
|
59
|
+
# define initializer for outputs
|
60
|
+
define_method(:outputs) do
|
61
|
+
(args + kwargs.keys).each do |output|
|
62
|
+
instance_variable_set("@#{output}", Signals::Signal.new(false))
|
63
|
+
@output_targets << output
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# define evaluate method
|
68
|
+
return if kwargs.empty?
|
69
|
+
|
70
|
+
define_method(:evaluate) do |*override_args|
|
71
|
+
return unless initialized
|
72
|
+
|
73
|
+
kwargs.each do |output, evaluator|
|
74
|
+
signal = instance_variable_get("@#{output}")
|
75
|
+
e_args = if override_args.empty?
|
76
|
+
@input_targets.map do |input|
|
77
|
+
instance_variable_get("@#{input}").current
|
78
|
+
end
|
79
|
+
else
|
80
|
+
override_args
|
81
|
+
end
|
82
|
+
if @inputs_as_bool_struct.new(*e_args).instance_exec(&evaluator)
|
83
|
+
signal.on
|
84
|
+
else
|
85
|
+
signal.off
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.assembling(&block)
|
92
|
+
define_method(:assembling) do
|
93
|
+
ret = instance_eval(&block)
|
94
|
+
ret.each { @components << it } if ret.is_a?(Array)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.diagram(source)
|
99
|
+
define_method(:to_s) do
|
100
|
+
source_ = @input_targets.reduce(source) do |result, input|
|
101
|
+
result.gsub(/\(#{input}\)/i, "(#{instance_variable_get("@#{input}")})#{"-" * (input.size - 1)}")
|
102
|
+
end
|
103
|
+
@output_targets.reduce(source_) do |result, output|
|
104
|
+
result.gsub(/\(#{output}\)/i, "#{"-" * (output.size - 1)}(#{instance_variable_get("@#{output}")})")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.truth_table(source)
|
110
|
+
define_method(:truth_table) do
|
111
|
+
rows = source.strip.split("\n").map { |row| row.gsub(/#.*$/, "") }
|
112
|
+
headers = rows.shift.split("|").map(&:strip).reject(&:empty?).map(&:downcase).map(&:to_sym)
|
113
|
+
rows.shift # devide line
|
114
|
+
table = rows.map do |row|
|
115
|
+
row.split("|").map(&:strip).reject(&:empty?).map(&:downcase).map do |v|
|
116
|
+
case v
|
117
|
+
when "^"
|
118
|
+
:clock
|
119
|
+
when "x"
|
120
|
+
:any
|
121
|
+
when "1"
|
122
|
+
true
|
123
|
+
when "0"
|
124
|
+
false
|
125
|
+
else
|
126
|
+
raise "Invalid value in truth table: #{v}" unless headers.include?(v.to_sym)
|
127
|
+
|
128
|
+
[:ref, v.to_sym]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end.select do |values|
|
132
|
+
headers.size == values.size
|
133
|
+
end.map do |values|
|
134
|
+
array = [values]
|
135
|
+
while array.any? { it.any? { |v| v == :any } }
|
136
|
+
target_index = array.find_index { it.any? { |v| v == :any } }
|
137
|
+
target = array[target_index]
|
138
|
+
prop_index = target.find_index { |v| v == :any }
|
139
|
+
array.delete_at(target_index)
|
140
|
+
array.insert(target_index, *[true, false].map do |v|
|
141
|
+
target.dup.tap do |a|
|
142
|
+
a[prop_index] = v
|
143
|
+
end
|
144
|
+
end)
|
145
|
+
end
|
146
|
+
array
|
147
|
+
end.flatten!(1).map do |values|
|
148
|
+
headers.zip(values).to_h
|
149
|
+
end
|
150
|
+
table
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.run(opts = {})
|
155
|
+
::Logicuit.run(new, **opts)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/lib/logicuit/gates/and.rb
CHANGED
@@ -3,18 +3,16 @@
|
|
3
3
|
module Logicuit
|
4
4
|
module Gates
|
5
5
|
# AND gate
|
6
|
-
class And <
|
7
|
-
tag :AND
|
8
|
-
|
6
|
+
class And < DSL
|
9
7
|
diagram <<~DIAGRAM
|
10
|
-
(A)-|
|
8
|
+
(A)-| |
|
11
9
|
|AND|-(Y)
|
12
|
-
(B)-|
|
10
|
+
(B)-| |
|
13
11
|
DIAGRAM
|
14
12
|
|
15
|
-
|
13
|
+
inputs :a, :b
|
16
14
|
|
17
|
-
|
15
|
+
outputs y: -> { a && b }
|
18
16
|
|
19
17
|
truth_table <<~TRUTH_TABLE
|
20
18
|
| A | B | Y |
|
data/lib/logicuit/gates/nand.rb
CHANGED
@@ -3,18 +3,16 @@
|
|
3
3
|
module Logicuit
|
4
4
|
module Gates
|
5
5
|
# NAND gate
|
6
|
-
class Nand <
|
7
|
-
tag :NAND
|
8
|
-
|
6
|
+
class Nand < DSL
|
9
7
|
diagram <<~DIAGRAM
|
10
|
-
(A)-|
|
8
|
+
(A)-| |
|
11
9
|
|NAND|-(Y)
|
12
|
-
(B)-|
|
10
|
+
(B)-| |
|
13
11
|
DIAGRAM
|
14
12
|
|
15
|
-
|
13
|
+
inputs :a, :b
|
16
14
|
|
17
|
-
|
15
|
+
outputs y: -> { !(a && b) }
|
18
16
|
|
19
17
|
truth_table <<~TRUTH_TABLE
|
20
18
|
| A | B | Y |
|
data/lib/logicuit/gates/not.rb
CHANGED
@@ -3,16 +3,14 @@
|
|
3
3
|
module Logicuit
|
4
4
|
module Gates
|
5
5
|
# NOT gate
|
6
|
-
class Not <
|
7
|
-
tag :NOT
|
8
|
-
|
6
|
+
class Not < DSL
|
9
7
|
diagram <<~DIAGRAM
|
10
8
|
(A)-|NOT|-(Y)
|
11
9
|
DIAGRAM
|
12
10
|
|
13
|
-
|
11
|
+
inputs :a
|
14
12
|
|
15
|
-
|
13
|
+
outputs y: -> { !a }
|
16
14
|
|
17
15
|
truth_table <<~TRUTH_TABLE
|
18
16
|
| A | Y |
|
data/lib/logicuit/gates/or.rb
CHANGED
@@ -3,18 +3,16 @@
|
|
3
3
|
module Logicuit
|
4
4
|
module Gates
|
5
5
|
# OR gate
|
6
|
-
class Or <
|
7
|
-
tag :OR
|
8
|
-
|
6
|
+
class Or < DSL
|
9
7
|
diagram <<~DIAGRAM
|
10
|
-
(A)-|
|
8
|
+
(A)-| |
|
11
9
|
|OR|-(Y)
|
12
|
-
(B)-|
|
10
|
+
(B)-| |
|
13
11
|
DIAGRAM
|
14
12
|
|
15
|
-
|
13
|
+
inputs :a, :b
|
16
14
|
|
17
|
-
|
15
|
+
outputs y: -> { a || b }
|
18
16
|
|
19
17
|
truth_table <<~TRUTH_TABLE
|
20
18
|
| A | B | Y |
|
data/lib/logicuit/gates/xor.rb
CHANGED
@@ -3,18 +3,16 @@
|
|
3
3
|
module Logicuit
|
4
4
|
module Gates
|
5
5
|
# XOR gate
|
6
|
-
class Xor <
|
7
|
-
tag :XOR
|
8
|
-
|
6
|
+
class Xor < DSL
|
9
7
|
diagram <<~DIAGRAM
|
10
|
-
(A)-|
|
8
|
+
(A)-| |
|
11
9
|
|XOR|-(Y)
|
12
|
-
(B)-|
|
10
|
+
(B)-| |
|
13
11
|
DIAGRAM
|
14
12
|
|
15
|
-
|
13
|
+
inputs :a, :b
|
16
14
|
|
17
|
-
|
15
|
+
outputs y: -> { (a && !b) || (!a && b) }
|
18
16
|
|
19
17
|
truth_table <<~TRUTH_TABLE
|
20
18
|
| A | B | Y |
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Logicuit module
|
4
|
+
module Logicuit
|
5
|
+
def self.run(circuit, hz: 1, noclear: false)
|
6
|
+
render = lambda {
|
7
|
+
system("clear") unless noclear
|
8
|
+
puts circuit
|
9
|
+
puts
|
10
|
+
puts "tick: #{Signals::Clock.tick_count}" if circuit.clock
|
11
|
+
puts "input: #{circuit.input_targets.join ","}?" if circuit.input_targets.any?
|
12
|
+
}
|
13
|
+
|
14
|
+
if circuit.clock && hz.nonzero?
|
15
|
+
Thread.new do
|
16
|
+
loop do
|
17
|
+
render.call
|
18
|
+
sleep 1.0 / hz
|
19
|
+
Signals::Clock.tick
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
render.call
|
24
|
+
end
|
25
|
+
|
26
|
+
while (input = gets.chomp)
|
27
|
+
key = input.to_sym
|
28
|
+
unless circuit.respond_to? key
|
29
|
+
if circuit.clock && hz.zero?
|
30
|
+
Signals::Clock.tick
|
31
|
+
render.call
|
32
|
+
end
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
signal = circuit.send(key)
|
37
|
+
if signal.current
|
38
|
+
signal.off
|
39
|
+
else
|
40
|
+
signal.on
|
41
|
+
end
|
42
|
+
render.call
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -5,31 +5,39 @@ module Logicuit
|
|
5
5
|
# Clock
|
6
6
|
class Clock
|
7
7
|
def initialize
|
8
|
-
@
|
8
|
+
@downstreams = []
|
9
9
|
@tick_count = 0
|
10
10
|
end
|
11
11
|
|
12
|
-
attr_reader :
|
12
|
+
attr_reader :downstreams, :tick_count
|
13
13
|
|
14
14
|
def tick
|
15
15
|
@tick_count += 1
|
16
|
-
|
16
|
+
# Call the `evaluate` method for all components.
|
17
|
+
# However, the input argument values should be bound to the values at the time `tick` is called.
|
18
|
+
@downstreams.map do |component|
|
19
|
+
args = component.input_targets.map { |input| component.instance_variable_get("@#{input}").current }
|
20
|
+
-> { component.evaluate(*args) }
|
21
|
+
end.each(&:call)
|
17
22
|
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
class << self
|
25
|
+
def instance
|
26
|
+
@instance ||= new
|
27
|
+
end
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
def connects_to(component)
|
30
|
+
instance.downstreams << component
|
31
|
+
end
|
32
|
+
alias >> connects_to
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
def tick
|
35
|
+
instance.tick
|
36
|
+
end
|
30
37
|
|
31
|
-
|
32
|
-
|
38
|
+
def tick_count
|
39
|
+
instance.tick_count
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
35
43
|
end
|