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

from typing                             import List

from torii.build                        import (
	Connector, Resource, Pins, Clock, Attrs, PinsN,
	Subsignal, DiffPairs
)
from torii.build.run                    import BuildProducts, BuildPlan
from torii.hdl.ir                       import Fragment
from torii.platform.vendor.lattice.ecp5 import ECP5Platform
from torii.platform.resources           import (
	RGBLEDResource, SPIFlashResources, SDCardResources, DirectUSBResource
)

__all__ = (
	'OrangeCrabR0_1Platform',
)


class OrangeCrabR0_1Platform(ECP5Platform):
	device      = 'LFE5U-25F'
	package     = 'MG285'
	speed       = '8'
	default_clk = 'clk'

	resources   = [
		Resource('clk', 0, Pins('A9', dir = 'i'), Clock(48e6), Attrs(IO_TYPE = 'LVCMOS33')),

		# Used to reload FPGA configuration.
		# Can enter USB bootloader by assigning button 0 to program.
		Resource('program', 0, PinsN('R16', dir = 'o'), Attrs(IO_TYPE = 'LVCMOS33')),

		RGBLEDResource(0,
			r = 'V17', g = 'T17', b = 'J3', invert = True,
			attrs = Attrs(IO_TYPE = 'LVCMOS33')
		),

		*SPIFlashResources(0,
			cs_n = 'U17', clk = 'U16', cipo = 'T18', copi = 'U18', wp_n = 'R18', hold_n = 'N18',
			attrs = Attrs(IO_TYPE = 'LVCMOS33'),
		),

		Resource('ddr3', 0,
			Subsignal('rst',     PinsN('B1', dir = 'o')),
			Subsignal('clk',     DiffPairs('J18', 'K18', dir = 'o'), Attrs(IO_TYPE = 'SSTL135D_I')),
			Subsignal('clk_en',  Pins('D6', dir = 'o')),
			Subsignal('cs',      PinsN('A12', dir = 'o')),
			Subsignal('we',      PinsN('B12', dir = 'o')),
			Subsignal('ras',     PinsN('C12', dir = 'o')),
			Subsignal('cas',     PinsN('D13', dir = 'o')),
			Subsignal('a',       Pins('A4 D2 C3 C7 D3 D4 D1 B2 C1 A2 A7 C2 C4', dir = 'o')),
			Subsignal('ba',      Pins('B6 B7 A6', dir = 'o')),
			Subsignal('dqs',     DiffPairs('G18 H17', 'B15 A16', dir = 'io'),
				Attrs(IO_TYPE = 'SSTL135D_I', TERMINATION = 'OFF', DIFFRESISTOR = '100')
			),
			Subsignal('dq',      Pins('F17 F16 G15 F15 J16 C18 H16 F18 C17 D15 B17 C16 A15 B13 A17 A13', dir = 'io'),
				Attrs(TERMINATION = '75')
			),
			Subsignal('dm',      Pins('G16 D16', dir = 'o')),
			Subsignal('odt',     Pins('C13', dir = 'o')),
			Attrs(IO_TYPE = 'SSTL135_I', SLEWRATE = 'FAST')
		),

		Resource('ddr3_pseudo_power', 0,
			# pseudo power pins, leave these at their default value
			Subsignal('vcc_virtual', PinsN('A3 B18 C6 C15 D17 D18 K15 K16 K17', dir = 'o')),
			Subsignal('gnd_virtual', Pins('L15 L16 L18', dir = 'o')),
			Attrs(IO_TYPE = 'SSTL135_II', SLEWRATE = 'FAST')
		),

		*SDCardResources(0,
			dat0 = 'J1', dat1 = 'K3', dat2 = 'L3', dat3 = 'M1', clk = 'K1', cmd = 'K2', cd = 'L1',
			attrs = Attrs(IO_TYPE = 'LVCMOS33', SLEWRATE = 'FAST')
		),

		DirectUSBResource(0, d_p = 'N1', d_n = 'M2', pullup = 'N2', attrs = Attrs(IO_TYPE = 'LVCMOS33'))
	]
	connectors = [
		Connector('io', 0, {
			'0':    'N17',
			'1':    'M18',
			'5':    'B10',
			'6':    'B9',
			'9':    'C8',
			'10':   'B8',
			'11':   'A8',
			'12':   'H2',
			'13':   'J2',
			'cipo': 'N15',
			'copi': 'N16',
			'sck':  'R17',
			'scl':  'C9',
			'sda':  'C10'
		}),
		Connector('mcu', 0, {
			'0':    'A10',
			'1':    'C11',
			'2':    'A11',
			'3':    'B11',
		}),
	]

	@property
	def required_tools(self) -> List[str]:
		return super().required_tools + [
			'dfu-suffix'
		]

	@property
	def command_templates(self) -> List[str]:
		return super().command_templates + [
			r'''
			{{invoke_tool('dfu-suffix')}}
				-v 1209 -p 5af0 -a {{name}}.bit
			'''
		]

	def toolchain_prepare(self, fragment: Fragment, name: str, **kwargs) -> BuildPlan:
		overrides = dict(ecppack_opts = '--compress --freq 38.8')
		overrides.update(kwargs)
		return super().toolchain_prepare(fragment, name, **overrides)

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

		dfu_util = environ.get('DFU_UTIL', 'dfu-util')
		with products.extract(f'{name}.bit') as bitstream_filename:
			check_call([dfu_util, '-D', bitstream_filename])


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