PIC16C5X CPU设计

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系列的指令集

PIC16C5X CPU设计_第1张图片
面向字节类指令集.png
PIC16C5X CPU设计_第2张图片
面向位操作类指令.png
PIC16C5X CPU设计_第3张图片
常数操作和控制类指令.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计数计满时,溢出信号为高,系统重置。

实时时钟/计数器 RTCC(Real Time Clock/Count)[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设计)