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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51ce3fab568abef949848bf4f1cb8c8dbe3f0f80da773b9f6f48ea374e21c376
4
- data.tar.gz: 787d8ae4a61d770bba76612b5a7ffffbee750dfe0d645dae4a89c57e130ecdc1
3
+ metadata.gz: 1a2841e79a0daf8046710c6bd43d8862a3d9f28f482ab2b9cac2886b271f571f
4
+ data.tar.gz: 679b263308e15b0da229573bfd7be5cf02c8e65bd98fb01753874afb3fe56413
5
5
  SHA512:
6
- metadata.gz: 28dcfd69c6f889c27283f46928dabcf178ba0113609f36895ab4e10642e36eff631070573946b5d5c7dd4ab06180d7b5649e524d855c977ab361d20675ece5b6
7
- data.tar.gz: 4b253121ffffc6d0f06e53f07c23bde1bc371c91b2217ba91cfe46fd134376b0c2c0dac7146afad73d12f02a8a7fc1d0ad1242c85a11888a1b00434a63c5ceaf
6
+ metadata.gz: 3e942269a9b55dad3f7efff1d68935f431952d0c3276d7d290461dabe84230f6d6d1d0eb2f307b6dd375c2d3b1261c8e2e7c72f1faa17db0de576cee9c37c0da
7
+ data.tar.gz: 3f1428cced00eee1798ea56dc9cfb169eb041b40bfc514a729d1cd3d67ce4b05d93ea633a9290c3ac22432aa1bfeb384e8de20cee59a0e463c582bf82be61ea4
data/.rubocop.yml CHANGED
@@ -31,6 +31,4 @@ Metrics:
31
31
 
32
32
  plugins:
33
33
  - rubocop-minitest
34
-
35
- require:
36
34
  - rubocop-rake
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 "#{a3}#{a2}#{a1}#{a0}"
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
- output.split("").zip([d7, d6, d5, d4, d3, d2, d1, d0]).each do |v, o|
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 << it } if ret.is_a?(Array)
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? { it.any? { |v| v == :any } }
136
- target_index = array.find_index { it.any? { |v| v == :any } }
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 { it.current ? "1" : "0" }.join
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Logicuit
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
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.0
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: 2025-04-19 00:00:00.000000000 Z
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.5
71
+ rubygems_version: 3.6.7
72
72
  specification_version: 4
73
73
  summary: logi(c cir)cuit -> logicuit
74
74
  test_files: []