from migen.fhdl.structure import *
from migen.fhdl.specials import Special
from migen.fhdl.tools import value_bits_sign, list_signals

class MultiRegImpl:
	def __init__(self, i, idomain, o, odomain, n):
		self.i = i
		self.idomain = idomain
		self.o = o
		self.odomain = odomain

		w, signed = value_bits_sign(self.i)
		self.regs = [Signal((w, signed)) for i in range(n)]

	def get_fragment(self):
		src = self.i
		o_sync = []
		for reg in self.regs:
			o_sync.append(reg.eq(src))
			src = reg
		comb = [
			self.o.eq(src)
		]
		return Fragment(comb, {self.odomain: o_sync})

class MultiReg(Special):
	def __init__(self, i, idomain, o, odomain, n=2):
		Special.__init__(self)
		self.i = i
		self.idomain = idomain
		self.o = o
		self.odomain = odomain
		self.n = n

	def list_ios(self, ins, outs, inouts):
		r = set()
		if ins:
			r.update(list_signals(self.i))
		if outs:
			r.update(list_signals(self.o))
		return r

	@staticmethod
	def lower(dr):
		return MultiRegImpl(dr.i, dr.idomain, dr.o, dr.odomain, dr.n)

class PulseSynchronizer:
	def __init__(self, idomain, odomain):
		self.idomain = idomain
		self.odomain = odomain
		self.i = Signal()
		self.o = Signal()

	def get_fragment(self):
		toggle_i = Signal()
		toggle_o = Signal()
		toggle_o_r = Signal()
		sync_i = [
			If(self.i, toggle_i.eq(~toggle_i))
		]
		sync_o = [
			toggle_o_r.eq(toggle_o)
		]
		comb = [
			self.o.eq(toggle_o ^ toggle_o_r)
		]
		return Fragment(comb, 
			{self.idomain: sync_i, self.odomain: sync_o},
			specials={MultiReg(toggle_i, self.idomain, toggle_o, self.odomain)})