The OrangeCrab Verilog example has an orangecrab_reset block that uses the user button to go into boot mode (DFU). Although I found it practical so you do not have to unplug and plug the board while pressing the user button-down, sometimes it is good to have a system reset button for your application (or you required a user button for something else).
I updated the block so you can have both functionalities on the board. A single "quick" press, will generate an internal pulse that can be used as a global system reset, or in this case, used to toggle the RGB LEDs.
The design also adds a simple XOR debouncer + timer to the usr_btn input.
/* BSD Zero Clause License Copyright (c) 2021 Danilo Ramos */ /* Module to give multiple functionality to a user input (button) by different press/hold actions - quick press: send system reset - press and hold: send boot_rst */ `default_nettype none module sys_boot_rst #( parameter CLK_FREQUENCY = 48000000, parameter BUTTON_LOGIC_LEVEL = 1, parameter SYS_RESET_LOGIC_LEVEL = 1, parameter SYS_RESET_LOGIC_DEBOUNCE_MS = 10, parameter BOOT_RESET_LOGIC_LEVEL = 1, parameter BOOT_LONG_PRESS_DURATION_MS = 1000 ) ( input wire clk, input wire usr_btn, output logic sys_rst, output logic boot_rst ); // Reset logic logic [1:0] xor_path; localparam sys_CLKS = $rtoi($ceil(CLK_FREQUENCY/1000*SYS_RESET_LOGIC_DEBOUNCE_MS)); localparam boot_CLKS = $rtoi($ceil(CLK_FREQUENCY/1000*BOOT_LONG_PRESS_DURATION_MS)); localparam BOOT_CNT_WL = $clog2(boot_CLKS); logic [BOOT_CNT_WL:0] boot_counter = '0; // Send proper rst assign sys_rst = (boot_counter==sys_CLKS) ? (SYS_RESET_LOGIC_LEVEL) : (~SYS_RESET_LOGIC_LEVEL); assign boot_rst = (boot_counter>=boot_CLKS) ? (BOOT_RESET_LOGIC_LEVEL):(~BOOT_RESET_LOGIC_LEVEL); always @(posedge clk) begin xor_path <= {xor_path[0], usr_btn}; if(^xor_path) begin boot_counter <= '0; end else begin if(xor_path[1]==BUTTON_LOGIC_LEVEL) begin if(boot_counter <= boot_CLKS) begin boot_counter <= boot_counter + 1; end else begin end end else begin boot_counter <= '0; end end end endmodule
One SV gotcha I forgot from time to time is the parameter declaration. Parameters types are inferred from the value. Most of the time, as the keyword is used to declare small integer values it is OK. But occasionally I do some large integer math with parameters. I was getting an error during the simulation and I realized that the values of the parameters were ill-calculated.
Initially, I was using the formula below
localparam boot_CLKS = $rtoi($ceil((CLK_FREQUENCY*BOOT_LONG_PRESS_DURATION_MS)/1000));
to calculate the number of clocks required to validate a boot hold_n_press action. That gave me a value of 48e9 which is greater than 2^32. I changed the formula to:
localparam boot_CLKS = $rtoi($ceil(CLK_FREQUENCY/1000*BOOT_LONG_PRESS_DURATION_MS));
where the division is done first before the multiplication and the calculated value by the tools were correct.
I tried a couple of tools with similar results. Looking closely you can infer that the parameters are declared as 32bit signed integers (SV standard) and therefore the overflow in the multiplication is "expected".
A recommendation, which I usually forgot to follow, is to always assign a proper type to parameter, and localparam declarations if you are doing some extra math.
You can find the complete code with a testbench at https://github.com/dramoz/orangecrab/tree/dev/usr_rst
You can read more about the OrangeCrab in my roadtest review.