1. 前言
做博主的第一份工作FPGA公司给了工程师CPLD我练习了我的任务。我参与了几个主板的设计,并多次修改和使用相同的代码。跳槽后,整理代码,改进代码结构,优化可移植性。
使用单片机和常用的上电控制CPLD或者EC的方式,本文使用实现上电控制操作
1.1 需求
可完成以下要求
- 主板插电后,能够进行上电自启动
- 指示运行状态
- 支持软件关机重启(系统发送关机重启指令)
- 支持按钮开关机重启(硬件按钮,多个物理按钮或单个按钮)
- 支持远程开关重启(远程、网络或串口或数据线)
- 支持看门狗,检测系统是否正常运行
- 串口通信
以后有机会补充串口通信和看门狗。
2. 背景介绍
归根结底,上电时序控制要求芯片在有限的时间内发出电源Enable并检测信号Power Good继续下一个电源的信号。所以计划使用它时序控制的方式。
- 1个脉冲表示CPU上电完成,工作正常
- 12个脉冲表示CPU发送关机请求
- 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 = 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 辅助逻辑
//***** 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. 后言
我的代码在我参与的项目中能够稳定运行,但是如果读者想要进行移植和二次开发还需要结合自己项目的实际情况,最好是理解设计代码。
完整代码不贴了,代码不是最完善的状态,欢迎批评指正,本篇博客仅供参考!
有任何疑问欢迎评论留言