锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

基于CPLD的计算机主板上电时序控制--状态机方式

时间:2023-01-21 20:00:00 d150steel连接器4394s15ac接近传感器

1. 前言

做博主的第一份工作FPGA公司给了工程师CPLD我练习了我的任务。我参与了几个主板的设计,并多次修改和使用相同的代码。跳槽后,整理代码,改进代码结构,优化可移植性。

使用单片机和常用的上电控制CPLD或者EC的方式,本文使用CPLD实现上电控制操作

1.1 需求

可完成以下要求

需求如下:

  1. 主板插电后,能够进行上电自启动
  2. 指示运行状态
  3. 支持软件关机重启(系统发送关机重启指令)
  4. 支持按钮开关机重启(硬件按钮,多个物理按钮或单个按钮)
  5. 支持远程开关重启(远程、网络或串口或数据线)
  6. 支持看门狗,检测系统是否正常运行
  7. 串口通信

以后有机会补充串口通信和看门狗。

2. 背景介绍

归根结底,上电时序控制要求芯片在有限的时间内发出电源Enable并检测信号Power Good继续下一个电源的信号。所以计划使用它状态机时序控制的方式。

CPU状态可以通过PWR_CTR获取0-1引脚

  1. 1个脉冲表示CPU上电完成,工作正常
  2. 12个脉冲表示CPU发送关机请求
  3. 5个脉冲表示CPU发送重启请求

不同CPU根据芯片参考手册,反馈不同。

上电要求如下:
上电时序

掉电要求如下:


3. 代码部分

3.1 定义端口

端口的名称应根据硬件设计师给出的原理图进行类似的命名,以便于沟通

    input       CPLD_CLK_50M,          //1.8V      //CPU reset low active      output reg  FT_POR_N,             //CPU power state out and control 1.8     output reg  FT_GPIO0_A1,     input       FT_PWR_CTR1,     input       FT_PWR_CTR0,         output      LED0,              //DDR reset ,low active     output      MEM_RESET_S3_N,          // POWER CONTROL     output reg  VDD_CORE_EN,         input       VDD_CORE_PWRGD,             output reg  VDDQ_EN,         input       VDDQ_PWRGD,             output reg  VTT_EN,      output reg  VPP_EN,     output reg  P3V3_EN,     output reg  P1V8_EN,                       //control    input       RST_IN,       input       RST_OUT,       input       PWRBTN,         input       PS_ON    

3.2 定义参数

定义了状态机的所有状态和所需的延迟值

  //state machine      parameter S0_WAIT_PWOER_ON      = 5'd0;      parameter S1_IDLE               = 5'd1;     parameter S2_OTHER_ON           = 5'd2;     parameter S3_CPU_CORE_UP        = 5'd3;     parameter S4_P1V8_ON            = 5'd4;     parameter S5_GPIO0_A1_DOWN      = 5'd5;     parameter S6_PCIE_RST           = 5'd6;     parameter S7_FT_RST_N_UP        = 5'd7;     parameter S8_WAIT_CPU_ACK       = 5'd8;     parameter S9_RECEIVE_CPU_ACK    = 5'd9;     parameter S10_S0_WORK           = ;
    parameter S11_POWER_DOWN        = 5'd11;
    parameter S12_PCIe_FT_DOWN      = 5'd12;

    parameter S13_IO_PWR_DOWN       = 5'd13;
    parameter S14_CORE_PWR_DOWN     = 5'd14;
    parameter S15_OTHER_PWR_DOWN    = 5'd15;

	// delay time
	parameter   TIME_20ms	= 27'd20;
    parameter   TIME_120ms  = 27'd120;    
    parameter   TIME_140ms  = 27'd140;
    parameter   TIME_150ms	= 27'd150;
    parameter   TIME_1000ms = 27'd1000;

3.3 定义变量

定义了一些变量:

  1. CPU状态捕捉相关的信号
  2. 状态机
  3. 一些flag信号
  4. 延时计数器
	
