from misoclib.liteeth.common import *
from misoclib.liteeth.generic import *

class LiteEthPHYMIITX(Module):
	def __init__(self, pads):
		self.sink = sink = Sink(eth_phy_description(8))
		###
		tx_en_r = Signal()
		tx_data_r = Signal(4)
		self.sync += [
			pads.tx_er.eq(0),
			pads.tx_en.eq(tx_en_r),
			pads.tx_data.eq(tx_data_r),
		]

		fsm = FSM(reset_state="IDLE")
		self.submodules += fsm
		fsm.act("IDLE",
			sink.ack.eq(1),
			If(sink.stb & sink.sop,
				sink.ack.eq(0),
				NextState("SEND_LO")
			)
		)
		fsm.act("SEND_LO",
			tx_data_r.eq(sink.data[0:4]),
			tx_en_r.eq(1),
			NextState("SEND_HI")
		)
		fsm.act("SEND_HI",
			tx_data_r.eq(sink.data[4:8]),
			tx_en_r.eq(1),
			sink.ack.eq(1),
			If(sink.stb & sink.eop,
				NextState("IDLE")
			).Else(
				NextState("SEND_LO")
			)
		)

class LiteEthPHYMIIRX(Module):
	def __init__(self, pads):
		self.source = source = Source(eth_phy_description(8))
		###
		sop = source.sop
		set_sop = Signal()
		clr_sop = Signal()
		self.sync += \
			If(clr_sop,
				sop.eq(0)
			).Elif(set_sop,
				sop.eq(1)
			)

		lo = Signal(4)
		hi = Signal(4)
		load_nibble = Signal(2)
		self.sync  += \
			If(load_nibble[0],
				lo.eq(pads.rx_data)
			).Elif(load_nibble[1],
				hi.eq(pads.rx_data)
			)
		self.comb += [
			source.data.eq(Cat(lo, hi))
		]

		fsm = FSM(reset_state="IDLE")
		self.submodules += fsm
		fsm.act("IDLE",
			set_sop.eq(1),
			If(pads.dv,
				load_nibble.eq(0b01),
				NextState("LOAD_HI")
			)
		)
		fsm.act("LOAD_LO",
			source.stb.eq(1),
			If(pads.dv,
				clr_sop.eq(1),
				load_nibble.eq(0b01),
				NextState("LOAD_HI")
			).Else(
				source.eop.eq(1),
				NextState("IDLE")
			)
		)
		fsm.act("LOAD_HI",
			load_nibble.eq(0b10),
			NextState("LOAD_LO")
		)

class LiteEthPHYMIICRG(Module, AutoCSR):
	def __init__(self, clock_pads, pads, with_hw_init_reset):
		self._reset = CSRStorage()
		###
		self.sync.base50 += clock_pads.phy.eq(~clock_pads.phy)

		self.clock_domains.cd_eth_rx = ClockDomain()
		self.clock_domains.cd_eth_tx = ClockDomain()
		self.comb += self.cd_eth_rx.clk.eq(clock_pads.rx)
		self.comb += self.cd_eth_tx.clk.eq(clock_pads.tx)

		if with_hw_init_reset:
			reset = Signal()
			counter_done = Signal()
			self.submodules.counter = counter = Counter(max=512)
			self.comb += [
				counter_done.eq(counter.value == 256),
				counter.ce.eq(~counter_done),
				reset.eq(~counter_done | self._reset.storage)
			]
		else:
			reset = self._reset.storage
		self.comb += pads.rst_n.eq(~reset)
		self.specials += [
			AsyncResetSynchronizer(self.cd_eth_tx, reset),
			AsyncResetSynchronizer(self.cd_eth_rx, reset),
		]

class LiteEthPHYMII(Module, AutoCSR):
	def __init__(self, clock_pads, pads, with_hw_init_reset=True):
		self.dw = 8
		self.submodules.crg = LiteEthPHYMIICRG(clock_pads, pads, with_hw_init_reset)
		self.submodules.tx = RenameClockDomains(LiteEthPHYMIITX(pads), "eth_tx")
		self.submodules.rx = RenameClockDomains(LiteEthPHYMIIRX(pads), "eth_rx")
		self.sink, self.source = self.tx.sink, self.rx.source