目录
1. 前言
博主第一份工作做FPGA工程师,公司给了CPLD的任务给我练手,大大小小参与了几款主板上电的设计,同一套代码进行了多次修改运用。跳槽后,把代码整理一下,改善代码结构,优化可移植性。
CLPD是安路的,CPU是FT2000系列;
常用的上电控制有使用单片机和CPLD或者EC的方式,本文使用CPLD实现上电控制操作
如果觉得对你有帮助的话,还请不忘点个赞!!!!
支持付费出售源码
1.1 需求
以下需求均可完成
需求如下:
- 主板插电后,能够进行上电自启动
- 指示运行状态
- 支持软件关机重启(系统发送关机重启指令)
- 支持按键开关机重启(硬件按键,可以多个实体按键也可以单个)
- 支持远程开关机重启(远程,网络或者串口或者就一根数据线)
- 支持看门狗,检测系统运行是否正常
- 串口通信
串口通信和看门狗以后有机会再补充。
2. 背景介绍
上电时序控制说到底,是要求芯片在限定的时间对电源发出Enable信号,并检测Power Good信号,在继续下一电源的上电。所以打算使用状态机的方式进行时序控制。
CPU状态能够通过PWR_CTR0-1引脚进行获取
- 1个脉冲表示CPU上电完成,工作正常
- 12个脉冲表示CPU发送关机请求
- 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 定义变量
定义了一些变量:
- CPU状态捕捉相关的信号
- 状态机
- 一些flag信号
- 延时计数器
//******** 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 例化模块
例化了两次模块,因为按键输入分为电源键和复位键,模块支持复用,代码在最后。
- 可以捕捉一路信号
- 可以设置三种延时时间
- 可以输出三个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 辅助逻辑
- 上电复位
- LED指示
- 首次开机标志
- 系统复位完成
- 延时计数器
//***** 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. 后言
我的代码在我参与的项目中能够稳定运行,但是如果读者想要进行移植和二次开发还需要结合自己项目的实际情况,最好是理解设计代码。
完整代码不贴了,代码不是最完善的状态,欢迎批评指正,本篇博客仅供参考!
有偿提供代码,欢迎私信联系
有任何疑问欢迎评论留言