//******** variable definition ********//

    // 1kHZ clk
	reg [20:0] 	count_1kclk=1'b0;
	reg  		clk_1K=1'b0;

    //cpu state
	reg 		soft_reset=1'b0;
    reg         soft_s3_flag=1'b0;
    reg         soft_s5_flag=1'b0;
    reg         receive_cpu_1p=1'b0;
    reg         soft_s3_vtt_off_flag=1'b0;
    reg         soft_s3_vtt_on_flag=1'b0;
	reg         soft_s3_vtt_flag=1'b0;
	reg	[7:0]	pm_count,   pm_count_a, pm_count_b, pm_count_c;
	reg	[7:0]	statu_pm_count=8'b0;
	reg 		cpu_ack=0;

    //state machine
    reg [5:0]   current_state;
    reg [5:0]   next_state;

    //power on
    reg         first_power_on_flag     =   1;
    reg         power_on_flag           =   1;       
    //soft power down
    wire        soft_power_off_flag     =   soft_s5_flag;             
    //soft reset
    wire        soft_reset_flag         =   soft_reset;    

    wire		sys_rst;
    reg         first_sys_rst_flag      =0  ;
    reg	[15:0]	sys_rst_cnt             =0  ;        

    //key
    wire        hard_reset_flag;
    wire        hard_power_on_flag;
    wire        hard_power_off_flag;   
    reg         clear_hard_flag  =1'b0;

    //led conuter
   	reg [15:0]  led_cnt         =0;   

    //delay counter
    reg [15:0]  delay_cnt       =0;
    reg [15:0]  delay_time_set  =0;
    reg         delay_cnt_enable=0;


//******** end variable definition ********//

3.4 例化模块

例化了两次模块,因为按键输入分为电源键和复位键,模块支持复用,代码在最后。

  1. 可以捕捉一路信号
  2. 可以设置三种延时时间
  3. 可以输出三个flag信号
// ******** Instantiated module ******** // 

    // ***** PWRBTN key catch *****//

    //KEY_DELAY1 > KEY_DELAY2 > KEY_DELAY3
    // if key time > KEY_DELAY1 key_on_flag1=1
    // if KEY_DELAY1 > key time > KEY_DELAY2 key_on_flag2=1
    // if KEY_DELAY2 > key time > KEY_DELAY3 key_on_flag3=1

    key_catch #(
    .KEY_DELAY1(PWOER_OFF_DELAY),         
    .KEY_DELAY2(PWOER_ON_DELAY),       
    .KEY_DELAY3(PWOER_ON_DELAY-10),
    .ACTIVE_LEVEL(0)        
    ) key_catch_pwrpin(
    .clk                (clk_1K),
    .reset              (sys_rst),
    .key_in             (PWRBTN),
    .clear_flag         (clear_hard_flag),
    .key_on_flag1       (hard_power_off_flag),
    .key_on_flag2       (hard_power_on_flag),
    .key_on_flag3       ()
    );



    // ***** RST_IN key catch *****//

    //KEY_DELAY1 > KEY_DELAY2 > KEY_DELAY3
    // if key time > KEY_DELAY1 key_on_flag1=1
    // if KEY_DELAY1 > key time > KEY_DELAY2 key_on_flag2=1
    // if KEY_DELAY2 > key time > KEY_DELAY3 key_on_flag3=1


    key_catch #(
    .KEY_DELAY1(PWOER_RST_DELAY),       
    .KEY_DELAY2(PWOER_RST_DELAY-10),      
    .KEY_DELAY3(PWOER_RST_DELAY-10),
    .ACTIVE_LEVEL(0)        
    ) key_catch_rst(
    .clk                (clk_1K),
    .reset              (sys_rst),
    .key_in             (RST_IN),
    .clear_flag         (clear_hard_flag),
    .key_on_flag1       (hard_reset_flag),
    .key_on_flag2       (),
    .key_on_flag3       ()
    );

