# -- coding: utf-8 --
from analyzer import Analyzer
from analyzer import register_analyzer
from analyzer import LogLevel


class SPIAnalyzer(Analyzer):
    def run(self):
        super().run()
        self.log(LogLevel.INFO, f"{self.periph_name}分析启动")
        inst_num = self.get_instance_num(self.periph_name)
        inst_error = 0
        
        #前置检查开始，如果出错就需要中止分析，以防读寄存器卡死或出现非法值
        HPSYS_RCC = self.read_peripheral("hpsys_rcc")
        HPSYS_CFG = self.read_peripheral("hpsys_cfg")
        #self.print_reg(HPSYS_CFG)
        
        #以下是根据实例编号需要区分的内容
        #检查时钟与复位
        clk_pin = 28
        cs_pin = 29
        dio_pin = 24
        di_pin = 25
        if (inst_num==1):
            if (HPSYS_RCC.ENR1.SPI1!=1):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块时钟未开启",
                         "需将HPSYS_RCC的ESR1_SPI1置1以开启模块时钟")
                inst_error = 1
            if (HPSYS_RCC.RSTR1.SPI1!=0):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块被复位",
                         "需将HPSYS_RCC的RSTR1_SPI1置0以释放模块复位")
                inst_error = 1
            clk_pin = 28
            cs_pin  = 29
            dio_pin = 24
            di_pin  = 25
        elif (inst_num==2):
            if (HPSYS_RCC.ENR1.SPI2!=1):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块时钟未开启",
                         "需将HPSYS_RCC的ESR1_SPI2置1以开启模块时钟")
                inst_error = 1
            if (HPSYS_RCC.RSTR1.SPI2!=0):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块被复位",
                         "需将HPSYS_RCC的RSTR1_SPI2置0以释放模块复位")
                inst_error = 1
            clk_pin = 39
            cs_pin  = 40
            dio_pin = 37
            di_pin  = 38

        #前置检查结束，如果出错就中止分析
        if (inst_error==1):
            self.log(LogLevel.ERROR, f"{self.periph_name}上述初始配置发现错误，分析无法继续进行",\
                     "请将上述错误修正后重新启动分析")
            return
        #self.log(LogLevel.INFO, "初始配置分析完成，未发现错误，启动功能分析")

        #读取SPI寄存器
        SPI = self.read_peripheral(self.periph_name)

        #以下是各实例共同的内容
        #PINMUX检查
        HPSYS_PINMUX = self.read_peripheral("hpsys_pinmux")
        #CLK IO检查
        if (HPSYS_PINMUX.__dict__[f"PAD_PA{clk_pin:02d}"].FSEL != 2):
            self.log(LogLevel.ERROR, f"CLK PA{clk_pin:02d}功能错误，未选择SPI功能",\
                     f"应将HPSYS_PINMUX->PAD_PA{clk_pin:02d}.FSEL设为2")
            inst_error = 1
        if ((HPSYS_PINMUX.__dict__[f"PAD_PA{clk_pin:02d}"].PE == 1)):
            self.log(LogLevel.WARN, f"CLK PA{clk_pin:02d}内部上下拉开启，可能产生漏电",\
                     f"应将HPSYS_PINMUX->PAD_PA{clk_pin:02d}.PE设为0以关闭内部上下拉电阻")
        #CS IO检查
        if (HPSYS_PINMUX.__dict__[f"PAD_PA{cs_pin:02d}"].FSEL != 2):
            self.log(LogLevel.ERROR, f"CS PA{cs_pin:02d}功能错误，未选择SPI功能",\
                     f"应将HPSYS_PINMUX->PAD_PA{cs_pin:02d}.FSEL设为2")
            inst_error = 1
        if ((HPSYS_PINMUX.__dict__[f"PAD_PA{cs_pin:02d}"].PE == 1) and \
            (HPSYS_PINMUX.__dict__[f"PAD_PA{cs_pin:02d}"].PS == 0)):
            self.log(LogLevel.ERROR, f"CS PA{cs_pin:02d}内部下拉开启，会产生漏电",\
                     f"应将HPSYS_PINMUX->PAD_PA{cs_pin:02d}.PS设为1改成内部上拉")
            inst_error = 1
        #DIO IO检查
        if (HPSYS_PINMUX.__dict__[f"PAD_PA{dio_pin:02d}"].FSEL != 2):
            self.log(LogLevel.ERROR, f"DIO PA{dio_pin:02d}功能错误，未选择SPI功能",\
                     f"应将HPSYS_PINMUX->PAD_PA{dio_pin:02d}.FSEL设为2")
            inst_error = 1
        if ((HPSYS_PINMUX.__dict__[f"PAD_PA{dio_pin:02d}"].IE != 1) and
                (SPI.TRIWIRE_CTRL.SPI_TRI_WIRE_EN==1)):
            #TODO 此处应当根据三线或四线模式自动判断
            self.log(LogLevel.WARN, f"DIO PA{dio_pin:02d}输入未使能",\
                     f"SPI三线模式需开启DIO输入使能，将HPSYS_PINMUX->PAD_PA{dio_pin:02d}.IE设为1")
            inst_error = 1
        #DI IO检查
        if (HPSYS_PINMUX.__dict__[f"PAD_PA{di_pin:02d}"].FSEL != 2):
            self.log(LogLevel.WARN, f"DI PA{di_pin:02d}未选择SPI功能",\
                     f"SPI四线模式下，将HPSYS_PINMUX->PAD_PA{di_pin:02d}.FSEL设为2")
        if ((HPSYS_PINMUX.__dict__[f"PAD_PA{di_pin:02d}"].IE != 1) and
                (SPI.TRIWIRE_CTRL.SPI_TRI_WIRE_EN==1)):
            #TODO 此处应当根据三线或四线模式自动判断
            self.log(LogLevel.WARN, f"DI PA{di_pin:02d}输入未使能",\
                     f"SPI四线模式需开启DI输入使能，将HPSYS_PINMUX->PAD_PA{di_pin:02d}.IE设为1")
               

        
        #检查SPI速率
