from migen.fhdl.structure import *
from migen.bank.description import *
from migen.corelogic.misc import optree

class EventSource:
	def __init__(self):
		self.trigger = Signal()

class EventSourcePulse(EventSource):
	pass

class EventSourceLevel(EventSource):
	pass

class EventManager:
	def __init__(self, *sources):
		self.sources = sources
		self.irq = Signal()
		n = len(self.sources)
		self.status = RegisterRaw("status", n)
		self.pending = RegisterRaw("pending", n)
		self.enable = RegisterFields("enable",
		  [Field("s" + str(i), access_bus=READ_WRITE, access_dev=READ_ONLY) for i in range(n)])
	
	def get_registers(self):
		return [self.status, self.pending, self.enable]
	
	def get_fragment(self):
		comb = []
		sync = []
		
		# status
		for i, source in enumerate(self.sources):
			if isinstance(source, EventSourcePulse):
				comb.append(self.status.w[i].eq(0))
			elif isinstance(source, EventSourceLevel):
				comb.append(self.status.w[i].eq(source.trigger))
			else:
				raise TypeError
		
		# pending
		for i, source in enumerate(self.sources):
			pending = Signal()
			# W1C
			sync.append(If(self.pending.re & self.pending.r[i], pending.eq(0)))
			if isinstance(source, EventSourcePulse):
				# set on a positive trigger pulse
				sync.append(If(source.trigger, pending.eq(1)))
			elif isinstance(source, EventSourceLevel):
				# set on the falling edge of the trigger
				old_trigger = Signal()
				sync += [
					old_trigger.eq(source.trigger),
					If(~source.trigger & old_trigger, pending.eq(1))
				]
			else:
				raise TypeError
			comb.append(self.pending.w[i].eq(pending))
		
		# IRQ
		irqs = [self.pending.w[i] & field.r for i, field in enumerate(self.enable.fields)]
		comb.append(self.irq.eq(optree("|", irqs)))
		
		return Fragment(comb, sync)