3.5 逻辑部分

见下文:

3.5.1 辅助逻辑

  1. 上电复位
  2. LED指示
  3. 首次开机标志
  4. 系统复位完成
  5. 延时计数器
        //***** CLK_1K generate *****// 
        always @( posedge CPLD_CLK_50M )
        begin
            count_1kclk		<=	count_1kclk+1'b1;  //poweroff reset flag
            if( count_1kclk == 25000 )begin
                clk_1K		<=	~clk_1K;
                count_1kclk	<=	1'b0;
            end
        end

        //***** LED *****//
        always @(posedge clk_1K ) begin
                led_cnt         <=led_cnt+1;                        
        end
        
        assign  LED0            =led_cnt[11];
        
        //***** system reset *****//
        always@(posedge clk_1K) begin 
            if(sys_rst_cnt<TIME_150ms)begin 
                sys_rst_cnt<=sys_rst_cnt+16'd1;
            end 
            else begin 
                sys_rst_cnt<=sys_rst_cnt;
            end 
        end 

        assign sys_rst = (sys_rst_cnt>TIME_20ms && sys_rst_cnt<TIME_120ms) ? 1:0;

        //***** first power on *****//
        always @(posedge clk_1K) begin
            if (soft_reset_flag || soft_power_off_flag) begin
                first_power_on_flag   <=0; 
            end else begin
                first_power_on_flag   <=first_power_on_flag;
            end
        end

        //***** system reset done flag *****//
        always @(posedge clk_1K ) begin
            if (sys_rst) begin
                first_sys_rst_flag    <=  1;
            end else begin
                first_sys_rst_flag    <=  first_sys_rst_flag;
            end
        end

        //***** delay conuter *****//
        always @(posedge clk_1K ) begin
            if (sys_rst) begin
                delay_cnt<=27'h0;       
            end else if(delay_cnt_enable==1 && (delay_cnt<delay_time_set)) begin
                delay_cnt<=delay_cnt+1;
            end else begin
                delay_cnt<=27'h0;
            end    
        end

3.5.2 CPU状态获取逻辑

暂时不贴出来,需要的可以评论留言!

