Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do you make a bounded up-down counter without modulo? #492

Closed
BrettRD opened this issue Aug 27, 2020 · 4 comments
Closed

How do you make a bounded up-down counter without modulo? #492

BrettRD opened this issue Aug 27, 2020 · 4 comments
Labels

Comments

@BrettRD
Copy link

@BrettRD BrettRD commented Aug 27, 2020

Modulo operators for negative numbers are a known issue, and the operator is not implemented on stable.
What is the work around for a decrement to underflow around an arbitrary bounds?

This test shows the modulo producing strange results on decrement that disappear if you force the range into positive.

from nmigen import *
from nmigen.back.pysim import Simulator

class counter_thing(Elaboratable):
    def __init__(self, max_count, rst):
        self.dir = Signal(1)
        self.mod_pos = Signal(range(max_count), reset=rst)
        self.mod_neg = Signal(range(max_count), reset=rst)
    def elaborate(self, platform):
        m = Module()
        m.d.sync += self.mod_pos.eq((self.mod_pos + max_count - 1) % max_count)
        m.d.sync += self.mod_neg.eq((self.mod_neg - 1) % max_count)
        return m

def test():
    count = rst
    for _ in range(2*max_count):
        yield
        count = (count - 1) % max_count
        print(str((yield dut.mod_pos)) + ', ' + str((yield dut.mod_neg)) + ', ' + str(count))
        assert (yield dut.mod_pos) == count # passes
        assert (yield dut.mod_pos) == (yield dut.mod_neg) # fails after underflow

if __name__ == "__main__":
    max_count = 17
    rst = 13
    dut = counter_thing(max_count, rst)
    sim = Simulator(dut)
    sim.add_clock(1)
    sim.add_sync_process(test) # or sim.add_sync_process(process), see below
    sim.run()
@whitequark
Copy link
Member

@whitequark whitequark commented Aug 29, 2020

What is the work around for a decrement to underflow around an arbitrary bounds?

Assuming you are interested in a counter that increments or decrements by 1 each cycle, something like this would give you arbitrary overflow bounds:

class BoundedCounter(Elaboratable):
    def __init__(self, min, max):
        self.min = min
        self.max = max

        self.up = Signal(1)
        self.count = Signal(range(min, max + 1))

    def elaborate(self, platform):
        m = Module()
        with m.If(self.up):
            with m.If(self.count == max):
                m.d.sync += self.count.eq(min)
            with m.Else():
                m.d.sync += self.count.eq(self.count + 1)
        with m.Else():
            with m.If(self.count == min):
                m.d.sync += self.count.eq(max)
            with m.Else():
                m.d.sync += self.count.eq(self.count - 1)
        return m

@whitequark
Copy link
Member

@whitequark whitequark commented Sep 4, 2020

Does this answer your question?

@DaKnig
Copy link

@DaKnig DaKnig commented Sep 4, 2020

I'd suggest to avoid the % and / operators in hardware design (when you divide by a number that is not a power of 2). division is slow and expensive in hardware, it would take more than one clock at a rate users usually want to work, that is why usually backends don't implement that in synthesizable code regardless of what language you would use.

@BrettRD
Copy link
Author

@BrettRD BrettRD commented Sep 4, 2020

This answers my question,
Thanks.

@BrettRD BrettRD closed this Sep 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants