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

1. 前言

博主第一份工作做FPGA工程师,公司给了CPLD的任务给我练手,大大小小参与了几款主板上电的设计,同一套代码进行了多次修改运用。跳槽后,把代码整理一下,改善代码结构,优化可移植性。

CLPD是安路的,CPU是FT2000系列;

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

如果觉得对你有帮助的话,还请不忘点个赞!!!!

支持付费出售源码

1.1 需求

以下需求均可完成

需求如下:

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

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

2. 背景介绍

上电时序控制说到底,是要求芯片在限定的时间对电源发出Enable信号,并检测Power Good信号,在继续下一电源的上电。所以打算使用状态机的方式进行时序控制。

CPU状态能够通过PWR_CTR0-1引脚进行获取

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

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

FT2000的上电要求如下:
上电时序

上电表格

掉电要求如下:

掉电要求
掉电要求

仿真结果:
在这里插入图片描述

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           = 5'd10;
    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. 后言

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

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

有偿提供代码,欢迎私信联系

有任何疑问欢迎评论留言

  • 24
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bigbeea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值