##        i2c_mode = I2C.CR.MODE;
##        if (i2c_mode == 0):
##            self.log(LogLevel.INFO, "I2C为标准模式(standard-mode)")
##            if (I2C.LCR.SLV > (2*I2C.WCR.CNT + 6)):
##                i2c_freq = 48000/(2*I2C.LCR.SLV + 7 + I2C.CR.DNF);
##            else:
##                i2c_freq = 48000/(I2C.LCR.SLV + 2*I2C.WCR.CNT + 6 + 7 + I2C.CR.DNF);
##        elif (i2c_mode == 1):
##            self.log(LogLevel.INFO, "I2C为快速或快速增强模式(fast-mode/fast-mode plus)")
##            if (I2C.LCR.FLV > (2*I2C.WCR.CNT + 6)):
##                i2c_freq = 48000/(2*I2C.LCR.FLV + 7 + I2C.CR.DNF);
##            else:
##                i2c_freq = 48000/(I2C.LCR.FLV + 2*I2C.WCR.CNT + 6 + 7 + I2C.CR.DNF);
##        else:
##            self.log(LogLevel.WARN, "I2C为高速模式，需使用专用格式访问",\
##                     "请确认是否需要3.4M高速模式(HS-mode)")
##            i2c_freq = 48000/(I2C.LCR.HLVH + I2C.LCR.HLVL + 7 + 2*I2C.CR.DNF);
##        self.log(LogLevel.INFO, f"接口频率约{i2c_freq:.0f}kHz")
##        if (i2c_freq >= 1000):
##            self.log(LogLevel.INFO, "外部上拉电阻推荐1K欧")
##        elif (i2c_freq >= 400):
##            self.log(LogLevel.INFO, "外部上拉电阻推荐4.7K欧")

        #检查SPI模块状态
        if(SPI.TOP_CTRL.SSE !=1):
            self.log(LogLevel.ERROR, f"SSE设置错误，未使能SPI", \
                     f"正常工作时应将SSE设为1")
            inst_error = 1
        #检查SPI协议设置
        if(SPI.TOP_CTRL.FRF==3):
            self.log(LogLevel.ERROR, f"SPI协议设置错误，配置为RSVD", \
                     f"正常工作时应将FRF设为0/1/2中的一个")
            inst_error = 1
        #检查SPI主从设置
        if((SPI.TOP_CTRL.SCLKDIR==0) and
                (SPI.TOP_CTRL.SFRMDIR==0)):
            self.log(LogLevel.INFO, f"请确认SPI当前设置为Master")
        if((SPI.TOP_CTRL.SCLKDIR==1) and
                (SPI.TOP_CTRL.SFRMDIR==1)):
            self.log(LogLevel.INFO, f"请确认SPI当前设置为Slave")
        if(SPI.TOP_CTRL.SCLKDIR != SPI.TOP_CTRL.SFRMDIR):
            self.log(LogLevel.INFO, f"请确认SPI的CS和CLK主从设置不一致")
        #检查SPI时钟设置
        if(SPI.CLK_CTRL.CLK_SEL==1):
            self.log(LogLevel.INFO, f"SPI接口频率是48MHz")
        else:
            spi_freq = 48/(SPI.CLK_CTRL.CLK_DIV)
            self.log(LogLevel.INFO, f"SPI接口频率{spi_freq:.0f}MHz")
        if(SPI.CLK_CTRL.CLK_SSP_EN!=1):
            self.log(LogLevel.ERROR, f"SPI时钟设置错误，没有使能", \
                     f"正常工作应将CLK_SSP_EN设为1")
            inst_error = 1
        #检查SPI DMA配置
        if (SPI.INTE.RIE == 0):
            self.log(LogLevel.INFO, f"RX中断没有打开")
        if (SPI.INTE.TIE == 0):
            self.log(LogLevel.INFO, f"TX中断没有打开")

        #检查SPI 中断配置
        if (SPI.FIFO_CTRL.RSRE == 0):
            self.log(LogLevel.INFO, f"RXFIFO 的DMA接口没有打开")
        if (SPI.FIFO_CTRL.TSRE == 0):
            self.log(LogLevel.INFO, f"TXFIFO 的DMA接口没有打开")

        #检查SPI三线设置
        if((SPI.TRIWIRE_CTRL.SPI_TRI_WIRE_EN==1) and
                (SPI.CLK_CTRL.SPI_DI_SEL!=1)):
            self.log(LogLevel.ERROR, f"SPI三线设置错误", \
                     f"三线时SPI_DI_SEL应设为1")
            inst_error = 1

        #检查SPI FIFO状态
        if(SPI.STATUS.ROR ==1):
            self.log(LogLevel.ERROR, f"RXFIFO 发生溢出！", \
                     f"请检查RXFIFO读数流程")
            inst_error = 1
        if(SPI.STATUS.TUR ==1):
            self.log(LogLevel.ERROR, f"TXFIFO 发生下溢！", \
                     f"请检查向TXFIFO送数流程")
            inst_error = 1

        #输出分析结果
        if (inst_error==1):
            self.log(LogLevel.ERROR, f"{self.periph_name}配置发现错误",\
                     "请检查配置")
        else:
            self.log(LogLevel.INFO, f"{self.periph_name}分析结束，未发现错误")
        
register_analyzer("SPI", SPIAnalyzer)

