from migen.fhdl.structure import *
from migen.corelogic.record import *
from migen.corelogic.fsm import *
from migen.flow.actor import *

# Generates integers from start to maximum-1
class IntSequence(Actor):
	def __init__(self, nbits, offsetbits=0, step=1):
		self.nbits = nbits
		self.offsetbits = offsetbits
		self.step = step
		
		parameters_layout = [("maximum", BV(self.nbits))]
		if self.offsetbits:
			parameters_layout.append(("offset", BV(self.offsetbits)))
		
		super().__init__(
			("parameters", Sink, parameters_layout),
			("source", Source, [("value", BV(max(self.nbits, self.offsetbits)))]))
	
	def get_fragment(self):
		load = Signal()
		ce = Signal()
		last = Signal()
		
		maximum = Signal(BV(self.nbits))
		if self.offsetbits:
			offset = Signal(BV(self.offsetbits))
		counter = Signal(BV(self.nbits))
		
		if self.step > 1:
			comb = [last.eq(counter + self.step >= maximum)]
		else:
			comb = [last.eq(counter + 1 == maximum)]
		sync = [
			If(load,
				counter.eq(0),
				maximum.eq(self.token("parameters").maximum),
				offset.eq(self.token("parameters").offset) if self.offsetbits else None
			).Elif(ce,
				If(last,
					counter.eq(0)
				).Else(
					counter.eq(counter + self.step)
				)
			)
		]
		if self.offsetbits:
			comb.append(self.token("source").value.eq(counter + offset))
		else:
			comb.append(self.token("source").value.eq(counter))
		counter_fragment = Fragment(comb, sync)
		
		fsm = FSM("IDLE", "ACTIVE")
		fsm.act(fsm.IDLE,
			load.eq(1),
			self.endpoints["parameters"].ack.eq(1),
			If(self.endpoints["parameters"].stb, fsm.next_state(fsm.ACTIVE))
		)
		fsm.act(fsm.ACTIVE,
			self.busy.eq(1),
			self.endpoints["source"].stb.eq(1),
			If(self.endpoints["source"].ack,
				ce.eq(1),
				If(last, fsm.next_state(fsm.IDLE))
			)
		)
		return counter_fragment + fsm.get_fragment()