Sylvain Munaut | 21b03ba | 2020-09-14 10:01:45 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * dfu_helper.v |
| 3 | * |
| 4 | * vim: ts=4 sw=4 |
| 5 | * |
| 6 | * Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com> |
| 7 | * SPDX-License-Identifier: CERN-OHL-P-2.0 |
| 8 | */ |
| 9 | |
| 10 | `default_nettype none |
| 11 | |
| 12 | module dfu_helper #( |
| 13 | parameter integer TIMER_WIDTH = 24, |
| 14 | parameter integer BTN_MODE = 3, // [2] Use btn_tick, [1] Include IO buffer, [0] Invert (active-low) |
| 15 | parameter integer DFU_MODE = 0 // 0 = For user app, 1 = For bootloader |
| 16 | )( |
| 17 | // External control |
| 18 | input wire [1:0] boot_sel, |
| 19 | input wire boot_now, |
| 20 | |
| 21 | // Button |
| 22 | input wire btn_pad, |
| 23 | input wire btn_tick, |
| 24 | |
| 25 | // Outputs |
| 26 | output wire btn_val, |
| 27 | output reg rst_req, |
| 28 | |
| 29 | // Clock |
| 30 | input wire clk, |
| 31 | input wire rst |
| 32 | ); |
| 33 | |
| 34 | // Signals |
| 35 | // ------- |
| 36 | |
| 37 | // Button |
| 38 | wire btn_iob; |
| 39 | wire btn_v; |
| 40 | wire btn_r; |
| 41 | wire btn_f; |
| 42 | |
| 43 | // Timer and arming logic |
| 44 | reg armed; |
| 45 | reg [TIMER_WIDTH-1:0] timer; |
| 46 | (* keep="true" *) wire timer_act; |
| 47 | |
| 48 | // Boot logic |
| 49 | reg [1:0] wb_sel; |
| 50 | reg wb_req; |
| 51 | reg wb_now; |
| 52 | |
| 53 | |
| 54 | // Button logic |
| 55 | // ------------ |
| 56 | |
| 57 | // IOB |
| 58 | generate |
| 59 | if (BTN_MODE[1]) |
| 60 | SB_IO #( |
| 61 | .PIN_TYPE(6'b000000), // Reg input, no output |
| 62 | .PULLUP(1'b1), |
| 63 | .IO_STANDARD("SB_LVCMOS") |
| 64 | ) btn_iob_I ( |
| 65 | .PACKAGE_PIN(btn_pad), |
| 66 | .INPUT_CLK (clk), |
| 67 | .D_IN_0 (btn_iob) |
| 68 | ); |
| 69 | else |
| 70 | assign btn_iob = btn_pad; |
| 71 | endgenerate |
| 72 | |
| 73 | // Deglitch |
| 74 | glitch_filter #( |
| 75 | .L(BTN_MODE[2] ? 2 : 4), |
| 76 | .RST_VAL(BTN_MODE[0]), |
| 77 | .WITH_SYNCHRONIZER(1), |
| 78 | .WITH_SAMP_COND(BTN_MODE[2]) |
| 79 | ) btn_flt_I ( |
| 80 | .in (btn_iob ^ BTN_MODE[0]), |
| 81 | .samp_cond(btn_tick), |
| 82 | .val (btn_v), |
| 83 | .rise (btn_r), |
| 84 | .fall (btn_f), |
| 85 | .clk (clk), |
| 86 | `ifdef SIM |
| 87 | .rst (rst) |
| 88 | `else |
| 89 | // Don't reset so we let the filter settle before |
| 90 | // the rest of the logic engages |
| 91 | .rst (1'b0) |
| 92 | `endif |
| 93 | ); |
| 94 | |
| 95 | assign btn_val = btn_v; |
| 96 | |
| 97 | |
| 98 | // Arming & Timer |
| 99 | // -------------- |
| 100 | |
| 101 | assign timer_act = btn_v ^ armed; |
| 102 | |
| 103 | always @(posedge clk or posedge rst) |
| 104 | if (rst) |
| 105 | armed <= 1'b0; |
| 106 | else |
| 107 | armed <= armed | timer[TIMER_WIDTH-2]; |
| 108 | |
| 109 | always @(posedge clk or posedge rst) |
| 110 | if (rst) |
| 111 | timer <= 0; |
| 112 | else |
| 113 | timer <= timer_act ? { TIMER_WIDTH{1'b0} } : (timer + { { (TIMER_WIDTH-1){1'b0} }, ~timer[TIMER_WIDTH-1] }); |
| 114 | |
| 115 | |
| 116 | // Boot Logic |
| 117 | // ---------- |
| 118 | |
| 119 | // Decision |
| 120 | always @(posedge clk or posedge rst) |
| 121 | if (rst) begin |
| 122 | wb_sel <= 2'b00; |
| 123 | wb_req <= 1'b0; |
| 124 | rst_req <= 1'b0; |
| 125 | end else if (~wb_req) begin |
| 126 | if (boot_now) begin |
| 127 | // External boot request |
| 128 | wb_sel <= boot_sel; |
| 129 | wb_req <= 1'b1; |
| 130 | rst_req <= 1'b0; |
| 131 | end else begin |
| 132 | if (DFU_MODE == 1) begin |
| 133 | // We're in a DFU bootloader, any button press results in |
| 134 | // boot to application |
| 135 | wb_sel <= 2'b10; |
| 136 | wb_req <= wb_now | (armed & btn_f); |
| 137 | rst_req <= 1'b0; |
| 138 | end else begin |
| 139 | // We're in user application, short press resets the |
| 140 | // logic, long press triggers DFU reboot |
| 141 | wb_sel <= 2'b01; |
| 142 | wb_req <= wb_now | (armed & btn_f & timer[TIMER_WIDTH-1]); |
| 143 | rst_req <= rst_req | (armed & btn_f & ~timer[TIMER_WIDTH-1]); |
| 144 | end |
| 145 | end |
| 146 | end |
| 147 | |
| 148 | // Ensure select bits are set before the boot pulse |
| 149 | always @(posedge clk or posedge rst) |
| 150 | if (rst) |
| 151 | wb_now <= 1'b0; |
| 152 | else |
| 153 | wb_now <= wb_req; |
| 154 | |
| 155 | // IP core |
| 156 | SB_WARMBOOT warmboot ( |
| 157 | .BOOT(wb_now), |
| 158 | .S0(wb_sel[0]), |
| 159 | .S1(wb_sel[1]) |
| 160 | ); |
| 161 | |
| 162 | endmodule // dfu_helper |