from migen.fhdl.std import *
from migen.bus import wishbone
from migen.genlib.fsm import FSM, NextState
from migen.genlib.cdc import MultiReg

class FSMCBridge(Module):
	def __init__(self, fsmc_pins):
		self.master = master = wishbone.Interface(16)
		
		fsmc_a = Signal(11)
		self.d_in = fsmc_d_in = Signal(16)
		self.d_out = fsmc_d_out = Signal(16)
		fsmc_bl = Signal(2)
		fsmc_e = Signal()
		fsmc_oe = Signal()
		fsmc_we = Signal()
		
		d_ts = TSTriple(16)
		self.specials += d_ts.get_tristate(fsmc_pins.d)
		self.comb += d_ts.oe.eq(~fsmc_pins.ne & ~fsmc_pins.noe)
		self.comb += d_ts.o.eq(fsmc_d_out)
		
		self.specials += MultiReg(fsmc_pins.a, fsmc_a)
		self.specials += MultiReg(d_ts.i, fsmc_d_in)
		self.specials += MultiReg(~fsmc_pins.nbl, fsmc_bl)
		self.specials += MultiReg(~fsmc_pins.ne, fsmc_e)
		self.specials += MultiReg(~fsmc_pins.noe, fsmc_oe)
		self.specials += MultiReg(~fsmc_pins.nwe, fsmc_we)
		
		start_read_cycle = Signal()
		start_write_cycle = Signal()
		end_cycle = Signal()
		sample_data = Signal()
		
		fsm = FSM()
		
		fsm.act('IDLE',
			If(fsmc_e,
				# Read
				If(fsmc_oe,
					start_read_cycle.eq(1),
					NextState('READ_WAIT_ACK'),
				),
				# Write
				If(fsmc_we,
					start_write_cycle.eq(1),
					NextState('WRITE_WAIT_ACK'),
				),
			),
		)
		
		fsm.act('WRITE_WAIT_ACK',
			If(master.ack,
				end_cycle.eq(1),
				NextState('WRITE_WAIT'),
			),
		)
		
		fsm.act('WRITE_WAIT',
			If(~fsmc_we,
				NextState('IDLE'),
			),
		)
		
		fsm.act('READ_WAIT_ACK',
			If(master.ack,
				sample_data.eq(1),
				end_cycle.eq(1),
				NextState('READ_WAIT'),
			),
		)
		
		fsm.act('READ_WAIT',
			If(~fsmc_oe,
				NextState('IDLE'),
			),
		)
		
		self.submodules.fsm = fsm
		
		self.sync += If(start_read_cycle,
			master.cyc.eq(1),
			master.stb.eq(1),
			master.adr.eq(fsmc_a),
			master.sel.eq(fsmc_bl),
			master.we.eq(0),
		)
		
		self.sync += If(start_write_cycle,
			master.cyc.eq(1),
			master.stb.eq(1),
			master.adr.eq(fsmc_a),
			master.sel.eq(fsmc_bl),
			master.we.eq(1),
			master.dat_w.eq(fsmc_d_in),
		)
		
		self.sync += If(end_cycle,
			fsmc_d_out.eq(master.dat_r),
		)
		
		self.sync += If(end_cycle,
			master.cyc.eq(0),
			master.stb.eq(0),
		)