circuit-fibsqrt
Circuit-Fibsqrt
Overview
This skill provides guidance for implementing mathematical computations as gate-level circuits. It covers combinational logic (adders, comparators, multiplexers) and sequential logic (feedback-based iteration) in text-based circuit simulators that use event-driven simulation.
When to Use This Skill
Use this skill when:
- Building circuits that compute mathematical functions (square root, Fibonacci, etc.)
- Working with text-based gate netlists (e.g.,
gates.txtformat) - Implementing multi-bit arithmetic operations at the gate level
- Debugging circuits in event-driven simulators with feedback loops
- Optimizing gate counts to meet resource constraints
Approach: Component-First Development
Step 1: Understand the Simulator Semantics
Before writing any circuit code:
-
Read example files carefully - Examine any provided example gate files to understand:
- Input signal handling (e.g.,
out{i} = out{i}pattern for preserving inputs) - Gate syntax and argument ordering
- How feedback loops are represented
- Input signal handling (e.g.,
-
Establish conventions - Document clearly:
- Mux semantics: Does
mux(sel, a, b)selectawhensel=0orsel=1? - Signal naming conventions
- Bit ordering (LSB vs MSB first)
- Mux semantics: Does
-
Test simulator behavior - Create minimal test circuits to verify:
- Feedback loop behavior (toggle tests)
- Multi-cycle convergence
- Event-driven vs synchronous semantics
Step 2: Paper-Trace Algorithms First
Before implementing complex algorithms:
- Work through small examples by hand - For integer square root, trace through isqrt(16), isqrt(17), isqrt(100)
- Verify mathematical correctness - Ensure formulas are correct before coding (e.g., Newton-Raphson, binary search bounds)
- Identify iteration counts - Determine how many iterations are needed for the input range
Step 3: Estimate Resource Usage
Before implementation:
- Calculate expected gate counts - A 32-bit multiplier may require 30,000+ gates
- Compare against limits - If limit is 32,000 gates, avoid multiplication-heavy approaches
- Choose appropriate algorithms - Binary search isqrt avoids multiplication; comparison-based approaches are gate-efficient
Step 4: Build and Test Components Independently
Implement in isolation before combining:
-
Primitive gates first:
- AND, OR, XOR, NOT
- MUX (multiplexer)
-
Arithmetic building blocks:
- Half adder, full adder
- N-bit ripple-carry adder
- N-bit subtractor (adder with inverted operand + carry-in)
- N-bit comparator (A < B, A == B)
-
Test each component:
- Unit test adders with known values
- Verify comparators with edge cases (0, max value, equal values)
- Test mux selection in both directions
Step 5: Implement Algorithm-Specific Logic
For isqrt (integer square root):
- Binary search approach: Start with bounds [0, 2^16], narrow by comparing mid^2 with input
- Avoid direct multiplication if gate-constrained; use iterative addition or precomputed squares
- Handle edge cases: isqrt(0)=0, isqrt(1)=1
For Fibonacci:
- Implement as sequential state machine using feedback
- Two registers: fib_prev and fib_curr
- Iterate based on iteration count from isqrt result
- Handle edge cases: fib(0)=0, fib(1)=1
Step 6: Combine and Integrate
When combining components:
- Use clear signal naming to track data flow
- Verify interface widths match between components
- Test the combined circuit with known input/output pairs
Verification Strategies
Unit Testing
- Test primitives in isolation - Create separate test scripts for each component
- Use known test vectors - For adders: 0+0=0, 1+1=2, max+1=overflow
- Edge case coverage:
- Zero inputs
- Maximum value inputs
- Boundary conditions (e.g., perfect squares for isqrt)
Integration Testing
- Test with small inputs first - Verify fib(isqrt(1)), fib(isqrt(4)), fib(isqrt(9))
- Verify intermediate values - Check isqrt output before Fibonacci computation
- Test large inputs - Ensure overflow handling works correctly
Debugging Techniques
- Add debug outputs - Temporarily expose internal signals for inspection
- Trace signal propagation - Follow a known input through the circuit manually
- Binary search for bugs - If output is wrong, test intermediate stages to isolate the issue
Common Pitfalls
Input Signal Handling
Mistake: Not preserving input signals in the gate file.
Solution: Many simulators require explicit input preservation:
out0 = out0 # Preserve input bit 0
out1 = out1 # Preserve input bit 1
...
Read example files to identify this pattern before implementation.
Algorithm Implementation Errors
Mistake: Implementing formulas without verification.
Example: Using test_val = 2*res + 1 when the correct formula is different for the chosen iteration method.
Solution: Paper-trace the algorithm with concrete values before coding.
Gate Count Explosion
Mistake: Using multiplication without estimating gate cost.
Example: A 32x32 multiplier can require 30,000+ gates, exceeding typical limits.
Solution:
- Estimate gates before implementation
- Prefer comparison-based or additive approaches when possible
- Consider iterative algorithms that reuse circuitry
Off-by-One Errors
Mistake: Getting fib(k-1) instead of fib(k) due to iteration count errors.
Solution:
- Clearly define what iteration 0, 1, 2, ... produce
- Trace through fib(0), fib(1), fib(2) by hand to verify indexing
Feedback Loop Confusion
Mistake: Misunderstanding how feedback stabilizes in event-driven simulation.
Example: A toggle test showing 0 after even iterations is correct, not broken.
Solution:
- Test feedback loops with minimal circuits first
- Understand that value after N steps depends on initial value and operation
Mux Argument Order
Mistake: Confusing which input is selected when selector is 0 vs 1.
Solution:
- Establish and document convention at the start
- Test with a minimal mux circuit before using in larger designs
Recommended Workflow
- Read all provided examples and documentation
- Paper-trace the algorithm with test values
- Estimate gate counts for chosen approach
- Build and test primitive gates
- Build and test arithmetic components
- Build and test algorithm-specific logic
- Integrate components
- Test with edge cases
- Optimize if needed (reduce gates, fix bugs)
Testing Checklist
- Input signals preserved correctly
- Adder produces correct sums
- Comparator handles all cases (less, equal, greater)
- isqrt(0) = 0
- isqrt(1) = 1
- isqrt(perfect square) = exact root
- fib(0) = 0
- fib(1) = 1
- Combined circuit matches expected outputs
- Gate count within limits