3.5.3 状态机逻辑

        //***** Three-stage state machine *****//

        // first
        always @(posedge CPLD_CLK_50M) begin
                current_state <= next_state;
        end


        //second
        always @(*) begin
            case (current_state)
                S0_WAIT_PWOER_ON:begin
                    if (first_power_on_flag && first_sys_rst_flag && (delay_cnt== TIME_1000ms)) begin
                        next_state  =S1_IDLE;
                    end else if(soft_reset_flag ) begin
                        next_state  =S1_IDLE;
                    end else if(hard_power_on_flag || hard_reset_flag) begin
                        next_state  =S1_IDLE;
                    end else  begin    
                        next_state  =S0_WAIT_PWOER_ON;
                    end
                end//
                S1_IDLE:begin
                    if (delay_cnt== TIME_20ms) begin
                        next_state  =S2_OTHER_ON;

                    end else  begin    
                        next_state  =S1_IDLE;
                    end    
                end
                S2_OTHER_ON         :begin
                    if ( (delay_cnt== TIME_20ms)) begin
                        next_state  =S3_CPU_CORE_UP;
                    end else  begin    
                        next_state  =S2_OTHER_ON;
                    end  
                    end
                S3_CPU_CORE_UP    :begin
                    if ( (delay_cnt== TIME_20ms)) begin
                        next_state  =S4_P1V8_ON;
                    end else  begin    
                        next_state  =S3_CPU_CORE_UP;
                    end  
                    end
                S4_P1V8_ON      :begin
                    if ((delay_cnt== TIME_20ms)) begin
                        next_state  =S5_GPIO0_A1_DOWN;
                    end else  begin    
                        next_state  =S4_P1V8_ON;
                    end 
                    end
                S5_GPIO0_A1_DOWN     :begin
                    if ( (delay_cnt== TIME_140ms)) begin
                        next_state  =S6_PCIE_RST;
                    end else  begin    
                        next_state  =S5_GPIO0_A1_DOWN;
                    end  
                    end
                S6_PCIE_RST       :begin
                    if ((delay_cnt== TIME_20ms)) begin
                        next_state  =S7_FT_RST_N_UP;
                    end else  begin    
                        next_state  =S6_PCIE_RST;
                    end                
                    end
                S7_FT_RST_N_UP     :begin
                    if ((delay_cnt== TIME_120ms)) begin
                        next_state  =S8_WAIT_CPU_ACK;
                    end else  begin    
                        next_state  =S7_FT_RST_N_UP;
                    end 
                    end
                S8_WAIT_CPU_ACK   :begin
                    if (receive_cpu_1p == 1'b1) begin
                        next_state  =S9_RECEIVE_CPU_ACK;
                    end else  begin    
                        next_state  =S8_WAIT_CPU_ACK;
                    end 
                    end
                S9_RECEIVE_CPU_ACK:begin
                    if (cpu_ack==1'b1) begin
                        next_state  =S10_S0_WORK;
                    end else  begin    
                        next_state  =S9_RECEIVE_CPU_ACK;
                    end 
                    end
                S10_S0_WORK        :begin
                    if((soft_power_off_flag == 1'b1) || (soft_reset_flag == 1'b1))
                    begin 
                        next_state 	= 	S11_POWER_DOWN;
                    end else if(hard_power_off_flag || hard_reset_flag) begin
                        next_state  =   S11_POWER_DOWN;
                    end else begin 
                        next_state 	= 	S10_S0_WORK;
                    end
                    end 
                S11_POWER_DOWN     :begin
                    if ((delay_cnt== TIME_20ms)) begin
                        next_state  =S12_PCIe_FT_DOWN;
                    end else  begin    
                        next_state  =S11_POWER_DOWN;
                    end 
                    end
                S12_PCIe_FT_DOWN   :begin
                    if ((delay_cnt== TIME_150ms)) begin
                        next_state  =S13_IO_PWR_DOWN;
                    end else  begin    
                        next_state  =S12_PCIe_FT_DOWN;
                    end  
                    end
                S13_IO_PWR_DOWN    :begin
                    if ((delay_cnt== TIME_20ms)) begin
                        next_state  =S14_CORE_PWR_DOWN;
                    end else  begin    
                        next_state  =S13_IO_PWR_DOWN;
                    end 
                    end
                S14_CORE_PWR_DOWN  :begin
                    if ((delay_cnt== TIME_20ms)) begin
                        next_state  =S15_OTHER_PWR_DOWN;
                    end else  begin    
                        next_state  =S14_CORE_PWR_DOWN;
                    end 
                    end
                S15_OTHER_PWR_DOWN   :begin
                    if ((delay_cnt== TIME_1000ms)) begin
                        next_state  =S0_WAIT_PWOER_ON;
                    end else  begin    
                        next_state  =S15_OTHER_PWR_DOWN;
                    end 
                    end

                default: begin
                        next_state <= S0_WAIT_PWOER_ON;
                end
            endcase       
        end	



        //third
        always @(posedge CPLD_CLK_50M)begin
            if(sys_rst) begin
                    VDD_CORE_EN     <=1'b0;                                  
                    VDDQ_EN		    <=1'b0;                                      
                    VTT_EN          <=1'b0;                      
                    VPP_EN  	    <=1'b0;                       
                    P3V3_EN 	    <=1'b0; 			                        
                    P1V8_EN 	    <=1'b0; 			                        


                    FT_GPIO0_A1     <=1'b1;
                    FT_POR_N        <=1'b0;    
                    clear_hard_flag  <=1'b1;      


        end
            else begin
                case (current_state)
                S0_WAIT_PWOER_ON     :begin
                    delay_cnt_enable  <=1'b1;
                    delay_time_set  <=TIME_1000ms;
                end //
                S1_IDLE              :begin
                    VDD_CORE_EN     <=1'b0;                                  
                    VDDQ_EN		    <=1'b0;                                      
                    VTT_EN          <=1'b0;                      
                    VPP_EN  	    <=1'b0;                       
                    P3V3_EN 	    <=1'b0; 			                        
                    P1V8_EN 	    <=1'b0; 			                        


                    FT_GPIO0_A1     <=1'b1;
                    FT_POR_N        <=1'b0;    
                // GAMC_RST_N <=1'b0; 
                    delay_time_set  <=TIME_20ms;
                end
                S2_OTHER_ON           :begin
                    VDDQ_EN		    <=1'b1;                                      
                    VTT_EN          <=1'b1;                      
                    VPP_EN  	    <=1'b1;                       
                    P3V3_EN 	    <=1'b1;

                    delay_time_set  <=TIME_20ms;
                end
                S3_CPU_CORE_UP     	 :begin
                    VDD_CORE_EN     <=1'b1;

                    delay_time_set  <=TIME_20ms;
                end
                S4_P1V8_ON        	 :begin
                    P1V8_EN 	    <=1'b1; 

                    delay_time_set  <=TIME_20ms;
                end
                S5_GPIO0_A1_DOWN  	 :begin
                    FT_GPIO0_A1     <=1'b0;
                
                    delay_time_set  <=TIME_140ms;                
                end
                S6_PCIE_RST       	 :begin

                    //GAMC_RST_N <=1'b1; //display card reset,pcie,low active
                
                    delay_time_set  <=TIME_20ms;              
                end
                S7_FT_RST_N_UP     	 :begin
                    FT_POR_N        <=1'b1;

                    delay_time_set  <=TIME_120ms;              
                end

                S8_WAIT_CPU_ACK     :begin


                    cpu_ack			<= 	1'b1;
                    clear_hard_flag  <=  1'b1;
                    delay_time_set  <=TIME_20ms;
                end
                S9_RECEIVE_CPU_ACK  :begin
                
                    clear_hard_flag  <=1'b0;
                    delay_time_set  <=TIME_20ms;
                end
                S10_S0_WORK          :begin


                    delay_time_set  <=TIME_20ms;
                end

                S11_POWER_DOWN       :begin

                    power_on_flag   <=1'b0;     
                    delay_time_set  <=TIME_20ms;
                end
                S12_PCIe_FT_DOWN     :begin

                    FT_POR_N        <=1'b0;

                    delay_time_set  <=TIME_150ms;              
                end
                S13_IO_PWR_DOWN      :begin
                    P1V8_EN    		<=1'b0;

                    delay_time_set  <=TIME_20ms;                 
                end
                S14_CORE_PWR_DOWN    :begin
                    VDD_CORE_EN     <=1'b0;

                    delay_time_set  <=TIME_20ms;             
                end
                S15_OTHER_PWR_DOWN     :begin

                    VDDQ_EN		    <=1'b0;                                      
                    VTT_EN          <=1'b0;                      
                    VPP_EN  	    <=1'b0;                       
                    P3V3_EN 	    <=1'b0; 				
                    delay_time_set  <=TIME_1000ms;            
                end
                    default: begin
                    ;
                    end
                endcase
            end
        end

    // ******** sequential logic ******** // 

3.5.4 按键捕捉

见我的另一篇博客:

4. 后言

我的代码在我参与的项目中能够稳定运行,但是如果读者想要进行移植和二次开发还需要结合自己项目的实际情况,最好是理解设计代码。

完整代码不贴了,代码不是最完善的状态,欢迎批评指正,本篇博客仅供参考!

有任何疑问欢迎评论留言

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章