# SPDX-License-Identifier: BSD-2-Clause

from .. import Elaboratable, Module, Signal

__all__ = (
	'RoundRobin',
)


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

	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
	is updated.

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

	Attributes
	----------
	requests : Signal(count), in
		Set of requests.
	grant : Signal(range(count)), out
		Number of the granted request. Does not change if there are no
		active requests.
	valid : Signal(), out
		Asserted if grant corresponds to an active request. De-asserted
		otherwise, i.e. if no requests are active.

	'''

	def __init__(self, *, count: int) -> None:
		if not isinstance(count, int) or count < 0:
			raise ValueError(f'Count must be a non-negative integer, not {count!r}')
		self.count    = count

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

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

		with m.Switch(self.grant):
			for i in range(self.count):
				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.count)):
						with m.If(self.requests[succ]):
							m.d.sync += self.grant.eq(succ)

		m.d.sync += self.valid.eq(self.requests.any())

		return m
