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

from torii.build                  import Attrs, Clock, Connector, DiffPairs, Pins, PinsN, Resource, Subsignal
from torii.build.run              import BuildPlan, BuildProducts
from torii.hdl.ir                 import Fragment
from torii.platform.resources     import (
	ButtonResources, LEDResources, RGBLEDResource, SPIFlashResources, SPIResource, SwitchResources, UARTResource
)
from torii.platform.vendor.xilinx import XilinxPlatform

__all__ = (
	'ArtyA7_35Platform',
	'ArtyA7_100Platform',
)

class _ArtyA7Platform(XilinxPlatform):
	package     = 'csg324'
	speed       = '1L'
	default_clk = 'clk100'
	default_rst = 'rst'

	resources   = [
		Resource(
			'clk100', 0, Pins('E3', dir = 'i'), Clock(100e6), Attrs(IOSTANDARD = 'LVCMOS33')
		),
		Resource('rst', 0, PinsN('C2', dir = 'i'), Attrs(IOSTANDARD = 'LVCMOS33')),

		*LEDResources(pins = 'H5 J5 T9 T10', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),

		RGBLEDResource(0, r = 'G6', g = 'F6', b = 'E1', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),
		RGBLEDResource(1, r = 'G3', g = 'J4', b = 'G4', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),
		RGBLEDResource(2, r = 'J3', g = 'J2', b = 'H4', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),
		RGBLEDResource(3, r = 'K1', g = 'H6', b = 'K2', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),

		*ButtonResources(pins = 'D9  C9  B9  B8 ', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),
		*SwitchResources(pins = 'A8  C11 C10 A10', attrs = Attrs(IOSTANDARD = 'LVCMOS33')),

		UARTResource(0,
			rx = 'A9', tx = 'D10',
			attrs = Attrs(IOSTANDARD = 'LVCMOS33')
		),

		SPIResource(0,
			cs_n = 'C1', clk = 'F1', copi = 'H1', cipo = 'G1',
			attrs = Attrs(IOSTANDARD = 'LVCMOS33')
		),

		Resource('i2c', 0,
			Subsignal('scl',        Pins('L18', dir = 'io')),
			Subsignal('sda',        Pins('M18', dir = 'io')),
			Subsignal('scl_pullup', Pins('A14', dir = 'o')),
			Subsignal('sda_pullup', Pins('A13', dir = 'o')),
			Attrs(IOSTANDARD = 'LVCMOS33')
		),

		*SPIFlashResources(0,
			cs_n = 'L13', clk = 'L16', copi = 'K17', cipo = 'K18', wp_n = 'L14', hold_n = 'M14',
			attrs = Attrs(IOSTANDARD = 'LVCMOS33')
		),

		Resource('ddr3', 0,
			Subsignal('rst',    PinsN('K6', dir = 'o')),
			Subsignal('clk',    DiffPairs('U9', 'V9', dir = 'o'), Attrs(IOSTANDARD = 'DIFF_SSTL135')),
			Subsignal('clk_en', Pins('N5', dir = 'o')),
			Subsignal('cs',     PinsN('U8', dir = 'o')),
			Subsignal('we',     PinsN('P5', dir = 'o')),
			Subsignal('ras',    PinsN('P3', dir = 'o')),
			Subsignal('cas',    PinsN('M4', dir = 'o')),
			Subsignal('a',      Pins('R2 M6 N4 T1 N6 R7 V6 U7 R8 V7 R6 U6 T6 T8', dir = 'o')),
			Subsignal('ba',     Pins('R1 P4 P2', dir = 'o')),
			Subsignal('dqs',    DiffPairs('N2 U2', 'N1 V2', dir = 'io'),
								Attrs(IOSTANDARD = 'DIFF_SSTL135')
			),
			Subsignal('dq',     Pins('K5 L3 K3 L6 M3 M1 L4 M2 V4 T5 U4 V5 V1 T3 U3 R3', dir = 'io'),
								Attrs(IN_TERM = 'UNTUNED_SPLIT_40')
			),
			Subsignal('dm',     Pins('L1 U1', dir = 'o')),
			Subsignal('odt',    Pins('R5', dir = 'o')),
			Attrs(IOSTANDARD = 'SSTL135', SLEW = 'FAST'),
		),

		Resource(
			'eth_clk25', 0, Pins('G18', dir = 'o'), Clock(25e6),
			Attrs(IOSTANDARD = 'LVCMOS33')
		),
		Resource(
			'eth_clk50', 0, Pins('G18', dir = 'o'), Clock(50e6),
			Attrs(IOSTANDARD = 'LVCMOS33')
		),
		Resource('eth_mii', 0,
			Subsignal('rst',     PinsN('C16', dir = 'o')),
			Subsignal('mdio',    Pins('K13', dir = 'io')),
			Subsignal('mdc',     Pins('F16', dir = 'o')),
			Subsignal('tx_clk',  Pins('H16', dir = 'i')),
			Subsignal('tx_en',   Pins('H15', dir = 'o')),
			Subsignal('tx_data', Pins('H14 J14 J13 H17', dir = 'o')),
			Subsignal('rx_clk',  Pins('F15', dir = 'i')),
			Subsignal('rx_dv',   Pins('G16', dir = 'i'), Attrs(PULLDOWN = 'TRUE')), # strap to select MII
			Subsignal('rx_er',   Pins('C17', dir = 'i')),
			Subsignal('rx_data', Pins('D18 E17 E18 G17', dir = 'i')),
			Subsignal('col',     Pins('D17', dir = 'i')),
			Subsignal('crs',     Pins('G14', dir = 'i')),
			Attrs(IOSTANDARD = 'LVCMOS33')
		),
		Resource('eth_rmii', 0,
			Subsignal('rst',       PinsN('C16', dir = 'o')),
			Subsignal('mdio',      Pins('K13', dir = 'io')),
			Subsignal('mdc',       Pins('F16', dir = 'o')),
			Subsignal('tx_en',     Pins('H15', dir = 'o')),
			Subsignal('tx_data',   Pins('H14 J14', dir = 'o')),
			Subsignal('rx_crs_dv', Pins('G14', dir = 'i')),
			Subsignal('rx_dv',     Pins('G16', dir = 'i'), Attrs(PULLUP = 'TRUE')), # strap to select RMII
			Subsignal('rx_er',     Pins('C17', dir = 'i')),
			Subsignal('rx_data',   Pins('D18 E17', dir = 'i')),
			Attrs(IOSTANDARD = 'LVCMOS33')
		)
	]
	connectors  = [
		Connector('pmod', 0, 'G13 B11 A11 D12 - - D13 B18 A18 K16 - -'), # JA
		Connector('pmod', 1, 'E15 E16 D15 C15 - - J17 J18 K15 J15 - -'), # JB
		Connector('pmod', 2, 'U12 V12 V10 V11 - - U14 V14 T13 U13 - -'), # JC
		Connector('pmod', 3, ' D4  D3  F4  F3 - -  E2  D2  H2  G2 - -'), # JD

		Connector('ck_io', 0, {
			# Outer Digital Header
			'io0':  'V15',
			'io1':  'U16',
			'io2':  'P14',
			'io3':  'T11',
			'io4':  'R12',
			'io5':  'T14',
			'io6':  'T15',
			'io7':  'T16',
			'io8':  'N15',
			'io9':  'M16',
			'io10': 'V17',
			'io11': 'U18',
			'io12': 'R17',
			'io13': 'P17',

			# Inner Digital Header
			'io26': 'U11',
			'io27': 'V16',
			'io28': 'M13',
			'io29': 'R10',
			'io30': 'R11',
			'io31': 'R13',
			'io32': 'R15',
			'io33': 'P15',
			'io34': 'R16',
			'io35': 'N16',
			'io36': 'N14',
			'io37': 'U17',
			'io38': 'T18',
			'io39': 'R18',
			'io40': 'P18',
			'io41': 'N17',

			# Outer Analog Header as Digital IO
			'a0': 'F5',
			'a1': 'D8',
			'a2': 'C7',
			'a3': 'E7',
			'a4': 'D7',
			'a5': 'D5',

			# Inner Analog Header as Digital IO
			'io20': 'B7',
			'io21': 'B6',
			'io22': 'E6',
			'io23': 'E5',
			'io24': 'A4',
			'io25': 'A3'
		}),
		Connector('xadc', 0, {
			# Outer Analog Header
			'vaux4_n':   'C5',
			'vaux4_p':   'C6',
			'vaux5_n':   'A5',
			'vaux5_p':   'A6',
			'vaux6_n':   'B4',
			'vaux6_p':   'C4',
			'vaux7_n':   'A1',
			'vaux7_p':   'B1',
			'vaux15_n':  'B2',
			'vaux15_p':  'B3',
			'vaux0_n':  'C14',
			'vaux0_p':  'D14',

			# Inner Analog Header
			'vaux12_n': 'B7',
			'vaux12_p': 'B6',
			'vaux13_n': 'E6',
			'vaux13_p': 'E5',
			'vaux14_n': 'A4',
			'vaux14_p': 'A3',

			# Power Measurements
			'vsnsuv_n':   'B17',
			'vsnsuv_p':   'B16',
			'vsns5v0_n':  'B12',
			'vsns5v0_p':  'C12',
			'isns5v0_n':  'F14',
			'isns5v0_p':  'F13',
			'isns0v95_n': 'A16',
			'isns0v95_p': 'A15',
		})
	]

	def toolchain_prepare(self, fragment: Fragment, name: str, **kwargs) -> BuildPlan:
		overrides = {
			'script_before_bitstream':
				'set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]',
			'script_after_bitstream':
				'write_cfgmem -force -format bin -interface spix4 -size 16 '
				'-loadbit \'up 0x0 {name}.bit\' -file {name}.bin'.format(name = name),
			'add_constraints':
				'''
				set_property INTERNAL_VREF 0.675 [get_iobanks 34]
				set_property CFGBVS VCCO [current_design]
				set_property CONFIG_VOLTAGE 3.3 [current_design]
				'''
		}
		return super().toolchain_prepare(fragment, name, **overrides, **kwargs)

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

		xc3sprog = environ.get('XC3SPROG', 'xc3sprog')
		with products.extract(f'{name}.bit') as bitstream_filename:
			run([xc3sprog, '-c', 'nexys4', bitstream_filename], check = True)


class ArtyA7_35Platform(_ArtyA7Platform):
	device = 'xc7a35ti'

	pretty_name = 'Arty A7-35T'
	description = 'Digilent Arty A7-35T Xilinx Artix7-35 Development Board'

class ArtyA7_100Platform(_ArtyA7Platform):
	device = 'xc7a100ti'

	pretty_name = 'Arty A7-100T'
	description = 'Digilent Arty-A7-100T Xilinx Artix7-100 Development Board'

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