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

from textwrap                           import dedent

from torii.build                        import Attrs, Clock, Connector, Pins, PinsN, Resource, Subsignal
from torii.build.run                    import BuildProducts
from torii.hdl.ir                       import Fragment
from torii.platform.resources.display   import Display7SegResource, VGAResource
from torii.platform.resources.interface import I2CResource, PS2Resource, UARTResource
from torii.platform.resources.memory    import SDRAMResource
from torii.platform.resources.user      import ButtonResources, LEDResources
from torii.platform.vendor.altera       import AlteraPlatform

__all__ = (
	'RZEasyFPGAA2_2Platform',
)

class RZEasyFPGAA2_2Platform(AlteraPlatform):
	device      = 'EP4CE6' # Cyclone IV 6K LEs
	package     = 'E22'    # EQFP 144 pins
	speed       = 'C8'
	default_clk = 'clk50'  # 50MHz builtin clock
	default_rst = 'rst'

	pretty_name = 'RZ-EasyFPGA A2.2'
	description = 'RZ-EasyFPGA Altera Cyclone IV Development Board'

	resources   = [
		# Clock
		Resource(
			'clk50', 0, Pins('23', dir = 'i'), Clock(50e6), Attrs(io_standard = '3.3-V LVTTL')
		),
		# Reset switch, located on the lower left of the board.
		Resource('rst', 0, PinsN('25', dir = 'i'), Attrs(io_standard = '3.3-V LVTTL')),
		# LEDs, located on the bottom of the board.
		*LEDResources(
			pins = '87 86 85 84', invert = True,
			attrs = Attrs(io_standard = '3.3-V LVTTL')
		),
		# Buttons, located on the bottom of the board, right of the LEDs.
		*ButtonResources(
			pins = '88 89 90 91', invert = True,
			attrs = Attrs(io_standard = '3.3-V LVTTL')
		),
		# Connections to the SKHynix RAM chip on board.
		SDRAMResource(
			0,
			clk = '43', cs_n = '72', we_n = '69', ras_n = '71', cas_n = '70',
			ba = '73 74', a = '76 77 80 83 68 67 66 65 64 60 75 59',
			dq = '28 30 31 32 33 34 38 39 54 53 52 51 50 49 46 44',
			dqm = '42 55', attrs = Attrs(io_standard = '3.3-V LVCMOS')
		),
		# VGA connector, located on the right of the board.
		VGAResource(
			0,
			r = '106', g = '105', b = '104',
			hs = '101', vs = '103'
		),
		# 4 digit 7 segment display, located on top of the board.
		Display7SegResource(
			0,
			a = '128', b = '121', c = '125', d = '129', e = '132', f = '126', g = '124', dp = '127',
			invert = True
		),
		Resource(
			'display_7seg_ctrl', 0,
			Subsignal('en', Pins('133 135 136 137', dir = 'o', invert = True)),
		),
		# PS2 port, located on upper right of the board.
		PS2Resource(0, clk = '119', dat = '120'),
		# LM75 temperature sensor
		I2CResource(0, scl = '112', sda = '113'),
		# AT24C08 EEPROM
		I2CResource(1, scl = '99', sda = '98'),
		# Buzzer
		Resource('buzzer', 0, PinsN('110', dir = 'o')),
		# Serial port, located above the VGA port.
		UARTResource(0, tx = '114', rx = '115'),
		# LCD connector, located above the 7 segment display.
		Resource(
			'lcd_hd44780', 0,
			Subsignal('rs', Pins('141', dir = 'o')),
			Subsignal('rw', Pins('138', dir = 'o')),
			Subsignal('e', Pins('143', dir = 'o')),
			Subsignal('d', Pins('142 1 144 3 2 10 7 11', dir = 'io')),
		),
		# IR receiver, located right of the buttons.
		Resource(
			'cir', 0,
			Subsignal('rx', Pins('100', dir = 'i'))
		),
	]

	connectors  = [
		# Located above the chip.
		Connector(
			'gpio', 0,
			'-   -   11  7   2   144 142 138 136 133 129 127 125 121 119 114 112 110 -  '
			'-   -   24  10  3   1   143 141 137 135 132 128 126 124 120 115 113 111 -  '
		),
		# Located right of the chip.
		Connector(
			'gpio', 1,
			'-   -   '
			'106 105 '
			'104 103 '
			'101 100 '
			'99  98  '
			'91  90  '
			'89  88  '
			'87  86  '
			'85  84  '
			'-   -   '
		),
		# Located below the chip.
		Connector(
			'gpio', 2,
			'30 32 34 39 43 46 50 52 54 58 60 65 67 71 73 75 77 83 -  -  - '
			'28 31 33 38 42 44 51 53 55 59 64 66 68 70 72 74 76 80 -  -  - '
		),
	]

	def toolchain_prepare(self, fragment: Fragment, name: str, **kwargs):
		overrides = {
			'add_settings': dedent('''
				set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION 'USE AS REGULAR IO'
			''')
		}
		return super().toolchain_prepare(fragment, name, **overrides, **kwargs)

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

		quartus_pgm = environ.get('QUARTUS_PGM', 'quartus_pgm')
		with products.extract(f'{name}.sof') as bitstream_filename:
			check_call([
				quartus_pgm, '--haltcc', '--mode', 'JTAG',
				'--operation', 'P;' + bitstream_filename
			])


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