#!/usr/bin/env python3
# 
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import ctypes
from qiling.hw.peripheral import QlPeripheral
from qiling.hw.const.stm32fxxx_rcc import RCC_CR, RCC_CFGR, RCC_CSR


class STM32F4xxRcc(QlPeripheral):
	class Type(ctypes.Structure):
		""" the structure available in :
			stm32f401xc.h
			stm32f401xe.h
			stm32f411xe.h 
		"""

		_fields_ = [
			('CR'        , ctypes.c_uint32),      # RCC clock control register,                                  Address offset: 0x00
			('PLLCFGR'   , ctypes.c_uint32),      # RCC PLL configuration register,                              Address offset: 0x04
			('CFGR'      , ctypes.c_uint32),      # RCC clock configuration register,                            Address offset: 0x08
			('CIR'       , ctypes.c_uint32),      # RCC clock interrupt register,                                Address offset: 0x0C
			('AHB1RSTR'  , ctypes.c_uint32),      # RCC AHB1 peripheral reset register,                          Address offset: 0x10
			('AHB2RSTR'  , ctypes.c_uint32),      # RCC AHB2 peripheral reset register,                          Address offset: 0x14
			('AHB3RSTR'  , ctypes.c_uint32),      # RCC AHB3 peripheral reset register,                          Address offset: 0x18
			('RESERVED0' , ctypes.c_uint32),      # Reserved, 0x1C
			('APB1RSTR'  , ctypes.c_uint32),      # RCC APB1 peripheral reset register,                          Address offset: 0x20
			('APB2RSTR'  , ctypes.c_uint32),      # RCC APB2 peripheral reset register,                          Address offset: 0x24
			('RESERVED1' , ctypes.c_uint32 * 2),  # Reserved, 0x28-0x2C
			('AHB1ENR'   , ctypes.c_uint32),      # RCC AHB1 peripheral clock register,                          Address offset: 0x30
			('AHB2ENR'   , ctypes.c_uint32),      # RCC AHB2 peripheral clock register,                          Address offset: 0x34
			('AHB3ENR'   , ctypes.c_uint32),      # RCC AHB3 peripheral clock register,                          Address offset: 0x38
			('RESERVED2' , ctypes.c_uint32),      # Reserved, 0x3C
			('APB1ENR'   , ctypes.c_uint32),      # RCC APB1 peripheral clock enable register,                   Address offset: 0x40
			('APB2ENR'   , ctypes.c_uint32),      # RCC APB2 peripheral clock enable register,                   Address offset: 0x44
			('RESERVED3' , ctypes.c_uint32 * 2),  # Reserved, 0x48-0x4C
			('AHB1LPENR' , ctypes.c_uint32),      # RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50
			('AHB2LPENR' , ctypes.c_uint32),      # RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54
			('AHB3LPENR' , ctypes.c_uint32),      # RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58
			('RESERVED4' , ctypes.c_uint32),      # Reserved, 0x5C
			('APB1LPENR' , ctypes.c_uint32),      # RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60
			('APB2LPENR' , ctypes.c_uint32),      # RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64
			('RESERVED5' , ctypes.c_uint32 * 2),  # Reserved, 0x68-0x6C
			('BDCR'      , ctypes.c_uint32),      # RCC Backup domain control register,                          Address offset: 0x70
			('CSR'       , ctypes.c_uint32),      # RCC clock control & status register,                         Address offset: 0x74
			('RESERVED6' , ctypes.c_uint32 * 2),  # Reserved, 0x78-0x7C
			('SSCGR'     , ctypes.c_uint32),      # RCC spread spectrum clock generation register,               Address offset: 0x80
			('PLLI2SCFGR', ctypes.c_uint32),      # RCC PLLI2S configuration register,                           Address offset: 0x84
			('RESERVED7' , ctypes.c_uint32),      # Reserved, 0x88
			('DCKCFGR'   , ctypes.c_uint32),      # RCC Dedicated Clocks configuration register,                 Address offset: 0x8C
		]

	def __init__(self, ql, label, intn=None):
		super().__init__(ql, label)

		self.instance = self.struct(
			CR         = 0x00000083,
			PLLCFGR    = 0x24003010,
			AHB1LPENR  = 0x0061900F,
			AHB2LPENR  = 0x00000080,
			APB1LPENR  = 0x10E2C80F,
			APB2LPENR  = 0x00077930,
			CSR        = 0x0E000000,
			PLLI2SCFGR = 0x24003000,
		)

		self.rdyon = {
			'CR': [
				(RCC_CR.HSIRDY   , RCC_CR.HSION   ),
				(RCC_CR.HSERDY   , RCC_CR.HSEON   ),
				(RCC_CR.PLLRDY   , RCC_CR.PLLON   ),
				(RCC_CR.PLLI2SRDY, RCC_CR.PLLI2SON),
			],
			'CFGR': [
				(RCC_CFGR.SWS_0, RCC_CFGR.SW_0),
				(RCC_CFGR.SWS_1, RCC_CFGR.SW_1),
			],
			'CSR': [
				(RCC_CSR.LSIRDY, RCC_CSR.LSION)
			]
		}

		self.intn = intn

	@QlPeripheral.monitor()
	def read(self, offset: int, size: int) -> int:		
		buf = ctypes.create_string_buffer(size)
		ctypes.memmove(buf, ctypes.addressof(self.instance) + offset, size)
		return int.from_bytes(buf.raw, byteorder='little')

	@QlPeripheral.monitor()
	def write(self, offset: int, size: int, value: int):
		if offset == self.struct.CR.offset:
			value = (self.instance.CR & RCC_CR.RO_MASK) | (value & RCC_CR.RW_MASK)
		elif offset == self.struct.CFGR.offset:
			value = (self.instance.CFGR & RCC_CFGR.RO_MASK) | (value & RCC_CFGR.RW_MASK)

		data = (value).to_bytes(size, 'little')
		ctypes.memmove(ctypes.addressof(self.instance) + offset, data, size)

	def step(self):
		for reg, rdyon in self.rdyon.items():
			value = getattr(self.instance, reg)
			for rdy, on in rdyon:
				if value & on:
					value |= rdy
				else:
					value &= ~rdy
			setattr(self.instance, reg, value)
