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

dmac1_sel = ("MPI1","MPI2","NONE","I2C4","USART1_TX","USART1_RX","USART2_TX","USART2_RX",\
             "GPTIM1_UPDATE","GPTIM1_TRIGGER","GPTIM1_CC1","GPTIM1_CC2","GPTIM1_CC3","GPTIM1_CC4","BTIM1","BTIM2",\
             "ATIM1_UPDATE","ATIM1_TRIGGER","ATIM1_CC1","ATIM1_CC2","ATIM1_CC3","ATIM1_CC4","I2C1","I2C2",\
             "I2C3","ATIM1_COM","USART3_TX","USART3_RX","SPI1_TX","SPI1_RX","SPI2_TX","SPI2_RX",\
             "I2S1_TX","I2S1_RX","NONE","NONE","PDM1_L","PDM1_R","GPADC","AUDADC_CH0",\
             "AUDADC_CH1","AUDDAC_CH0","AUDDAC_CH1","GPTIM2_UPDATE","GPTIM2_TRIGGER","GPTIM2_CC1","AUDPRC_TX_OUT_CH1","AUDPRC_TX_OUT_CH0",\
             "AUDPRC_TX_CH3","AUDPRC_TX_CH2","AUDPRC_TX_CH1","AUDPRC_TX_CH0","AUDPRC_RX_CH1","AUDPRC_RX_CH0","GPTIM2_CC2","GPTIM2_CC3",\
             "GPTIM2_CC4","SDMMC1","NONE","NONE","NONE","NONE","NONE","NONE"\
             )
dmac2_sel = ("USART4_TX","USART4_RX","USART5_TX","USART5_RX","NONE","NONE","BTIM3","BTIM4")

peri_dict = {"MPI1":{"tgt":"dst","addr":0x50041004},\
             "MPI2":{"tgt":"dst","addr":0x50042004},\
             "I2C1":{"tgt":"any","addr":0x5009c030},\
             "I2C2":{"tgt":"any","addr":0x5009d030},\
             "I2C3":{"tgt":"any","addr":0x5009e030},\
             "I2C4":{"tgt":"any","addr":0x5009f030},\
             "USART1_TX":{"tgt":"dst","addr":0x50084028},\
             "USART1_RX":{"tgt":"src","addr":0x50084024},\
             "USART2_TX":{"tgt":"dst","addr":0x50085028},\
             "USART2_RX":{"tgt":"src","addr":0x50085024},\
             "USART3_TX":{"tgt":"dst","addr":0x50086028},\
             "USART3_RX":{"tgt":"src","addr":0x50086024},\
             "USART4_TX":{"tgt":"dst","addr":0x40005028},\
             "USART4_RX":{"tgt":"src","addr":0x40005024},\
             "USART5_TX":{"tgt":"dst","addr":0x40006028},\
             "USART5_RX":{"tgt":"src","addr":0x40006024},\
             "SPI1_TX":{"tgt":"dst","addr":0x50095010},\
             "SPI1_RX":{"tgt":"src","addr":0x50095010},\
             "SPI2_TX":{"tgt":"dst","addr":0x50096010},\
             "SPI2_RX":{"tgt":"src","addr":0x50096010},\
             "I2S1_TX":{"tgt":"dst","addr":0x50009400},\
             "I2S1_RX":{"tgt":"src","addr":0x50009440},\
             "PDM1_L":{"tgt":"src","addr":0x5009a03c},\
             "PDM1_R":{"tgt":"src","addr":0x5009a040},\
             "SDMMC1":{"tgt":"any","addr":0x50045200}\
            }

