xmas-snoopy/gateware/rtl/pmu.v

279 lines
5.2 KiB
Verilog

/*
* pmu.v
*
* vim: ts=4 sw=4
*
* Platorm Management Unit
*
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: CERN-OHL-P-2.0
*/
`default_nettype none
module pmu (
// Wishbone
input wire [15:0] wb_addr,
output reg [31:0] wb_rdata,
input wire [31:0] wb_wdata,
input wire wb_we,
input wire wb_cyc,
output reg wb_ack,
// Buttons
input wire [ 1:0] btn,
// Power control
input wire pwr_usb_n,
input wire pwr_chg_n,
output wire pwr_off,
// Clock control
output wire sys_start,
output wire sys_stop,
output reg usb_ena,
// Wake up signal
input wire wakeup,
// Clock / Reset
input wire clk,
input wire rst
);
integer i;
// Signals
// -------
// Bus interface
wire bus_clr_rd;
wire bus_clr_wr;
wire bus_is_suspend;
reg [3:0] bus_ack_dly;
reg bus_we;
// Control signals
reg ctrl_sys_shutdown;
reg ctrl_sys_suspend;
reg ctrl_usb_off;
reg ctrl_usb_on;
// Buttons
wire btn_clk;
wire [1:0] btn_iob;
reg [2:0] btn_sync [0:1];
reg [4:0] btn_cnt [0:1];
reg [1:0] btn_val;
reg [1:0] btn_val_wb;
reg [16:0] btn_long_cnt [0:1];
reg [1:0] btn_long;
// Reboot
reg boot_wb_now;
reg [1:0] boot_wb_sel;
reg boot_now;
reg [1:0] boot_sel;
// Power/Clock Status/Control
wire iob_pwr_usb_n;
wire iob_pwr_chg_n;
reg stat_pwr_usb;
reg stat_pwr_chg;
wire pwr_off_trig;
reg pwr_off_i = 1'b0;
// Bus interface
// -------------
// Clears
assign bus_clr_rd = wb_ack | ~wb_cyc;
assign bus_clr_wr = wb_ack | ~wb_cyc | ~wb_we;
// Ack
always @(posedge clk)
wb_ack <= wb_cyc & (~bus_is_suspend | bus_ack_dly[3]) & ~wb_ack;
assign bus_is_suspend = wb_we & wb_wdata[6];
always @(posedge clk)
if (bus_clr_wr)
bus_ack_dly <= 0;
else
bus_ack_dly <= bus_ack_dly + 1;
// Reads
always @(posedge clk)
if (bus_clr_rd)
wb_rdata <= 32'h00000000;
else
wb_rdata <= {
27'd0,
usb_ena,
stat_pwr_usb,
stat_pwr_chg,
btn_val_wb[1],
btn_val_wb[0]
};
// Writes
always @(posedge clk)
if (bus_clr_wr) begin
bus_we <= 1'b0;
ctrl_sys_shutdown <= 1'b0;
ctrl_sys_suspend <= 1'b0;
ctrl_usb_off <= 1'b0;
ctrl_usb_on <= 1'b0;
end else begin
bus_we <= 1'b1;
ctrl_sys_shutdown <= wb_wdata[7];
ctrl_sys_suspend <= wb_wdata[6];
ctrl_usb_off <= wb_wdata[5];
ctrl_usb_on <= wb_wdata[4];
end
always @(posedge clk or posedge rst)
if (rst) begin
boot_wb_now <= 1'b0;
boot_wb_sel <= 2'b00;
end else if (bus_we) begin
boot_wb_now <= wb_wdata[2];
boot_wb_sel <= wb_wdata[1:0];
end
// Buttons
// -------
// Oscillator for button logic
(* ROUTE_THROUGH_FABRIC=1 *)
SB_LFOSC btn_osc (
.CLKLFPU (1'b1),
.CLKLFEN (1'b1),
.CLKLF (btn_clk)
);
// IOBs
SB_IO #(
.PIN_TYPE(6'b000001), // PIN_OUTPUT_NONE, PIN_INPUT
.PULLUP(1'b1),
.IO_STANDARD("SB_LVCMOS")
) btn_iob_I[1:0] (
.PACKAGE_PIN (btn),
.D_IN_0 (btn_iob)
);
// Synchronizer
initial
for (i=0; i<2; i=i+1)
btn_sync[i] = 1;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_sync[i] <= { btn_sync[i][1:0], btn_iob[i] };
// Small counter for debounce
initial
for (i=0; i<2; i=i+1)
btn_cnt[i] = 0;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_cnt[i] <= btn_sync[i][2] ? (btn_cnt[i] + {4'h0, btn_cnt[i][4]}) : 5'h10;
always @(*)
for (i=0; i<2; i=i+1)
btn_val[i] = btn_cnt[i][4];
// Synchronize value in sys clock domain
always @(posedge clk)
for (i=0; i<2; i=i+1)
btn_val_wb[i] <= btn_val[i];
// Long counter to detect very long presses
// ( ~6.5 sec )
initial
for (i=0; i<2; i=i+1)
btn_long_cnt[i] = 0;
always @(posedge btn_clk)
for (i=0; i<2; i=i+1)
btn_long_cnt[i] <= (btn_long_cnt[i] + btn_val[i]) & {17{btn_val[i]}};
always @(*)
for (i=0; i<2; i=i+1)
btn_long[i] = btn_long_cnt[i][16];
// Reboot
// ------
// Command from wb or button force
always @(*)
begin
if (btn_long[0]) begin
boot_now = 1'b1;
boot_sel = 2'b01;
end else begin
boot_now = boot_wb_now;
boot_sel = boot_wb_sel;
end
end
// Core
SB_WARMBOOT warmboot (
.BOOT (boot_now),
.S0 (boot_sel[0]),
.S1 (boot_sel[1])
);
// Power / Clock control
// ---------------------
// Synchronizers for status
SB_IO #(
.PIN_TYPE(6'b000000), // PIN_OUTPUT_NONE, PIN_INPUT_REGISTERED
.PULLUP(1'b1),
.IO_STANDARD("SB_LVCMOS")
) stat_iob_I[1:0] (
.PACKAGE_PIN ({ pwr_usb_n, pwr_chg_n}),
.INPUT_CLK (clk),
.D_IN_0 ({iob_pwr_usb_n, iob_pwr_chg_n})
);
always @(posedge clk)
begin
stat_pwr_usb <= ~iob_pwr_usb_n;
stat_pwr_chg <= ~iob_pwr_chg_n;
end
// Enable system clock on wakeup
assign sys_start = wakeup;
// Disable system clock on requests
assign sys_stop = ctrl_sys_suspend;
// Power-off on long press and on requests
assign pwr_off_trig = btn_long[1] | ctrl_sys_shutdown;
always @(posedge pwr_off_trig or posedge rst)
if (rst)
pwr_off_i <= 1'b0;
else
pwr_off_i <= 1'b1;
assign pwr_off = pwr_off_i;
// USB ena
always @(posedge clk)
if (rst)
usb_ena <= 1'b0;
else
usb_ena <= (usb_ena | ctrl_usb_on) & ~ctrl_usb_off;
endmodule // pmu