# SPDX-License-Identifier: BSD-2-Clause

from textwrap                           import dedent

from torii.build                        import Attrs, Clock, Connector, DiffPairs, Pins, PinsN, Resource, Subsignal
from torii.build.run                    import BuildProducts
from torii.platform.resources.interface import UARTResource
from torii.platform.resources.memory    import SPIFlashResources
from torii.platform.resources.user      import LEDResources, SwitchResources
from torii.platform.vendor.lattice.ecp5 import ECP5Platform

__all__ = (
	'VersaECP5Platform',
)

class VersaECP5Platform(ECP5Platform):
	device      = 'LFE5UM-45F'
	package     = 'BG381'
	speed       = '8'
	default_clk = 'clk100'
	default_rst = 'rst'

	pretty_name = 'Versa'
	description = 'Lattice Versa ECP5-45F Evaluation Board'

	resources   = [
		Resource('rst', 0, PinsN('T1', dir = 'i'), Attrs(IO_TYPE = 'LVCMOS33')),
		Resource('clk100', 0, DiffPairs('P3', 'P4', dir = 'i'), Clock(100e6), Attrs(IO_TYPE = 'LVDS')),
		Resource('pclk', 0, DiffPairs('A4', 'A5', dir = 'i'), Attrs(IO_TYPE = 'LVDS')),
		*LEDResources(
			pins = 'E16 D17 D18 E18 F17 F18 E17 F16', invert = True,
			attrs = Attrs(IO_TYPE = 'LVCMOS25')
		),
		Resource(
			'alnum_led', 0,
			Subsignal('a', PinsN('M20', dir = 'o')),
			Subsignal('b', PinsN('L18', dir = 'o')),
			Subsignal('c', PinsN('M19', dir = 'o')),
			Subsignal('d', PinsN('L16', dir = 'o')),
			Subsignal('e', PinsN('L17', dir = 'o')),
			Subsignal('f', PinsN('M18', dir = 'o')),
			Subsignal('g', PinsN('N16', dir = 'o')),
			Subsignal('h', PinsN('M17', dir = 'o')),
			Subsignal('j', PinsN('N18', dir = 'o')),
			Subsignal('k', PinsN('P17', dir = 'o')),
			Subsignal('l', PinsN('N17', dir = 'o')),
			Subsignal('m', PinsN('P16', dir = 'o')),
			Subsignal('n', PinsN('R16', dir = 'o')),
			Subsignal('p', PinsN('R17', dir = 'o')),
			Subsignal('dp', PinsN('U1', dir = 'o')),
			Attrs(IO_TYPE = 'LVCMOS25')
		),
		*SwitchResources(
			pins = {0: 'H2',  1: 'K3',  2: 'G3',  3: 'F2' },
			attrs = Attrs(IO_TYPE = 'LVCMOS15')
		),
		*SwitchResources(
			pins = {4: 'J18', 5: 'K18', 6: 'K19', 7: 'K20'},
			attrs = Attrs(IO_TYPE = 'LVCMOS25')
		),
		UARTResource(
			0,
			rx = 'C11', tx = 'A11',
			attrs = Attrs(IO_TYPE = 'LVCMOS33', PULLMODE = 'UP')
		),
		*SPIFlashResources(
			0,
			cs_n = 'R2', clk = 'U3', cipo = 'W2', copi = 'V2', wp_n = 'Y2', hold_n = 'W1',
			attrs = Attrs(IO_TYPE = 'LVCMOS33')
		),
		Resource(
			'eth_clk125', 0, Pins('L19', dir = 'i'), Clock(125e6), Attrs(IO_TYPE = 'LVCMOS25')
		),
		Resource(
			'eth_clk125_pll', 0, Pins('U16', dir = 'i'), Clock(125e6), Attrs(IO_TYPE = 'LVCMOS25')
		), # NC by default
		# TODO(aki): Replace with `EthernetResource` when it has `rst` signal support
		Resource(
			'eth_rgmii', 0,
			Subsignal('rst', PinsN('U17', dir = 'o')),
			Subsignal('mdc', Pins('T18', dir = 'o')),
			Subsignal('mdio', Pins('U18', dir = 'io')),
			Subsignal('tx_clk', Pins('P19', dir = 'o')),
			Subsignal('tx_ctl', Pins('R20', dir = 'o')),
			Subsignal('tx_data', Pins('N19 N20 P18 P20', dir = 'o')),
			Subsignal('rx_clk', Pins('L20', dir = 'i')),
			Subsignal('rx_ctl', Pins('U19', dir = 'i')),
			Subsignal('rx_data', Pins('T20 U20 T19 R18', dir = 'i')),
			Attrs(IO_TYPE = 'LVCMOS25')
		),
		# TODO(aki): Replace with `EthernetResource` when it has SGMII support
		Resource(
			'eth_sgmii', 0,
			Subsignal('rst', PinsN('U17', dir = 'o'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('mdc', Pins('T18', dir = 'o'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('mdio', Pins('U18', dir = 'io'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('tx', DiffPairs('W13', 'W14', dir = 'o')),
			Subsignal('rx', DiffPairs('Y14', 'Y15', dir = 'i')),
		),
		Resource(
			'eth_clk125', 1, Pins('J20', dir = 'i'), Clock(125e6), Attrs(IO_TYPE = 'LVCMOS25')
		),
		Resource(
			'eth_clk125_pll', 1, Pins('C18', dir = 'i'), Clock(125e6), Attrs(IO_TYPE = 'LVCMOS25')
		), # NC by default
		# TODO(aki): Replace with `EthernetResource` when it has `rst` signal support
		Resource(
			'eth_rgmii', 1,
			Subsignal('rst', PinsN('F20', dir = 'o')),
			Subsignal('mdc', Pins('G19', dir = 'o')),
			Subsignal('mdio', Pins('H20', dir = 'io')),
			Subsignal('tx_clk', Pins('C20', dir = 'o')),
			Subsignal('tx_ctrl', Pins('E19', dir = 'o')),
			Subsignal('tx_data', Pins('J17 J16 D19 D20', dir = 'o')),
			Subsignal('rx_clk', Pins('J19', dir = 'i')),
			Subsignal('rx_ctrl', Pins('F19', dir = 'i')),
			Subsignal('rx_data', Pins('G18 G16 H18 H17', dir = 'i')),
			Attrs(IO_TYPE = 'LVCMOS25')
		),
		# TODO(aki): Replace with `EthernetResource` when it has SGMII support
		Resource(
			'eth_sgmii', 1,
			Subsignal('rst', PinsN('F20', dir = 'o'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('mdc', Pins('G19', dir = 'o'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('mdio', Pins('H20', dir = 'io'), Attrs(IO_TYPE = 'LVCMOS25')),
			Subsignal('tx', DiffPairs('W17', 'W18', dir = 'o')),
			Subsignal('rx', DiffPairs('Y16', 'Y17', dir = 'i')),
		),
		# TODO(aki): Replace with `DDR3Resource` when more granular attrs are possible
		Resource(
			'ddr3', 0,
			Subsignal('rst', PinsN('N4', dir = 'o')),
			Subsignal('clk', DiffPairs('M4', 'N5', dir = 'o'), Attrs(IO_TYPE = 'SSTL135D_I')),
			Subsignal('clk_en', Pins('N2', dir = 'o')),
			Subsignal('cs', PinsN('K1', dir = 'o')),
			Subsignal('we', PinsN('M1', dir = 'o')),
			Subsignal('ras', PinsN('P1', dir = 'o')),
			Subsignal('cas', PinsN('L1', dir = 'o')),
			Subsignal('a', Pins('P2 C4 E5 F5 B3 F4 B5 E4 C5 E3 D5 B4 C3', dir = 'o')),
			Subsignal('ba', Pins('P5 N3 M3', dir = 'o')),
			Subsignal(
				'dqs', DiffPairs('K2 H4', 'J1 G5', dir = 'io'),
				Attrs(IO_TYPE = 'SSTL135D_I', DIFFRESISTOR = '100', TERMINATION = 'OFF')
			),
			Subsignal(
				'dq', Pins('L5 F1 K4 G1 L4 H1 G2 J3 D1 C1 E2 C2 F3 A2 E1 B1', dir = 'io'), Attrs(TERMINATION = '75')
			),
			Subsignal('dm', Pins('J4 H5', dir = 'o')),
			Subsignal('odt', Pins('L2', dir = 'o')),
			Attrs(IO_TYPE = 'SSTL135_I', SLEWRATE = 'FAST')
		)
	]
	connectors = [
		Connector(
			'expcon', 1,
			'-   -   -   B19 B12 B9  E6  D6  E7  D7  B11 B6  E9  D9  B8  C8  D8  E8  C7  C6 '
			'-   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -   -  '
		),  # X3
		Connector(
			'expcon', 2,
			'A8  -   A12 A13 B13 C13 D13 E13 A14 C14 D14 E14 D11 C10 A9  B10 D12 E12 -   - '
			'B15 -   C15 -   D15 -   E15 A16 B16 -   C16 D16 B17 -   C17 A17 B18 A7  A18 - '
		), # X4
	]

	@property
	def file_templates(self) -> dict[str, str]:
		return {
			**super().file_templates,
			'{{name}}-openocd.cfg': dedent(r'''
			interface ftdi
			{# FTDI descriptors is identical between non-5G and 5G recent Versa boards #}
			ftdi_device_desc 'Lattice ECP5_5G VERSA Board'
			ftdi_vid_pid 0x0403 0x6010
			ftdi_channel 0
			ftdi_layout_init 0xfff8 0xfffb
			reset_config none
			adapter_khz 25000

			# ispCLOCK device (unusable with openocd and must be bypassed)
			#jtag newtap ispclock tap -irlen 8 -expected-id 0x00191043
			# ECP5 device
			{% if '5G' in platform.device -%}
			jtag newtap ecp5 tap -irlen 8 -expected-id 0x81112043 ; # LFE5UM5G-45F
			{% else -%}
			jtag newtap ecp5 tap -irlen 8 -expected-id 0x01112043 ; # LFE5UM-45F
			{% endif %}
			''')
		}

	def toolchain_program(self, products: BuildProducts, name: str) -> None:
		from os         import environ
		from subprocess import check_call

		openocd = environ.get('OPENOCD', 'openocd')
		with products.extract(f'{name}-openocd.cfg', f'{name}.svf') as files:
			(config_filename, vector_filename) = files
			check_call([
				openocd, '-f', config_filename,
				'-c', f'transport select jtag; init; svf -quiet {vector_filename}; exit'
			])


if __name__ == '__main__':
	from ...test.blinky import Blinky
	VersaECP5Platform().build(Blinky(), do_program = True)
