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

nmigen.lib: Add RoundRobin #450

Merged
merged 20 commits into from
Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f233f09
nmigen.lib: Add RoundRobin
Jul 24, 2020
30176ab
nmigen.compat.genlib: Use RoundRobin implementation from oMigen
Jul 24, 2020
37c14c4
Remove buggy formal test case
Jul 24, 2020
bb2fafd
Revamp RoundRobin interface and tests, add documentation
Jul 24, 2020
24d9954
nmigen.test.test_lib_roundrobin: Validate width parameter
Jul 24, 2020
fed5de2
nmigen.test.test_lib_roundrobin: Add tests for parameters, add simula…
Jul 24, 2020
578b554
nmigen.test.test_lib_roundrobin: Add delay to take delta cycle into a…
Jul 24, 2020
237a505
nmigen.lib.roundrobin: Remove EnableInserter
Jul 27, 2020
77c99b9
nmigen.lib.roundrobin: Fix ValueError message in constructor
Jul 28, 2020
0e00f12
nmigen.test.test_lib_roundrobin: Fix test_wrong_width regex
Jul 28, 2020
dbd3926
nmigen.lib.roundrobin: Add valid output
Jul 28, 2020
69d697e
nmigen.lib.roundrobin: Use .any() instead of reduce(or_, ...)
Jul 28, 2020
de770e5
nmigen.compat.genlib.roundrobin: Update deprecation warning, make it …
Jul 28, 2020
7680568
nmigen.lib.roundrobin: Reformulate grant and valid descriptions
Jul 28, 2020
4f9c2ad
nmigen.lib.roundrobin: Update Elaboratable and attributes documentation
Jul 28, 2020
1eac37c
Rename nmigen.lib.roundrobin to nmigen.lib.scheduler
Jul 28, 2020
f92af9b
Update nmigen/compat/genlib/roundrobin.py
Jul 28, 2020
ec4c336
Update nmigen/compat/genlib/roundrobin.py
Jul 28, 2020
ff198d1
nmigen.lib.scheduler: Rename width to count in RoundRobin
Jul 28, 2020
74c3ec4
Merge branch 'roundrobin' of github.com:jeanthom/nmigen into roundrobin
Jul 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions nmigen/compat/genlib/roundrobin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from ..._utils import deprecated
from ..fhdl.module import CompatModule


__all__ = ["RoundRobin", "SP_WITHDRAW", "SP_CE"]

(SP_WITHDRAW, SP_CE) = range(2)

@deprecated("instead of `migen.genlib.roundrobin.RoundRobin`, "
"use `nmigen.lib.roundrobin.RoundRobin`; note that RoundRobin does not "
"require a policy anymore, and that the `ce` attribute has been renamed"
"to `en`")
jeanthom marked this conversation as resolved.
Show resolved Hide resolved
class CompatRoundRobin(CompatModule):
def __init__(self, n, switch_policy=SP_WITHDRAW):
self.request = Signal(n)
self.grant = Signal(max=max(2, n))
self.switch_policy = switch_policy
if self.switch_policy == SP_CE:
self.ce = Signal()

###

if n > 1:
cases = {}
for i in range(n):
switch = []
for j in reversed(range(i+1, i+n)):
t = j % n
switch = [
If(self.request[t],
self.grant.eq(t)
).Else(
*switch
)
]
if self.switch_policy == SP_WITHDRAW:
case = [If(~self.request[i], *switch)]
else:
case = switch
cases[i] = case
statement = Case(self.grant, cases)
if self.switch_policy == SP_CE:
statement = If(self.ce, statement)
self.sync += statement
else:
self.comb += self.grant.eq(0)



RoundRobin = CompatRoundRobin
54 changes: 54 additions & 0 deletions nmigen/lib/roundrobin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from .. import *
from functools import reduce
from operator import or_


__all__ = ["RoundRobin"]


class RoundRobin(Elaboratable):
"""Round-robin scheduler.

For a given set of requests, the round-robin scheduler will
grant one request.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this should be elaborated. Something like (phrasing needs to be improved):

For a given set of requests, the round-robin scheduler will grant one request. Once it grants a request, if any other requests are active, it grants the next active request with a greater number, restarting from zero once it reaches the highest one. Use :class:EnableInserter to control when the scheduler advances.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tweaked a little bit what you wrote. What do you think?


Parameters
----------
width : int
Number of requests.

Attributes
----------
requests : Signal(width), in
Set of requests.
grant : Signal(range(width)), out
Number of the granted request.
jeanthom marked this conversation as resolved.
Show resolved Hide resolved
jeanthom marked this conversation as resolved.
Show resolved Hide resolved
valid : Signal(), out
Indicate if grant is valid.
jeanthom marked this conversation as resolved.
Show resolved Hide resolved
jeanthom marked this conversation as resolved.
Show resolved Hide resolved
"""
def __init__(self, *, width):
if not isinstance(width, int) or width < 0:
raise ValueError("Width must be a non-negative integer, not {!r}"
.format(width))
self.width = width

self.requests = Signal(width)
self.grant = Signal(range(width))
self.valid = Signal()

def elaborate(self, platform):
m = Module()

with m.Switch(self.grant):
for i in range(self.width):
with m.Case(i):
for pred in reversed(range(i)):
with m.If(self.requests[pred]):
m.d.sync += self.grant.eq(pred)
for succ in reversed(range(i + 1, self.width)):
with m.If(self.requests[succ]):
m.d.sync += self.grant.eq(succ)

m.d.sync += self.valid.eq(reduce(or_, self.requests))
jeanthom marked this conversation as resolved.
Show resolved Hide resolved

return m
93 changes: 93 additions & 0 deletions nmigen/test/test_lib_roundrobin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# nmigen: UnusedElaboratable=no
import unittest
from .utils import *
from ..hdl import *
from ..asserts import *
from ..sim.pysim import *
from ..lib.roundrobin import *


class RoundRobinTestCase(unittest.TestCase):
def test_width(self):
dut = RoundRobin(width=32)
self.assertEqual(dut.width, 32)
self.assertEqual(len(dut.requests), 32)
self.assertEqual(len(dut.grant), 5)

def test_wrong_width(self):
with self.assertRaisesRegex(ValueError, r"Width must be a non-negative integer, not 'foo'"):
dut = RoundRobin(width="foo")
with self.assertRaisesRegex(ValueError, r"Width must be a non-negative integer, not -1"):
dut = RoundRobin(width=-1)


class RoundRobinSimulationTestCase(unittest.TestCase):
def test_width_one(self):
dut = RoundRobin(width=1)
sim = Simulator(dut)
def process():
yield dut.requests.eq(0)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 0)
self.assertFalse((yield dut.valid))

yield dut.requests.eq(1)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 0)
self.assertTrue((yield dut.valid))
sim.add_sync_process(process)
sim.add_clock(1e-6)
with sim.write_vcd("test.vcd"):
sim.run()

def test_transitions(self):
dut = RoundRobin(width=3)
sim = Simulator(dut)
def process():
yield dut.requests.eq(0b111)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 1)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b110)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 2)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b010)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 1)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b011)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 0)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b001)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 0)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b101)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 2)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b100)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 2)
self.assertTrue((yield dut.valid))

yield dut.requests.eq(0b000)
yield; yield Delay(1e-8)
self.assertFalse((yield dut.valid))

yield dut.requests.eq(0b001)
yield; yield Delay(1e-8)
self.assertEqual((yield dut.grant), 0)
self.assertTrue((yield dut.valid))
sim.add_sync_process(process)
sim.add_clock(1e-6)
with sim.write_vcd("test.vcd"):
sim.run()