diff --git a/firmware/main/pmu.c b/firmware/main/pmu.c index 2139803..95e5d01 100644 --- a/firmware/main/pmu.c +++ b/firmware/main/pmu.c @@ -14,17 +14,22 @@ struct wb_pmu { uint32_t stat; } __attribute__((packed,aligned(4))); -#define PMU_CTRL_SYS_SHUTDOWN (1 << 7) -#define PMU_CTRL_SYS_SUSPEND (1 << 6) -#define PMU_CTRL_USB_OFF (1 << 5) -#define PMU_CTRL_USB_ON (1 << 4) -#define PMU_CTRL_BOOT_EXEC (1 << 2) -#define PMU_CTRL_BOOT_SEL(n) (((n) & 3) << 0) +#define PMU_CTRL_BOOT_EXEC (1 << 18) +#define PMU_CTRL_BOOT_SEL(n) (((n) & 3) << 16) +#define PMU_CTRL_SYS_SHUTDOWN (1 << 11) +#define PMU_CTRL_SYS_SUSPEND (1 << 10) +#define PMU_CTRL_USB_OFF (1 << 9) +#define PMU_CTRL_USB_ON (1 << 8) +#define PMU_CTRL_BTN2_ACK (1 << 3) +#define PMU_CTRL_BTN1_ACK (1 << 1) -#define PMU_STAT_USB (1 << 4) -#define PMU_STAT_CHARGING (1 << 3) -#define PMU_STAT_VBUS (1 << 2) -#define PMU_STAT_BTN_MSK (3 << 0) +#define PMU_STAT_USB (1 << 8) +#define PMU_STAT_VBUS (1 << 5) +#define PMU_STAT_CHARGING (1 << 4) +#define PMU_STAT_BTN2_EVT (1 << 3) +#define PMU_STAT_BTN2_LONG (1 << 2) +#define PMU_STAT_BTN1_EVT (1 << 1) +#define PMU_STAT_BTN1_LONG (1 << 0) static volatile struct wb_pmu * const pmu_regs= (void*)(PMU_BASE); @@ -90,5 +95,27 @@ pmu_is_vbus_present(void) uint8_t pmu_get_buttons(void) { - return pmu_regs->stat & PMU_STAT_BTN_MSK; + uint32_t btn; + uint32_t ack = 0; + uint8_t rv = 0; + + /* Read status */ + btn = pmu_regs->stat; + + /* Translate to events */ + if (btn & PMU_STAT_BTN1_EVT) { + rv |= (btn & PMU_STAT_BTN1_LONG) ? BTN_1_LONG : BTN_1_SHORT; + ack |= PMU_CTRL_BTN1_ACK; + } + + if (btn & PMU_STAT_BTN2_EVT) { + rv |= (btn & PMU_STAT_BTN2_LONG) ? BTN_2_LONG : BTN_2_SHORT; + ack |= PMU_CTRL_BTN2_ACK; + } + + /* Ack */ + if (ack) + pmu_regs->ctrl = ack; + + return rv; } diff --git a/firmware/main/pmu.h b/firmware/main/pmu.h index 9ec799c..bf8782d 100644 --- a/firmware/main/pmu.h +++ b/firmware/main/pmu.h @@ -22,4 +22,9 @@ bool pmu_usb_state(void); bool pmu_is_charging(void); bool pmu_is_vbus_present(void); +#define BTN_2_LONG (1 << 3) +#define BTN_2_SHORT (1 << 2) +#define BTN_1_LONG (1 << 1) +#define BTN_1_SHORT (1 << 0) + uint8_t pmu_get_buttons(void); diff --git a/gateware/rtl/pmu.v b/gateware/rtl/pmu.v index 8b7fd01..81530f4 100644 --- a/gateware/rtl/pmu.v +++ b/gateware/rtl/pmu.v @@ -41,33 +41,28 @@ module pmu ( 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; + 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; + reg ctrl_sys_shutdown; + reg ctrl_sys_suspend; + reg ctrl_usb_off; + reg ctrl_usb_on; + reg [1:0] ctrl_btn_ack; + wire [1:0] stat_btn [0:1]; // 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; + wire [1:0] btn_trigged; + wire [1:0] btn_verylong; // Reboot reg boot_wb_now; @@ -90,14 +85,14 @@ module pmu ( // ------------- // Clears - assign bus_clr_rd = wb_ack | ~wb_cyc; - assign bus_clr_wr = wb_ack | ~wb_cyc | ~wb_we; + 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]; + assign bus_is_suspend = wb_we & wb_wdata[10]; always @(posedge clk) if (bus_clr_wr) @@ -113,10 +108,11 @@ module pmu ( wb_rdata <= { 27'd0, usb_ena, + 2'b00, stat_pwr_usb, stat_pwr_chg, - btn_val_wb[1], - btn_val_wb[0] + stat_btn[1], + stat_btn[0] }; // Writes @@ -127,12 +123,16 @@ module pmu ( ctrl_sys_suspend <= 1'b0; ctrl_usb_off <= 1'b0; ctrl_usb_on <= 1'b0; + ctrl_btn_ack[1] <= 1'b0; + ctrl_btn_ack[0] <= 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]; + ctrl_sys_shutdown <= wb_wdata[11]; + ctrl_sys_suspend <= wb_wdata[10]; + ctrl_usb_off <= wb_wdata[ 9]; + ctrl_usb_on <= wb_wdata[ 8]; + ctrl_btn_ack[1] <= wb_wdata[ 3]; + ctrl_btn_ack[0] <= wb_wdata[ 1]; end always @(posedge clk or posedge rst) @@ -140,8 +140,8 @@ module pmu ( 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]; + boot_wb_now <= wb_wdata[18]; + boot_wb_sel <= wb_wdata[17:16]; end @@ -156,57 +156,18 @@ module pmu ( .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) + // Submodule + pmu_button btn_I[1:0] ( + .si_stat ({stat_btn[1], stat_btn[0]}), + .si_ack (ctrl_btn_ack), + .si_clk (clk), + .si_rst (rst), + .btn_pad (btn), + .btn_trigged (btn_trigged), + .btn_verylong (btn_verylong), + .btn_clk (btn_clk) ); - // 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 // ------ @@ -214,7 +175,7 @@ module pmu ( // Command from wb or button force always @(*) begin - if (btn_long[0]) begin + if (btn_verylong[0]) begin boot_now = 1'b1; boot_sel = 2'b01; end else begin @@ -252,13 +213,13 @@ module pmu ( end // Enable system clock on wakeup - assign sys_start = wakeup; + assign sys_start = wakeup | |btn_trigged; // 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; + assign pwr_off_trig = btn_verylong[1] | ctrl_sys_shutdown; always @(posedge pwr_off_trig or posedge rst) if (rst) @@ -276,3 +237,170 @@ module pmu ( usb_ena <= (usb_ena | ctrl_usb_on) & ~ctrl_usb_off; endmodule // pmu + + +module pmu_button ( + // System interface + output wire [1:0] si_stat, + input wire si_ack, + input wire si_clk, + input wire si_rst, + + // Button side + input wire btn_pad, + output wire btn_trigged, + output wire btn_verylong, + input wire btn_clk +); + + // Signals + // ------- + + // Basic state + wire btn_iob; + reg [2:0] btn_sync = 3'b111; + + reg [4:0] btn_cnt = 5'd0; + wire btn_val; + + reg [16:0] btn_long_cnt = 16'd0; + wire btn_long; + + // System Interface + // System side + reg sis_ack_toggle = 1'b0; + reg [1:0] sis_trig_sync; + reg [1:0] sis_state; + + localparam [1:0] + STS_IDLE = 2'b00, + STS_TRIG_SHORT = 2'b10, + STS_TRIG_LONG = 2'b11, + STS_WAIT_REL = 2'b01; + + // Button side + reg [2:0] sib_ack_sync; + wire sib_ack; + reg [3:0] sib_state; + + localparam [2:0] + STB_IDLE = 3'b000, + STB_ARMED = 3'b101, + STB_TRIG_SHORT = 3'b110, + STB_TRIG_LONG = 3'b111, + STB_WAIT_REL = 3'b100; + + + // Basic state + // ----------- + + // IOBs + SB_IO #( + .PIN_TYPE(6'b000001), // PIN_OUTPUT_NONE, PIN_INPUT + .PULLUP(1'b1), + .IO_STANDARD("SB_LVCMOS") + ) btn_iob_I ( + .PACKAGE_PIN (btn_pad), + .D_IN_0 (btn_iob) + ); + + // Synchronizer + always @(posedge btn_clk) + btn_sync <= { btn_sync[1:0], btn_iob }; + + // Small counter for debounce + always @(posedge btn_clk) + btn_cnt <= btn_sync[2] ? (btn_cnt + {4'h0, btn_cnt[4]}) : 5'h10; + + assign btn_val = btn_cnt[4]; + + // Long counter to detect long and very long presses + // ( ~6.5 sec ) + always @(posedge btn_clk) + btn_long_cnt <= (btn_long_cnt + btn_val) & {17{btn_val}}; + + assign btn_long = btn_long_cnt[13]; + assign btn_verylong = btn_long_cnt[16]; + + + // System Interface [Sys Domain] + // ---------------- + + // Ack toggle + always @(posedge si_clk) + sis_ack_toggle <= sis_ack_toggle ^ (si_ack & sis_state[1]); + + // Synchronizer for trig signal + always @(posedge si_clk) + sis_trig_sync <= { sis_trig_sync[0], sib_state[3] }; + + // State + always @(posedge si_clk) + begin + case (sis_state) + STS_IDLE: + if (sis_trig_sync[1]) + sis_state <= sib_state[0] ? STS_TRIG_LONG : STS_TRIG_SHORT; + STS_TRIG_SHORT: + if (si_ack) + sis_state <= STS_WAIT_REL; + STS_TRIG_LONG: + if (si_ack) + sis_state <= STS_WAIT_REL; + STS_WAIT_REL: + if (~sis_trig_sync[1]) + sis_state <= STS_IDLE; + endcase + + if (si_rst) + sis_state <= STS_IDLE; + end + + assign si_stat = { sis_state[1], sis_state[1] & sis_state[0] }; + + + // System Interface [Button Domain] + // ---------------- + + // Ack sync / detect + always @(posedge btn_clk) + sib_ack_sync <= { ^sib_ack_sync[1:0], sib_ack_sync[0], sis_ack_toggle }; + + assign sib_ack = sib_ack_sync[2]; + + // State + always @(posedge btn_clk) + begin + // 3rd bit is always delayed version of trigger + sib_state[3] <= sib_state[1]; + + // Next state + case (sib_state[2:0]) + STB_IDLE: + if (btn_val) + sib_state[2:0] <= STB_ARMED; + + STB_ARMED: + if (btn_long) + sib_state[2:0] <= STB_TRIG_LONG; + else if (!btn_val) + sib_state[2:0] <= STB_TRIG_SHORT; + + STB_TRIG_SHORT: + if (sib_ack) + sib_state[2:0] <= STB_WAIT_REL; + + STB_TRIG_LONG: + if (sib_ack) + sib_state[2:0] <= STB_WAIT_REL; + + STB_WAIT_REL: + if (!btn_val) + sib_state[2:0] <= STB_IDLE; + endcase + end + + // External trig indicator + assign btn_trigged = sib_state[3]; + +endmodule // pmu_button