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
|