美文网首页
PIC16C5X  CPU设计

PIC16C5X  CPU设计

作者: 简丨生 | 来源:发表于2019-01-01 17:34 被阅读0次

    PIC16C5X系列CPU

    PIC16C5X系列的基本介绍

    PIC16C5X是美国Microchip推出的世界上第一种8脚的超小型单片机系列,体积虽小但拥有很多功能特点,节省了很多其他单片机应用中必须外接的元器件,所以它是目前最便宜的8位OPT单片机。主要点特如下:

    1. 采用RISC,仅33条指令,指令字长为12位。除了涉及PC值改变的指令外,其余指令均为单周期指令。在本设计中,单周期为3个时钟周期,即两级流水线设计。
    2. 系统为哈佛结构。数据总线与指令总线各自独立。当一条指令在ALU中执行时,下一条指令已经被取出放到指令寄存器中等待执行了。
    3. 内部有7个特殊功能的寄存器,以操作I/O接口,设置看门狗和定时器的参数等。
    4. 系统可进入睡眠模式。功耗很低。
    5. 12~20根双向可独立的编程I/O口。考虑到兼容所有系列,故本设计有20个I/O接口,A口4位,B口8位,C口8位。
    6. 手册给出的工作频率为约20MHz,本设计基于sim13工艺库,工作频率可达50MHz。

    PIC16C5X系列的引脚以及功能

    引脚 功能描述
    RA0~RA3 I/O口,双向可编程
    RB0~RB7 I/O口,双向可编程
    RC0~RC7 I/O口,双向可编程
    MCLR 复位脚,低电平有效
    VDD 电源
    VSS
    T0CKI 外部时钟输入
    OSC1 振荡输入
    OSC2 振荡输出
    N/C 未用

    注:RTCC设置成内部定时器时(由程序设定),这时应将RTCC端接VSS或VDD,以避免干扰。采用RC振荡时,OSC2端输出一OSC1的4分频信号。

    PIC16C5X系列的内部结构

    PIC16C5X在一个芯片上集成了一个8位算术逻辑单元ALU和工作寄存器(W),以承担算数逻辑操作任务;384~2K的12位程序存储器ROM,在本设计中ROM并未实现,而是采用简单的办法,将指令信息放在测试文件中,设计中合理的改变PC的值就好了;80个8位的数据寄存器RAM;20个I/O端口;8位计数器及预分频器;时钟、复位以及看门狗计数器等。

    PIC16C5X提供二级堆栈,故子程序的条用仅有两层

    PIC16C5X系列的指令集

    面向字节类指令集.png 面向位操作类指令.png 常数操作和控制类指令.png

    内部功能设计思路及代码

    二级堆栈

    always @(posedge Clk)
    begin
        if(POR)
            TOS <=   12'h000;  // 重新上电,堆栈清0
        else if(CE)
            TOS <=   (CALL ? PC : (RETLW ? NOS : TOS)); //CALL指令,PC置入栈顶;RETLW下,栈顶推出,栈底变栈顶
    end
    always @(posedge Clk)
    begin
        if(POR)
            NOS <=   12'h000;  
        else if(CE)
            NOS <=   (CALL ? TOS : NOS);    //子程序调用,栈顶存储新的PC数据,原本栈顶给的数据推到栈底
    end
    

    二级堆栈的实现方式如上述代码,大体思路为:

    在系统重新上电时,堆栈的数据全部清零,即堆栈中的数据初始为0。

    程序正常运行过程中, 当主程序中出现CALL指令时,栈顶储存此时主程序的PC,栈底值仍为0。

    当子程序内部存在CALL指令时,栈底存储之前栈顶的值,即主程序的PC;栈顶存储此时第一级子程序的PC,保证子程序嵌套的正常运行。

    当子程序返回时,原本栈底的存储的信息顶入栈顶,栈底信息并未清0。实际上,依据上述的代码设计,仅有第二级子程序返回的时候,栈顶的数据会变化。

    看门狗

    assign WDT_Rst = Rst | WDTClr;      //WDT计数复位信号,全局复位或者看门狗清零引起。
    always @(posedge Clk)               
    begin
        if(WDT_Rst)
            WDT <=   0;
        else if (WDTE)                  //WDTE为WDT使能信号。清0可以使WDT不再计数
            WDT <=   WDT + 1;           //WDT计数
    end
    
    always @(posedge Clk)
    begin
        if(WDT_Rst)
            WDT_TC <=   0;              //复位下,WDT溢出信号清零
        else
            WDT_TC <=   &WDT;           //WDT计时达到最大,输出WDT_TC为1,表示溢出
    end
    

    看门狗的实现方式如上述代码,主要分为两种,即WDT正常计数以及WDT溢出,系统重置。实现的大体思路如下:

    系统的复位以及看门狗清零信号都会引起WDT的计数清零,同时溢出信号无效(为低)。

    WDTE信号置低,以关闭WDT的功能。

    正常计数情况下,WDT每个时钟周期自动加1,即使系统进入低功耗,即SLEEP模式也不例外。

    可通过位与操作判断WDT是否计满,仅当WDT计数计满时,溢出信号为高,系统重置。

    <font color=#DC143C>实时时钟/计数器 RTCC(Real Time Clock/Count)</font>[1]

    RTCC寄存器为PIC的操作寄存器F1,它用于对外加在RTCC引脚上的脉冲计数,或对内部时钟计数(起定时器作用)。OPTION特殊寄存器以配置RTCC和WDT的各种参数,包括分频参数,计数信号源,计数边沿等。RTCC计数器采用递增方式计数,当计数至FF时,在下一个计数发生后,将自动复零,重新开始计数,以此一直循环下去。

    always @(posedge Clk)
    begin
        if(POR)
            TMR0 <=   0;
        else if(WE_TMR0)
            TMR0 <=   DO;
        else if(CE_Tmr0)
            TMR0 <=   TMR0 + 1;
    end
    

    RTCC的实现方式如上述代码,具体实现思路如下:

    系统复位时,RTCC清零;

    RTCC可以外部置数。通过对F1寄存器的写,可以将数据写入这个寄存器中;

    当复位和写功能均无效时,根据OPTION寄存器的配置,RTCC可以完成相应的计数功能。CE_Tmr0信号的作用在于选择所配置的信号进行累加。

    由于RTCC寄存器依赖于OPTION寄存器的配置,故有必要说明OPTION寄存器的设置方法:

    assign T0CS = OPTION[5];     // 选择RTCC时钟源:         1 - 外部触发,    0 - 内部指令时钟
    assign T0SE = OPTION[4];     // RTCC的触发源:    1 - 下降沿,       0 - 上升沿
    assign PSA  = OPTION[3];     // 预分频对象选择:           1 - WDT,         0 - RTCC
    assign PS   = OPTION[2:0];   // 预设分频参数:        RTCC - 2^(PS+1), WDT - 2^PS
        
    assign Tmr0_CS = (T0CS ? T0CKI_Pls : CE);           // T0CS 为1选择外部计数脉冲,为0选择内部时钟 
    assign Rst_PSC   = (PSA ? WDTClr : WE_TMR0) | Rst;          //WDT清零和操作F1都会产生重置信号
    assign CE_PSCntr = (PSA ? WDT_TC : Tmr0_CS);
    
    always @(posedge Clk)
    begin
        if(Rst_PSC)
            PSCntr <=   8'b0;
        else if (CE_PSCntr)
            PSCntr <=   PSCntr + 1;             
        
    always @(*)
    begin
        case (PS)
            3'b000 : PSC_Out <= PSCntr[0];
            3'b001 : PSC_Out <= PSCntr[1];
            3'b010 : PSC_Out <= PSCntr[2];
            3'b011 : PSC_Out <= PSCntr[3];
            3'b100 : PSC_Out <= PSCntr[4];
            3'b101 : PSC_Out <= PSCntr[5];
            3'b110 : PSC_Out <= PSCntr[6];
            default: PSC_Out <= PSCntr[7];
        endcase
    end
    
    always @(posedge Clk)
    begin
        if(POR)
            dPSC_Out <=   0;
        else 
        begin
            dPSC_Out[0] <=   PSC_Out;
            dPSC_Out[1] <=   PSC_Out & ~dPSC_Out[0];        //前一个信号右推一个时钟周期,做上升沿的统计
        end
    end
    assign PSC_Pls = dPSC_Out[1];                                   
    
    always @(posedge Clk)
    begin
        if(Rst)
            dT0CKI <=   3'b0;
        else 
        begin
            dT0CKI[0] <=   T0CKI;               // 外部时钟和内部时钟的最小公倍数为dT0CKI[0]的周期
            dT0CKI[1] <=   dT0CKI[0];                           // dT0CKI[1]右推一个内部时钟周期
            dT0CKI[2] <=   (T0SE ? (dT0CKI[1] & ~dT0CKI[0])     // dT0CKI[1]下降沿出现脉冲
                                  :(dT0CKI[0] & ~dT0CKI[1]));   // dT0CKI[1]上升沿出现脉冲
        end
    end
    assign T0CKI_Pls = dT0CKI[2]; 
    
    assign CE_Tmr0 = (PSA ? Tmr0_CS : PSC_Pls); 
    

    上述代码即为OPTION寄存器的解码和相应的RTCC以及WDT的配置。

    第一段将OPTION寄存器的功能位相应解出,从上到下作用分别为:选择信号源T0CS;选择信号触发源T0SE;定时器和WDT选择位PSA;分频参数PS。详细可见手册。

    第二段则用简单的连续赋值语句和条件操作符完成选择信号源、选择RTCC和WDT的功能。注意到:考虑到WDT清零和RTCC的置数功能,有必要添加计数复位的情况,即Rst_PSC信号。

    第三段则实现内部时钟的计数功能,以为后面的信号分频做准备。

    第四段则为分频的处理,用case语句描述了信号分频的8种情况,最终从PSC_Out中得到对应的分频信号。

    第五段则是用以选择分频信号的脉冲,将上一段得到的PSC_OutT做出相位差,即dPSC_Out[0]信号比PSC_Out信号晚一个时钟周期,在通过一定的逻辑操作统计分频信号的上升沿。最终通过PSC_Pls输出对应的内部时钟的脉冲。

    第六段则是对外部输入信号的处理,复位情况下,外部的信号的输入寄存清零;正常运行时,通过周期的推迟和一定的逻辑操作,T0SE选择外部信号上升沿或者下降沿做统计,输出相应的外部时钟的脉冲信号T0CKI_Pls。

    最后一段则为RTCC的使能信号的选择。当PSA信号为1时,RTCC被选择。F1寄存器上的信息即为内部或者外部信号分频后的统计脉冲。当PSA信号为0时,WDT被选择。F1寄存器上的信息即为内部时钟的分频信息。

    再编码加速

    本设计针对指令采用了译码-再编码-再译码的加速操作,可以提高运算速度。

    第一段,针对指令表的译码操作,其中参数为局部变量定义,此处省略。

    assign dNOP    = (OP_NOP    == IR[11:0]);
    assign dMOVWF  = (OP_MOVWF  == IR[11:5]);
    assign dCLRW   = (OP_CLRW   == IR[11:0]);
    assign dCLRF   = (OP_CLRF   == IR[11:5]);
    assign dSUBWF  = (OP_SUBWF  == IR[11:6]);
    assign dDECF   = (OP_DECF   == IR[11:6]);
    assign dIORWF  = (OP_IORWF  == IR[11:6]);
    assign dANDWF  = (OP_ANDWF  == IR[11:6]);
    assign dXORWF  = (OP_XORWF  == IR[11:6]);
    assign dADDWF  = (OP_ADDWF  == IR[11:6]);
    assign dMOVF   = (OP_MOVF   == IR[11:6]);
    assign dCOMF   = (OP_COMF   == IR[11:6]);
    assign dINCF   = (OP_INCF   == IR[11:6]);
    assign dDECFSZ = (OP_DECFSZ == IR[11:6]);
    assign dRRF    = (OP_RRF    == IR[11:6]);
    assign dRLF    = (OP_RLF    == IR[11:6]);
    assign dSWAPF  = (OP_SWAPF  == IR[11:6]);
    assign dINCFSZ = (OP_INCFSZ == IR[11:6]);
    
    assign dBCF    = (OP_BCF    == IR[11:8]);
    assign dBSF    = (OP_BSF    == IR[11:8]);
    assign dBTFSC  = (OP_BTFSC  == IR[11:8]);
    assign dBTFSS  = (OP_BTFSS  == IR[11:8]);
    
    assign dOPTION = (OP_OPTION == IR[11:0]);
    assign dSLEEP  = (OP_SLEEP  == IR[11:0]);
    assign dCLRWDT = (OP_CLRWDT == IR[11:0]);
    assign dRETLW  = (OP_RETLW  == IR[11:8]);
    assign dCALL   = (OP_CALL   == IR[11:8]);
    assign dGOTO   = (OP_GOTO   == IR[11:9]);
    assign dMOVLW  = (OP_MOVLW  == IR[11:8]);
    assign dIORLW  = (OP_IORLW  == IR[11:8]);
    assign dANDLW  = (OP_ANDLW  == IR[11:8]);
    assign dXORLW  = (OP_XORLW  == IR[11:8]);
    assign dTRISA  = (OP_TRISA  == IR[11:0]);
    assign dTRISB  = (OP_TRISB  == IR[11:0]);
    assign dTRISC  = (OP_TRISC  == IR[11:0]);
    
    assign dErr    = ~|{  dNOP, dMOVWF,  dCLRW,  dCLRF,  
                        dSUBWF, dDECF,   dIORWF, dANDWF,  
                        dXORWF, dADDWF,  dMOVF,  dCOMF,   
                        dINCF,  dDECFSZ, dRRF,   dRLF,    
                        dSWAPF, dINCFSZ,
                        
                          dBCF,   dBSF,    dBTFSC, dBTFSS,
                        
                        dOPTION, dSLEEP, dCLRWDT, dRETLW, 
                        dCALL,   dGOTO,  dMOVLW,  dIORLW, 
                        dANDLW,  dXORLW, dTRISA,  dTRISB, 
                        dTRISC};
    

    第二段,指令归类,便于后续的在编码。大体可有算数指令,逻辑指令,移位指令,位操作,与W寄存器有关的操作,与数据寄存器有关的操作,间址寻址操作,跳操作,写数据寄存器的操作,写W寄存器的操作。

    assign dAU_Op   = |{dSUBWF, dDECF, dADDWF, dINCF, dMOVF, dDECFSZ, dINCFSZ}; 
    
    assign dLU_Op   = |{dCOMF, dIORWF, dANDWF, dXORWF};
    
    assign dSU_Op   = |{dRRF, dRLF, dSWAPF};
    
    assign dBP_Op   = |{dBCF, dBSF, dBTFSC, dBTFSS};
    
    assign dLW_Op   = |{dCLRW,  dRETLW, dMOVLW, dIORLW, dANDLW, dXORLW};
    
    assign dFile_En = |{dMOVWF, dCLRF,  dAU_Op, dLU_Op, dSU_Op, dBP_Op};
    
    assign dINDF    = dFile_En & (IR[4:0] == pINDF);
    
    assign dTst  = |{dDECFSZ, dINCFSZ, dBTFSC, dBTFSS};
    
    assign dWE_F = |{dBP_Op, ((dAU_Op | dLU_Op | dSU_Op) &  IR[5]), dMOVWF, dCLRF};
    
    assign dWE_W = |{dLW_Op, ((dAU_Op | dLU_Op | dSU_Op) & ~IR[5])};
    

    第三段,再编码,使后续运算并行,提高处理速度。代码中附注释。

    assign dALU_Op[ 0] = (dBP_Op ? IR[5] : |{dSUBWF, dINCF, dINCFSZ,
                                             dIORLW, dXORLW,
                                             dIORWF, dXORWF,
                                             dRLF,   dSWAPF});
                                             
    assign dALU_Op[ 1] = (dBP_Op ? IR[6] : |{dSUBWF, dDECF,  dDECFSZ,
                                             dANDWF, dXORWF, dANDLW,  dXORLW,
                                             dRRF,   dRLF});
                                             
    assign dALU_Op[ 2] = (dBP_Op ? IR[7] : |{dSUBWF, dADDWF, dMOVWF,
                                             dIORWF, dANDWF, dXORWF,
                                             dIORLW, dANDLW, dXORLW});
                                                                                 
    assign dALU_Op[ 3] = |{dBSF, dBTFSS,   dCALL,  dRETLW,
                           dMOVLW, dIORLW, dANDLW, dXORLW};
        
    assign dALU_Op[ 4] = |{dSUBWF, dADDWF, dRRF, dRLF};
    
    assign dALU_Op[ 5] = |{dCLRW,  dCLRF,  dSUBWF, dDECF,  dADDWF, dINCF, dMOVF,
                           dIORWF, dANDWF, dXORWF, dIORLW, dANDLW, dXORLW};
                                           
    assign dALU_Op[ 6] = dBP_Op | dLU_Op | dIORLW | dANDLW | dXORLW;
    
    assign dALU_Op[ 7] = dBP_Op | dSU_Op | dMOVWF | dCLRW  | dCLRF;
    
    assign dALU_Op[ 8] = dTst;
    
    assign dALU_Op[ 9] = dINDF;
    
    assign dALU_Op[10] = dWE_W;
    
    assign dALU_Op[11] = dWE_F;
    
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////////
    //
    //  ALU Implementation - ALU[3:0] are overloaded for the four ALU elements:
    //                          Arithmetic Unit, Logic Unit, Shift Unit, and Bit
    //                          Processor.
    //
    //  ALU Operations - Arithmetic, Logic, and Shift Units
    //
    //  ALU_Op[1:0] = ALU Unit Operation Code
    //
    //      Arithmetic Unit (AU): 00 => Y = A +  B;
    //                            01 => Y = A +  B + 1;
    //                            10 => Y = A + ~B     = A - B - 1;
    //                            11 => Y = A + ~B + 1 = A - B;
    //
    //      Logic Unit (LU):      00 => V = ~A;
    //                            01 => V =  A & B;
    //                            10 => V =  A | B;
    //                            11 => V =  A ^ B;
    //
    //      Shift Unit (SU):      00 => S = W;                // MOVWF
    //                            01 => S = {A[3:0], A[7:4]}; // SWAPF
    //                            10 => S = {C, A[7:1]};      // RRF
    //                            11 => S = {A[6:0], C};      // RLF
    //
    //  ALU_Op[3:2] = ALU Operand:
    //                  A      B
    //          00 =>  File    0
    //          01 =>  File    W
    //          10 => Literal  0
    //          11 => Literal  W;
    //
    //  ALU Operations - Bit Processor (BP)
    //
    //  ALU_Op[2:0] = Bit Select: 000 => Bit 0;
    //                            001 => Bit 1;
    //                            010 => Bit 2;
    //                            011 => Bit 3;
    //                            100 => Bit 4;
    //                            101 => Bit 5;
    //                            110 => Bit 6;
    //                            111 => Bit 7;
    //
    //  ALU_Op[3] = Set: 0 - Clr Selected Bit;
    //                   1 - Set Selected Bit;
    //
    //  ALU_Op[5:4] = Status Flag Update Select
    //
    //          00 => None
    //          01 => C
    //          10 => Z
    //          11 => Z,DC,C
    //
    //  ALU_Op[7:6] = ALU Output Data Multiplexer
    //
    //          00 => AU
    //          01 => LU
    //          10 => SU
    //          11 => BP
    //
    //  ALU_Op[8]  = Tst: 0 - Normal Operation
    //                    1 - Test: INCFSZ/DECFSZ/BTFSC/BTFSS
    //
    //  ALU_Op[9]  = Indirect Register, INDF, Selected
    //
    //  ALU_Op[10] = Write Enable Working Register (W)
    //
    //  ALU_Op[11] = Write Enable File {RAM | Special Function Registers}
    ////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////
    

    第四段,再编码后的译码操作,后译码需结合各种运算单元。

    // 算术指令操作数
    assign C_In  = ALU_Op[0];   // 加法的进位输入
    assign B_Inv = ALU_Op[1];   // B操作数取反,完成减法
    assign A_Sel = ALU_Op[3];   // A操作数选择
    assign A = A_Sel ? KI : DI; // A_Sel为高,选择立即数,A_Sel为低,数据寄存器中的数据
    assign B_Sel = ALU_Op[2];   // B操作数选择
    assign B = B_Sel ?  W : 0;  // B_Sel为高,选择W操作寄存器中的数,B_Sel为低,数据置0
    assign Y = B_Inv ? ~B : B;  // B是取反相,为减法做准备
    
    // 算术单元
    assign {DC_In, X[3:0]} = A[3:0] + Y[3:0] + C_In;
    assign {C_Out, X[7:4]} = A[7:4] + Y[7:4] + DC_In;
    
    // 逻辑单元
    assign LU_Op = ALU_Op[1:0];
    always @(*)
    begin
        case (LU_Op)
            2'b00   : V <= ~A;
            2'b01   : V <=  A | B;
            2'b10   : V <=  A & B;
            default : V <=  A ^ B;
        endcase
    end
    
    // 移动和交换单元
    assign S_Sel = ALU_Op[1:0];
    always @(*)
    begin
        case (S_Sel)
            2'b00   : S <= B;                  
            2'b01   : S <= {A[3:0], A[7:4]};   
            2'b10   : S <= {C, A[7:1]};        
            default : S <= {A[6:0], C};        
        endcase
    end
    
    // 位操作单元
    assign Bit = ALU_Op[2:0];       //位选
    assign Set = ALU_Op[3];         //置1还是清0
    assign Tst = ALU_Op[8];         //是否测试跳
        // 位选
    always @(*)
    begin
        case(Bit)
            3'b000  : Msk <= 8'b0000_0001;
            3'b001  : Msk <= 8'b0000_0010;
            3'b010  : Msk <= 8'b0000_0100;
            3'b011  : Msk <= 8'b0000_1000;
            3'b100  : Msk <= 8'b0001_0000;
            3'b101  : Msk <= 8'b0010_0000;
            3'b110  : Msk <= 8'b0100_0000;
            default : Msk <= 8'b1000_0000;
        endcase
    end
    assign U = Set ? (DI | Msk) : (DI & ~Msk);          // 对DI指定位进行置位
    assign T = DI & Msk;        //测试DI的指定位,如果为1,且Msk也为1,则T中仅有一个1,反之T全0
    assign g = Tst ? (Set ? |T : ~|T) : 1'b0;   
    //如果是跳指令,则1跳的话,对T进行位或,可得指定位,
    //反之0跳,T比全0,需要位或后取反。存在一个Bug,BCFSS与BSFSS并不置位!!!
    
    // 算术单元的输出
    assign D_Sel = ALU_Op[7:6];
    always @(*)
    begin
        case (D_Sel)
            2'b00   : DO <= X; 
            2'b01   : DO <= V; 
            2'b10   : DO <= S; 
            default : DO <= U; 
        endcase
    end
    
    // 写W寄存器
    assign WE_W = CE & ALU_Op[10];
    always @(posedge Clk)
    begin
        if(POR)
            W <=   8'b0;        
        else if(CE)
            W <=   (WE_W ? DO : W);     // 写使能信号有效下,将ALU输出或者地址上的数据写入W
    end
    
    // 状态位Z的操作
    assign Z_Sel = ALU_Op[5];   
    assign Z_Tst = ~|DO;                //DO全零下,Z状态应该为高。
    always @(posedge Clk)
    begin
        if(POR)
            Z <=   1'b0;
        else if(CE)
            Z <=   (Z_Sel  ? Z_Tst : (WE_PSW ? DO[2] : Z));    //Z的置位分两种情况,数据输出全0或者写PSW
    end
    
    // 状态位DC操作
    assign DC_Sel = ALU_Op[5] & ALU_Op[4];
    always @(posedge Clk)
    begin
        if(POR)
            DC <=   1'b0;
        else if(CE)
            DC <=   (DC_Sel ? DC_In : (WE_PSW ? DO[1] : DC));
    end
    
    // 状态位C的操作
    assign C_Sel = ALU_Op[4];                   //算数加减引起C位变化
    assign S_Dir = ALU_Op[1] & ALU_Op[0];        //移位引起的状态C的变化,11左移,不是11右移
    assign C_Drv = (~ALU_Op[7] & ~ALU_Op[6]) ? C_Out : (S_Dir ? A[7] : A[0]);
    always @(posedge Clk)
    begin
        if(POR)
            C <=   1'b0;
        else if(CE)
            C <=   (C_Sel  ? C_Drv : (WE_PSW ? DO[0] : C));
    end
    
    // 跳操作
    always @(*)
    begin
        Skip <= WE_SLEEP | WE_PCL       
                | (Tst ? ((&ALU_Op[7:6]) ? g    : Z_Tst)    
                       : ((GOTO | CALL | RETLW)   ? 1'b1 : 1'b0 ));         
    //  Sleep和写F2都会使PC跳过下一条指令
    //  g为BCFSS和BSFSS引起的跳,Z_Tst为INCFSZ和DECFSZ引起的跳
    //  GOTO CALL和RETLW引起跳过下条指令
    end
    
    // 间址寻址
    assign INDF = ALU_Op[9];    
    assign FA   = (INDF ? FSR : (KI[4] ? {FSR[6:5], KI[4:0]} : {2'b0, KI[4:0]}));
    

    设计理解结语

    这个PIC16C5X系列CPU的设计是基于Github上Michael A. Morris的源代码,理解并稍作修改而成。作为eda课程的作业以及初入数字IC设计的第一个完整代码,在各种设计思想上都有很大的益处。不同于雷思磊在【自己动手写CPU】中较为明确的模块拆分和复杂情况,PIC CPU在本设计仅有一个模块,可读性上也许更好,因人而异吧。而考虑到独特的再编码加速设计,也对于Verilog的设计有一定的启发作用,不同于本科接触的仅考虑实现前仿的功能,数字IC的涉及出发点还应该是基于DC综合的电路方法,以优化IC面积和时序的方式的角度去设计。

    本设计代码基本实现了PIC16C5X系列手册的要求,33条指令,特殊的操作寄存器,看门狗,定时器,二级堆栈等等。具体到细节内部,有些地方仍缺乏理解,会在之后详细的考虑之后做出修改和更新。

    第一版 2019.1.1


    1. OPTION配置第六段存在仍未完全理解的问题

    相关文章

      网友评论

          本文标题:PIC16C5X  CPU设计

          本文链接:https://www.haomeiwen.com/subject/nlanlqtx.html