class DMAAnalyzer(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")
        #self.print_reg(HPSYS_RCC)
        #以下是根据实例编号需要区分的内容
        if (inst_num==1):
            if (HPSYS_RCC.ENR1.DMAC1!=1):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块时钟未开启",\
                         "需将HPSYS_RCC的ESR1_DMAC1置1以开启模块时钟")
                inst_error = 1
            if (HPSYS_RCC.RSTR1.DMAC1!=0):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块被复位",\
                         "需将HPSYS_RCC的RSTR1_DMAC1置0以释放模块复位")
                inst_error = 1
        elif (inst_num==2):
            HPSYS_AON = self.read_peripheral("hpsys_aon")
            if (HPSYS_AON.ISSR.LP_ACTIVE == 0):
                self.log(LogLevel.ERROR, f"LPSYS处于睡眠状态，无法访问DMAC2",\
                         "需将LPSYS唤醒后才能访问")
                inst_error = 1
                return
            LPSYS_RCC = self.read_peripheral("lpsys_rcc")
            if (LPSYS_RCC.ENR1.DMAC2!=1):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块时钟未开启",\
                         "需将LPSYS_RCC的ESR1_DMAC2置1以开启模块时钟")
                inst_error = 1
            if (LPSYS_RCC.RSTR1.DMAC2!=0):
                self.log(LogLevel.ERROR, f"{self.periph_name}模块被复位",\
                         "需将LPSYS_RCC的RSTR1_DMAC2置0以释放模块复位")
                inst_error = 1
            
        #以下是各实例共同的内容
        if (inst_error==1):
            self.log(LogLevel.ERROR, f"{self.periph_name}上述初始配置发现错误，分析无法继续进行",\
                     "请将上述错误修正后重新启动分析")
            return
        #self.log(LogLevel.INFO, "初始配置分析完成，未发现错误，启动功能分析")

        DMAC = self.read_peripheral(f"{self.periph_name}")
        #遍历各通道
        ch_valid = [0,0,0,0,0,0,0,0,0]
        for ch_num in range(1,9):
            #获取通道寄存器
            ch_m2m = DMAC.__dict__[f"CCR{ch_num}"].MEM2MEM
            if (ch_num < 5):
                ch_sel = DMAC.CSELR1.__dict__[f"C{ch_num}S"]
            else:
                ch_sel = DMAC.CSELR2.__dict__[f"C{ch_num}S"]
            ch_en   = DMAC.__dict__[f"CCR{ch_num}"].EN
            ch_circ = DMAC.__dict__[f"CCR{ch_num}"].CIRC
            ch_dir  = DMAC.__dict__[f"CCR{ch_num}"].DIR
            ch_ndt  = DMAC.__dict__[f"CNDTR{ch_num}"].NDT
            if (ch_dir):
                ch_src_size = DMAC.__dict__[f"CCR{ch_num}"].MSIZE
                ch_src_inc  = DMAC.__dict__[f"CCR{ch_num}"].MINC
                ch_src_addr = DMAC.__dict__[f"CM0AR{ch_num}"].value
                ch_dst_size = DMAC.__dict__[f"CCR{ch_num}"].PSIZE
                ch_dst_inc  = DMAC.__dict__[f"CCR{ch_num}"].PINC
                ch_dst_addr = DMAC.__dict__[f"CPAR{ch_num}"].value
            else:
                ch_src_size = DMAC.__dict__[f"CCR{ch_num}"].PSIZE
                ch_src_inc  = DMAC.__dict__[f"CCR{ch_num}"].PINC
                ch_src_addr = DMAC.__dict__[f"CPAR{ch_num}"].value
                ch_dst_size = DMAC.__dict__[f"CCR{ch_num}"].MSIZE
                ch_dst_inc  = DMAC.__dict__[f"CCR{ch_num}"].MINC
                ch_dst_addr = DMAC.__dict__[f"CM0AR{ch_num}"].value
            if (ch_src_size == 0):
                ch_src_size_str = "单字节"
            elif (ch_src_size == 1):
                ch_src_size_str = "双字节"
            elif (ch_src_size == 2):
                ch_src_size_str = "四字节"
            if (ch_dst_size == 0):
                ch_dst_size_str = "单字节"
            elif (ch_dst_size == 1):
                ch_dst_size_str = "双字节"
            elif (ch_dst_size == 2):
                ch_dst_size_str = "四字节"
            if (ch_src_inc):
                ch_src_inc_str = "递增"
            else:
                ch_src_inc_str = ""
            if (ch_dst_inc):
                ch_dst_inc_str = "递增"
            else:
                ch_dst_inc_str = ""
            if (ch_en):
                ch_en_str = "已使能"
            else:
                ch_en_str = "未使能"
            if (ch_circ):
                ch_circ_str = "循环"
            else:
                ch_circ_str = ""
            if (ch_src_size < 3):
                ch_byte_num = ch_ndt * (2 ** ch_src_size)
            if (ch_src_addr == 0 and ch_dst_addr == 0):
                #地址未配置
                self.log(LogLevel.INFO, f"通道{ch_num}未使用")
            else:
                ch_valid[ch_num] = 1

                if (ch_m2m):
                    self.log(LogLevel.INFO, f"通道{ch_num}为存储器模式,\
0x{ch_src_addr:08x}{ch_src_size_str}{ch_src_inc_str}->\
0x{ch_dst_addr:08x}{ch_dst_size_str}{ch_dst_inc_str}{ch_circ_str},\
{ch_en_str},剩余{ch_ndt}次待搬运({ch_byte_num}字节)")
                    if (ch_circ):
                        self.log(LogLevel.WARN, f"循环存储器搬运",\
                                 "罕见用法，请检查需求是否合理")
                else:
                    if (inst_num == 1): #DMAC1
                        ch_req = dmac1_sel[ch_sel]
                    else: #DMAC2
                        if (ch_sel<8):
                            ch_req = dmac2_sel[ch_sel]
                        else:
                            ch_req = "NONE"
                    self.log(LogLevel.INFO, f"通道{ch_num}为外设模式,请求源是{ch_req},\
0x{ch_src_addr:08x}{ch_src_size_str}{ch_src_inc_str}->\
0x{ch_dst_addr:08x}{ch_dst_size_str}{ch_dst_inc_str}{ch_circ_str},\
{ch_en_str},剩余{ch_ndt}次待搬运({ch_byte_num}字节)")
                    #检查外设请求
                    if (ch_req == "NONE"):
                        self.log(LogLevel.ERROR, f"通道{ch_num}为外设模式,但没有配置正确的外设请求",\
                                 "请正确配置CSELR1/2.CxS寄存器")
                        inst_error = 1
                    #检查外设地址
                    if (self.check_peri_addr(ch_req,ch_src_addr,ch_dst_addr)):
                        inst_error = 1
                #检查地址是否合法
                if ((ch_src_size == 2) and (ch_src_addr & 3)):
                    self.log(LogLevel.ERROR, f"四字节搬运时，源地址0x{ch_src_addr:08x}不是四字节对齐",\
                             "源数据类型四字节时源地址必须为四字节对齐")
                    inst_error = 1
                elif ((ch_src_size == 1) and (ch_src_addr & 1)):
                    self.log(LogLevel.ERROR, f"双字节搬运时，源地址0x{ch_src_addr:08x}不是双字节对齐",\
                             "源数据类型双字节时源地址必须为双字节对齐")
                    inst_error = 1
                if ((ch_dst_size == 2) and (ch_dst_addr & 3)):
                    self.log(LogLevel.ERROR, f"四字节搬运时，目的地址0x{ch_dst_addr:08x}不是四字节对齐",\
                             "目的数据类型四字节时目的地址必须为四字节对齐")
                    inst_error = 1
                elif ((ch_dst_size == 1) and (ch_dst_addr & 1)):
                    self.log(LogLevel.ERROR, f"双字节搬运时，目的地址0x{ch_dst_addr:08x}不是双字节对齐",\
                             "目的数据类型双字节时目的地址必须为双字节对齐")
                    inst_error = 1
                if (self.check_addr(inst_num,ch_src_addr)):
                    self.log(LogLevel.ERROR, f"源地址0x{ch_src_addr:08x}处于非法区间",\
                                     "请检查地址设置")
                    inst_error = 1
                if (self.check_addr(inst_num,ch_dst_addr)):
                    self.log(LogLevel.ERROR, f"目的地址0x{ch_dst_addr:08x}处于非法区间",\
                                     "请检查地址设置")
                    inst_error = 1
                #检查中断使能
                if (DMAC.__dict__[f"CCR{ch_num}"].TCIE == 0 and DMAC.__dict__[f"CCR{ch_num}"].HTIE == 0):
                    self.log(LogLevel.WARN, f"传输完成中断(TCIE)与传输过半中断(HTIE)均未使能",\
                                     f"请将CCR{ch_num}.TCIE或HTIE置1使能中断")
                #检查传输完成标志
                if (DMAC.ISR.__dict__[f"TCIF{ch_num}"]):
                    self.log(LogLevel.INFO, f"通道传输完成，中断未处理")
        
        if (inst_error==1):
            self.log(LogLevel.ERROR, f"{self.periph_name}配置发现错误",\
                     "请检查配置")
        else:
            self.log(LogLevel.INFO, f"{self.periph_name}分析结束，未发现错误")


    #根据编号查询通道
    def check_dmac1_mapping(self,idx):
        DMAC = self.read_peripheral("dmac1")
        #self.print_reg(DMAC)
        ch_num = 0
        if   (DMAC.CSELR1.C1S == idx):
            ch_num = 1
        elif (DMAC.CSELR1.C2S == idx):
            ch_num = 2
        elif (DMAC.CSELR1.C3S == idx):
            ch_num = 3
        elif (DMAC.CSELR1.C4S == idx):
            ch_num = 4
        elif (DMAC.CSELR2.C5S == idx):
            ch_num = 5
        elif (DMAC.CSELR2.C6S == idx):
            ch_num = 6
        elif (DMAC.CSELR2.C7S == idx):
            ch_num = 7
        elif (DMAC.CSELR2.C8S == idx):
            ch_num = 8
        return ch_num


    #根据外设请求源检查传输地址,合法返回0
    def check_peri_addr(self,ch_req,ch_src_addr,ch_dst_addr):
        if (ch_req not in peri_dict):
            #没有找到固定绑定地址
            return 0
        tgt_addr = peri_dict[ch_req]["addr"];
        if (peri_dict[ch_req]["tgt"] == "dst"):
            if (ch_dst_addr != tgt_addr):
                self.log(LogLevel.ERROR, f"{ch_req}的目的地址0x{ch_dst_addr:08x}错误",\
                         f"应当为0x{tgt_addr:08x}")
                return 1
        elif (peri_dict[ch_req]["tgt"] == "src"):
            if (ch_src_addr != tgt_addr):
                self.log(LogLevel.ERROR, f"{ch_req}的源地址0x{ch_src_addr:08x}错误",\
                         f"应当为0x{tgt_addr:08x}")
                return 1
        elif (peri_dict[ch_req]["tgt"] == "any"): #可能是源地址或目的地址
            if (ch_src_addr != tgt_addr and ch_dst_addr != tgt_addr):
                self.log(LogLevel.ERROR, f"{ch_req}的地址0x{ch_src_addr:08x}或0x{ch_dst_addr:08x}错误",\
                         f"应当为0x{tgt_addr:08x}")
                return 1
        return 0

    
    #检查传输地址是否合法,合法返回0
    def check_addr(self,inst_num,addr):
        if (inst_num == 1):
            if (addr >= 0xa0000000 and addr < 0xa0010000): #HPSYS ROM
                return 0
            if (addr >= 0x20000000 and addr < 0x20080000): #HPSYS RAM
                return 0
            if (addr >= 0x50000000 and addr < 0x50100000): #HPSYS PERI
                return 0
            if (addr >= 0x60000000 and addr < 0xa0000000): #MPI
                return 0
            if (addr >= 0x20800000 and addr < 0x20860000): #LPSYS ROM
                return 0
            if (addr >= 0x20400000 and addr < 0x20410000): #LPSYS RAM
                return 0
            if (addr >= 0x40000000 and addr < 0x400d0000): #LPSYS PERI
                return 0
        elif (inst_num == 2):
            if (addr >= 0x2a000000 and addr < 0x2a080000): #HPSYS RAM
                return 0
            if (addr >= 0x50000000 and addr < 0x50100000): #HPSYS PERI
                return 0
            if (addr >= 0x60000000 and addr < 0xa0000000): #MPI
                return 0
            if (addr >= 0x00000000 and addr < 0x00060000): #LPSYS ROM
                return 0
            if (addr >= 0x20400000 and addr < 0x20410000): #LPSYS RAM
                return 0
            if (addr >= 0x40000000 and addr < 0x400d0000): #LPSYS PERI
                return 0
        return 1

        
register_analyzer("DMAC", DMAAnalyzer)

