279 lines
5.2 KiB
Verilog
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
|