logicuit 0.3.0 → 0.3.1
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 +0 -2
- data/README.md +68 -13
- data/lib/logicuit/circuits/td4/rom.rb +2 -4
- data/lib/logicuit/dsl.rb +28 -3
- data/lib/logicuit/signals/signal_group.rb +7 -1
- data/lib/logicuit/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a2841e79a0daf8046710c6bd43d8862a3d9f28f482ab2b9cac2886b271f571f
|
4
|
+
data.tar.gz: 679b263308e15b0da229573bfd7be5cf02c8e65bd98fb01753874afb3fe56413
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e942269a9b55dad3f7efff1d68935f431952d0c3276d7d290461dabe84230f6d6d1d0eb2f307b6dd375c2d3b1261c8e2e7c72f1faa17db0de576cee9c37c0da
|
7
|
+
data.tar.gz: 3f1428cced00eee1798ea56dc9cfb169eb041b40bfc514a729d1cd3d67ce4b05d93ea633a9290c3ac22432aa1bfeb384e8de20cee59a0e463c582bf82be61ea4
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,50 @@
|
|
1
1
|
# Logicuit
|
2
2
|
|
3
|
+
```
|
4
|
+
******* ******* *******
|
5
|
+
********* ********* *********
|
6
|
+
*********** *********** ***********
|
7
|
+
********* ********* *********
|
8
|
+
******* ******* *******
|
9
|
+
|
10
|
+
+-----------------------------------------------+ OUT 0111
|
11
|
+
| | ADD A,0001
|
12
|
+
+--->|rg_a|(0111)----->| | | JNC 0001
|
13
|
+
| |1000| | | | ADD A,0001
|
14
|
+
| | | | > JNC 0011
|
15
|
+
+--->|rg_b|(0000)----->| |----------->| |---+ OUT 0110
|
16
|
+
| |0000| | | | | ADD A,0001
|
17
|
+
| |SEL| |ALU| JNC 0110
|
18
|
+
+--->| out| | in|--->| | | | ADD A,0001
|
19
|
+
| |0111| |0000| | | | im|--->| |--(0) JNC 1000
|
20
|
+
| | | |0011| OUT 0000
|
21
|
+
+--->| pc| (0000)--->| | OUT 0100
|
22
|
+
|0100| ADD A,0001
|
23
|
+
JNC 1010
|
24
|
+
OUT 1000
|
25
|
+
JMP 1111
|
26
|
+
|
27
|
+
tick: 48
|
28
|
+
input: in0,in1,in2,in3?
|
29
|
+
```
|
30
|
+
|
3
31
|
From logic circuit to Logicuit — a playful portmanteau.
|
4
32
|
|
5
33
|
A Ruby-based logic circuit simulator featuring an internal DSL for building circuits.
|
6
34
|
|
35
|
+
## Table of Contents
|
36
|
+
|
37
|
+
- [Installation](#installation)
|
38
|
+
- [DSL](#dsl)
|
39
|
+
- [Interactive Execution](#interactive-execution)
|
40
|
+
- [Assembling](#assembling)
|
41
|
+
- [SignalGroup](#signalgroup)
|
42
|
+
- [Sequential Circuits](#sequential-circuits)
|
43
|
+
- [Demo: Ramen Timer](#demo-ramen-timer)
|
44
|
+
- [Development](#development)
|
45
|
+
- [Contributing](#contributing)
|
46
|
+
- [License](#license)
|
47
|
+
|
7
48
|
## Installation
|
8
49
|
|
9
50
|
Install the gem and add to the application's Gemfile by executing:
|
@@ -25,7 +66,7 @@ You can define inputs, outputs, and even a visual diagram — all within a Ruby
|
|
25
66
|
|
26
67
|
Here is an example of a simple 2-input AND gate:
|
27
68
|
|
28
|
-
```
|
69
|
+
```ruby
|
29
70
|
require "logicuit"
|
30
71
|
|
31
72
|
class MyAndGate < Logicuit::DSL
|
@@ -77,7 +118,7 @@ This approach gives you more control and expressiveness when building complex ci
|
|
77
118
|
|
78
119
|
Here's an example of a 2-to-1 multiplexer:
|
79
120
|
|
80
|
-
```
|
121
|
+
```ruby
|
81
122
|
require "logicuit"
|
82
123
|
|
83
124
|
class MyMultiplexer < Logicuit::DSL
|
@@ -125,7 +166,7 @@ This allows the code to resemble the actual structure of the circuit, making it
|
|
125
166
|
|
126
167
|
For example:
|
127
168
|
|
128
|
-
```
|
169
|
+
```ruby
|
129
170
|
a >> not_gate.a
|
130
171
|
not_gate.y >> and_gate1.b
|
131
172
|
```
|
@@ -156,14 +197,14 @@ Logicuit provides a convenient way to express these kinds of connections using s
|
|
156
197
|
|
157
198
|
These two lines:
|
158
199
|
|
159
|
-
```
|
200
|
+
```ruby
|
160
201
|
a >> xor_gate.a
|
161
202
|
a >> and_gate.a
|
162
203
|
```
|
163
204
|
|
164
205
|
can be written more concisely as:
|
165
206
|
|
166
|
-
```
|
207
|
+
```ruby
|
167
208
|
a >> [xor_gate.a, and_gate.a]
|
168
209
|
```
|
169
210
|
|
@@ -173,7 +214,7 @@ The array on the right-hand side is treated as a signal group, and the connectio
|
|
173
214
|
|
174
215
|
You can also connect multiple outputs to multiple inputs at once by using the [] method to access signals by name:
|
175
216
|
|
176
|
-
```
|
217
|
+
```ruby
|
177
218
|
pc.qa >> rom.a0
|
178
219
|
pc.qb >> rom.a1
|
179
220
|
pc.qc >> rom.a2
|
@@ -182,7 +223,7 @@ pc.qd >> rom.a3
|
|
182
223
|
|
183
224
|
is equivalent to:
|
184
225
|
|
185
|
-
```
|
226
|
+
```ruby
|
186
227
|
pc[:qa, :qb, :qc, :qd] >> rom[:a0, :a1, :a2, :a3]
|
187
228
|
```
|
188
229
|
|
@@ -196,7 +237,7 @@ What if you want to connect signals from multiple different components as a sing
|
|
196
237
|
|
197
238
|
You can use `Logicuit::ArrayAsSignalGroup`, which adds signal group behavior to arrays:
|
198
239
|
|
199
|
-
```
|
240
|
+
```ruby
|
200
241
|
using Logicuit::ArrayAsSignalGroup
|
201
242
|
|
202
243
|
assembling do
|
@@ -212,7 +253,7 @@ In addition to combinational circuits, Logicuit also supports sequential circuit
|
|
212
253
|
|
213
254
|
For example, here’s a D flip-flop:
|
214
255
|
|
215
|
-
```
|
256
|
+
```ruby
|
216
257
|
require "logicuit"
|
217
258
|
|
218
259
|
class MyDFlipFlop < Logicuit::DSL
|
@@ -261,7 +302,7 @@ The number of elapsed ticks is shown as `tick: N`.
|
|
261
302
|
|
262
303
|
By default, the clock ticks at 1 Hz (once per second). You can change the frequency by passing the `hz:` option to `run`:
|
263
304
|
|
264
|
-
```
|
305
|
+
```ruby
|
265
306
|
MyDFlipFlop.run(hz: 10)
|
266
307
|
```
|
267
308
|
|
@@ -271,7 +312,7 @@ If you want full control, you can set `hz: 0` to disable automatic ticking.
|
|
271
312
|
|
272
313
|
In this mode, the clock only ticks when you press Enter, allowing you to step through the simulation manually:
|
273
314
|
|
274
|
-
```
|
315
|
+
```ruby
|
275
316
|
MyDFlipFlop.run(hz: 0)
|
276
317
|
```
|
277
318
|
|
@@ -283,7 +324,7 @@ You can build sequential circuits out of smaller components using the `assemblin
|
|
283
324
|
|
284
325
|
Here’s an example of a 4-bit register that stores its input when the load signal `ld` is not active:
|
285
326
|
|
286
|
-
```
|
327
|
+
```ruby
|
287
328
|
require "logicuit"
|
288
329
|
|
289
330
|
class MyRegister4bit < Logicuit::DSL
|
@@ -336,12 +377,26 @@ If your circuit contains one or more sequential components (such as D flip-flops
|
|
336
377
|
|
337
378
|
The clock signal is automatically connected to all internal sequential components. You don't need to wire it manually — just declare it at the top level:
|
338
379
|
|
339
|
-
```
|
380
|
+
```ruby
|
340
381
|
inputs ..., clock: :ck
|
341
382
|
```
|
342
383
|
|
343
384
|
> Note: If you forget to declare a clock input, Logicuit won't know it's a sequential circuit — even if you include flip-flops internally. Always include `clock:` to enable timing.
|
344
385
|
|
386
|
+
### Demo: Ramen Timer
|
387
|
+
|
388
|
+
Logicuit comes with a simple demo circuit — a working 4-bit CPU based on the TD4 architecture described in the book [CPUの創りかた](https://www.amazon.co.jp/dp/4839909865).
|
389
|
+
|
390
|
+
You can try it out by running:
|
391
|
+
|
392
|
+
```
|
393
|
+
ruby -r logicuit -e 'Logicuit::Circuits::Td4::Cpu.run'
|
394
|
+
```
|
395
|
+
|
396
|
+
This launches a fully functional CPU simulation that counts down from a programmed value — perfect for timing your instant ramen 🍜
|
397
|
+
|
398
|
+
The TD4 CPU is built entirely from logic gates and flip-flops, assembled using Logicuit’s DSL. It’s a great demonstration of how small components can be combined to create a complete digital system.
|
399
|
+
|
345
400
|
## Development
|
346
401
|
|
347
402
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -12,7 +12,7 @@ module Logicuit
|
|
12
12
|
def evaluate
|
13
13
|
return unless initialized
|
14
14
|
|
15
|
-
output = case
|
15
|
+
output = case self[:a3, :a2, :a1, :a0].to_s
|
16
16
|
in "0000" then "10110111"
|
17
17
|
in "0001" then "00000001"
|
18
18
|
in "0010" then "11100001"
|
@@ -30,9 +30,7 @@ module Logicuit
|
|
30
30
|
in "1110" then "10111000"
|
31
31
|
in "1111" then "11111111"
|
32
32
|
end
|
33
|
-
|
34
|
-
v == "1" ? o.on : o.off
|
35
|
-
end
|
33
|
+
self[:d7, :d6, :d5, :d4, :d3, :d2, :d1, :d0].set output
|
36
34
|
end
|
37
35
|
|
38
36
|
truth_table <<~TRUTH_TABLE
|
data/lib/logicuit/dsl.rb
CHANGED
@@ -91,7 +91,7 @@ module Logicuit
|
|
91
91
|
def self.assembling(&block)
|
92
92
|
define_method(:assembling) do
|
93
93
|
ret = instance_eval(&block)
|
94
|
-
ret.each { @components <<
|
94
|
+
ret.each { @components << _1 } if ret.is_a?(Array)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -132,8 +132,8 @@ module Logicuit
|
|
132
132
|
headers.size == values.size
|
133
133
|
end.map do |values|
|
134
134
|
array = [values]
|
135
|
-
while array.any? {
|
136
|
-
target_index = array.find_index {
|
135
|
+
while array.any? { _1.any? { |v| v == :any } }
|
136
|
+
target_index = array.find_index { _1.any? { |v| v == :any } }
|
137
137
|
target = array[target_index]
|
138
138
|
prop_index = target.find_index { |v| v == :any }
|
139
139
|
array.delete_at(target_index)
|
@@ -151,6 +151,31 @@ module Logicuit
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
+
def self.verify_against_truth_table
|
155
|
+
new.truth_table.each do |row|
|
156
|
+
args = row.values_at(*new.input_targets).map { _1 ? 1 : 0 }
|
157
|
+
subject = new(*args)
|
158
|
+
|
159
|
+
previous_values = row.reject do |_k, v|
|
160
|
+
v == :clock
|
161
|
+
end.keys.reduce({}) { |acc, key| acc.merge(key => subject.send(key).current) }
|
162
|
+
|
163
|
+
Signals::Clock.tick if row.values.find :clock
|
164
|
+
|
165
|
+
row.each do |key, value|
|
166
|
+
next if value == :clock
|
167
|
+
|
168
|
+
if value.is_a?(Array) && value.first == :ref
|
169
|
+
expected = previous_values[value.last]
|
170
|
+
|
171
|
+
raise "#{self}.new(#{args.join(", ")}).#{key} should be #{expected}" unless expected == subject.send(key).current
|
172
|
+
else
|
173
|
+
raise "#{self}.new(#{args.join(", ")}).#{key} should be #{value}" unless value == subject.send(key).current
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
154
179
|
def self.run(opts = {})
|
155
180
|
::Logicuit.run(new, **opts)
|
156
181
|
end
|
@@ -19,7 +19,13 @@ module Logicuit
|
|
19
19
|
alias >> connects_to
|
20
20
|
|
21
21
|
def to_s
|
22
|
-
signals.map {
|
22
|
+
signals.map { _1.current ? "1" : "0" }.join
|
23
|
+
end
|
24
|
+
|
25
|
+
def set(vals)
|
26
|
+
vals.split("").zip(signals).each do |v, o|
|
27
|
+
v == "1" ? o.on : o.off
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
data/lib/logicuit/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logicuit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koji NAKAMURA
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: logi(c cir)cuit -> logicuit
|
13
13
|
email:
|
@@ -68,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
requirements: []
|
71
|
-
rubygems_version: 3.6.
|
71
|
+
rubygems_version: 3.6.7
|
72
72
|
specification_version: 4
|
73
73
|
summary: logi(c cir)cuit -> logicuit
|
74
74
|
test_files: []
|