diff --git a/debounce.v b/debounce.v deleted file mode 100644 index 31a81bb..0000000 --- a/debounce.v +++ /dev/null @@ -1,66 +0,0 @@ -//-------------------------------------------------------------------------------- -// debounce.v -// Konstantin Pavlov, pavlovconst@gmail.com -//-------------------------------------------------------------------------------- - -// INFO -------------------------------------------------------------------------------- -// Debounce for two inpus signal samples -// Signal may and maynot be periodic -// Switches up and down with 3 ticks delay - - -/* --- INSTANTIATION TEMPLATE BEGIN --- - -debounce DB1 ( - .clk(), - .nrst( 1'b1 ), - .en( 1'b1 ), - .in(), - .out() - ); -defparam DB1.WIDTH = 1; - ---- INSTANTIATION TEMPLATE END ---*/ - - -module debounce(clk,nrst,en,in,out); - -input wire clk; -input wire nrst; -input wire en; - -input wire [(WIDTH-1):0] in; -output wire [(WIDTH-1):0] out; // also "present state" - -parameter WIDTH = 1; - -reg [(WIDTH-1):0] d1 = 0; -reg [(WIDTH-1):0] d2 = 0; - -always @ (posedge clk) begin - if (~nrst) begin - d1[(WIDTH-1):0] <= 0; - d2[(WIDTH-1):0] <= 0; - end - else begin - if (en) begin - d1[(WIDTH-1):0] <= d2[(WIDTH-1):0]; - d2[(WIDTH-1):0] <= in[(WIDTH-1):0]; - end; // if - end // else -end - -wire [(WIDTH-1):0] switch_hi = (d2[(WIDTH-1):0] & d1[(WIDTH-1):0]); -wire [(WIDTH-1):0] n_switch_lo = (d2[(WIDTH-1):0] | d1[(WIDTH-1):0]); - -SetReset SR ( - .clk(clk), - .nrst(nrst), - .s(switch_hi[(WIDTH-1):0]), - .r(~n_switch_lo[(WIDTH-1):0]), - .q(out[(WIDTH-1):0]), - .nq() - ); -defparam SR.WIDTH = WIDTH; - -endmodule \ No newline at end of file diff --git a/debounce_tb.v b/debounce_tb.v deleted file mode 100644 index 87a51c8..0000000 --- a/debounce_tb.v +++ /dev/null @@ -1,89 +0,0 @@ -//-------------------------------------------------------------------------------- -// debounce_tb.v -// Konstantin Pavlov, pavlovconst@gmail.com -//-------------------------------------------------------------------------------- - -// INFO -------------------------------------------------------------------------------- -// -// - -`timescale 1ns / 1ps - -module debounce_tb(); - -reg clk200; -initial begin - #0 clk200 = 1; - forever - #2.5 clk200 = ~clk200; -end - -reg rst; -initial begin - #10.2 rst = 1; - #5 rst = 0; - //#10000; - forever begin - #9985 rst = ~rst; - #5 rst = ~rst; - end -end -wire nrst = ~rst; - -reg rst_once; -initial begin // initializing non-X data before PLL starts - #10.2 rst_once = 1; - #5 rst_once = 0; -end -initial begin - #510.2 rst_once = 1; // PLL starts at 500ns, clock appears, so doing the reset for modules - #5 rst_once = 0; -end -wire nrst_once = ~rst_once; - -wire [31:0] DerivedClocks; -ClkDivider CD1 ( - .clk(clk200), - .nrst(nrst_once), - .out(DerivedClocks[31:0])); -defparam CD1.WIDTH = 32; - -wire [31:0] E_DerivedClocks; -EdgeDetect ED1 ( - .clk(clk200), - .nrst(nrst_once), - .in(DerivedClocks[31:0]), - .rising(E_DerivedClocks[31:0]), - .falling(), - .both() - ); -defparam ED1.WIDTH = 32; - -wire [15:0] RandomNumber1; -c_rand RNG1 ( - .clk(clk200), - .rst(rst_once), - .reseed(1'b0), - .seed_val(DerivedClocks[31:0]), - .out(RandomNumber1[15:0])); - -reg start; -initial begin - #100.2 start = 1; - #5 start = 0; -end - -//================================================= - -wire den = RandomNumber1[1] && RandomNumber1[2]; -wire dbout; -debounce DB1 ( - .clk(clk200), - .nrst(nrst_once), - .en(den), - .in(RandomNumber1[0]), - .out(dbout) - ); -defparam DB1.WIDTH = 1; - -endmodule diff --git a/debounce_v1.v b/debounce_v1.v new file mode 100644 index 0000000..56e7814 --- /dev/null +++ b/debounce_v1.v @@ -0,0 +1,118 @@ +//------------------------------------------------------------------------------ +// debounce_v1.v +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Button debounce v1 +// +// - sampling inputs using configurable divided clock (ithis is the +// simplest form of low-pass filter) +// - switching output only when both samples have equal level +// (this gives some form of hysteresis in case we sample unstable data) +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +debounce_v1 #( + .WIDTH( 4 ), + .SAMPLING_FACTOR( 16 ) +) DB1 ( + .clk( clk ), + .nrst( 1'b1 ), + .ena( 1'b1 ), + .in( btn[3:0] ), + .out( btn_db[3:0] ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module debounce_v1 #( parameter + WIDTH = 1, + SAMPLING_FACTOR = 16 // 0 - sampling every clk + // 1 - sampling on clk/2 + // 2 - sampling on clk/4 etc.... + + // only one or none should be enabled + TREAT_UNSTABLE_AS_HIGH = 0, + TREAT_UNSTABLE_AS_LOW = 0 +)( + input clk, + input nrst, + input ena, + + input [WIDTH-1:0] in, + output reg [WIDTH-1:0] out +); + + + localparam SAMPLING_RANGE = 32; + + + wire [SAMPLING_RANGE-1:0] s_clk; + clk_divider #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + .out( s_clk[SAMPLING_RANGE-1:0] ) + ); + + wire [SAMPLING_RANGE-1:0] s_clk_rise; + edge_detect #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div_ed ( + .clk( clk ), + .anrst( nrst ), + .in( s_clk[SAMPLING_RANGE-1:0] ), + .rising( s_clk_rise[SAMPLING_RANGE-1:0] ) + ); + + wire do_sample; + assign do_sample = s_clk_rise[SAMPLING_FACTOR]; + + + reg [WIDTH-1:0] in_d1 = 0; + reg [WIDTH-1:0] in_d2 = 0; + + always @(posedge clk) begin + if (~nrst) begin + in_d1[WIDTH-1:0] <= 0; + in_d2[WIDTH-1:0] <= 0; + end else if (ena && do_sample) begin + in_d1[WIDTH-1:0] <= in_d2[WIDTH-1:0]; + in_d2[WIDTH-1:0] <= in[WIDTH-1:0]; + end // if + end + + integer i; + always @(posedge clk) begin + if( ~nrst ) begin + out[WIDTH-1:0] <= 0; + end else begin + // every input has its own state + + for (i = 0; i < WIDTH; i=i+1) begin + + case ( {in_d2[i],in_d1[i]} ) + 2'b00: out[i] <= 1'b0; + 2'b11: out[i] <= 1'b1; + default: begin + if (TREAT_UNSTABLE_AS_HIGH) begin + out[i] <= 1'b1; + end else if (TREAT_UNSTABLE_AS_LOW) begin + out[i] <= 1'b0; + end + end + endcase + + end // for + end + end + +endmodule + diff --git a/debounce_v2.sv b/debounce_v2.sv new file mode 100644 index 0000000..af280fa --- /dev/null +++ b/debounce_v2.sv @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// debounce_v2.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Button debounce v2, SystemVerilog version +// +// - sampling inputs using configurable divided clock (ithis is the +// simplest form of low-pass filter) +// +// - in contrast with debounce_v1.v this implementation is switching output only +// when input had stable level IN ALL CLOCK CYCLES within the sample window +// (this gives some form of hysteresis in case we sample unstable data) +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +debounce_v2 #( + .WIDTH( 4 ), + .SAMPLING_FACTOR( 16 ) +) DB1 ( + .clk( clk ), + .nrst( 1'b1 ), + .ena( 1'b1 ), + .in( btn[3:0] ), + .out( btn_db[3:0] ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module debounce_v2 #( parameter + WIDTH = 1, + SAMPLING_FACTOR = 16, // 0 - sampling every clk + // 1 - sampling on clk/2 + // 2 - sampling on clk/4 etc.... + + // only one or none should be enabled + TREAT_UNSTABLE_AS_HIGH = 0, + TREAT_UNSTABLE_AS_LOW = 0 +)( + input clk, + input nrst, + input ena, + + input [WIDTH-1:0] in, + output logic [WIDTH-1:0] out +); + + + localparam SAMPLING_RANGE = 32; + + + logic [SAMPLING_RANGE-1:0] s_clk; + clk_divider #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + .out( s_clk[SAMPLING_RANGE-1:0] ) + ); + + logic [SAMPLING_RANGE-1:0] s_clk_rise; + edge_detect #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div_ed ( + .clk( clk ), + .anrst( nrst ), + .in( s_clk[SAMPLING_RANGE-1:0] ), + .rising( s_clk_rise[SAMPLING_RANGE-1:0] ) + ); + + wire do_sample; + assign do_sample = s_clk_rise[SAMPLING_FACTOR]; + + + logic [WIDTH-1:0] in_is_high = 0; + logic [WIDTH-1:0] in_is_low = 0; + + always_ff @(posedge clk) begin + if (~nrst) begin + out[WIDTH-1:0] <= 0; + + in_is_high[WIDTH-1:0] <= 0; + in_is_low[WIDTH-1:0] <= 0; + end else if (ena && do_sample) begin + + // making decisions for outputs + for (integer i = 0; i < WIDTH; i++) begin + case ( {in_is_high[i],in_is_low[i]} ) + 2'b01: out[i] <= 1'b0; + 2'b10: out[i] <= 1'b1; + default: begin + if (TREAT_UNSTABLE_AS_HIGH) begin + out[i] <= 1'b1; + end else if (TREAT_UNSTABLE_AS_LOW) begin + out[i] <= 1'b0; + end + end + endcase + end // for + + // resetting flags to initialize new sample window + in_is_high[WIDTH-1:0] <= 0; + in_is_low[WIDTH-1:0] <= 0; + + end else begin + + // collecting data + for (integer i = 0; i < WIDTH; i++) begin + if ( in[i] ) begin + in_is_high[i] <= 1'b1; + end else begin + in_is_low[i] <= 1'b1; + end + end // for + + end // if + end + +endmodule + diff --git a/debounce_v2.v b/debounce_v2.v new file mode 100644 index 0000000..e915daa --- /dev/null +++ b/debounce_v2.v @@ -0,0 +1,127 @@ +//------------------------------------------------------------------------------ +// debounce_v2.v +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// Button debounce v2 +// +// - sampling inputs using configurable divided clock (ithis is the +// simplest form of low-pass filter) +// +// - in contrast with debounce_v1.v this implementation is switching output only +// when input had stable level IN ALL CLOCK CYCLES within the sample window +// (this gives some form of hysteresis in case we sample unstable data) +// + + +/* --- INSTANTIATION TEMPLATE BEGIN --- + +debounce_v2 #( + .WIDTH( 4 ), + .SAMPLING_FACTOR( 16 ) +) DB1 ( + .clk( clk ), + .nrst( 1'b1 ), + .ena( 1'b1 ), + .in( btn[3:0] ), + .out( btn_db[3:0] ) +); + +--- INSTANTIATION TEMPLATE END ---*/ + + +module debounce_v2 #( parameter + WIDTH = 1, + SAMPLING_FACTOR = 16, // 0 - sampling every clk + // 1 - sampling on clk/2 + // 2 - sampling on clk/4 etc.... + + // only one or none should be enabled + TREAT_UNSTABLE_AS_HIGH = 0, + TREAT_UNSTABLE_AS_LOW = 0 +)( + input clk, + input nrst, + input ena, + + input [WIDTH-1:0] in, + output reg [WIDTH-1:0] out +); + + + localparam SAMPLING_RANGE = 32; + + + wire [SAMPLING_RANGE-1:0] s_clk; + clk_divider #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div ( + .clk( clk ), + .nrst( nrst ), + .ena( 1'b1 ), + .out( s_clk[SAMPLING_RANGE-1:0] ) + ); + + wire [SAMPLING_RANGE-1:0] s_clk_rise; + edge_detect #( + .WIDTH( SAMPLING_RANGE ) + ) clk_div_ed ( + .clk( clk ), + .anrst( nrst ), + .in( s_clk[SAMPLING_RANGE-1:0] ), + .rising( s_clk_rise[SAMPLING_RANGE-1:0] ) + ); + + wire do_sample; + assign do_sample = s_clk_rise[SAMPLING_FACTOR]; + + + reg [WIDTH-1:0] in_is_high = 0; + reg [WIDTH-1:0] in_is_low = 0; + + integer i; + always @(posedge clk) begin + if (~nrst) begin + out[WIDTH-1:0] <= 0; + + in_is_high[WIDTH-1:0] <= 0; + in_is_low[WIDTH-1:0] <= 0; + end else if (ena && do_sample) begin + + // making decisions for outputs + for (i = 0; i < WIDTH; i=i+1) begin + case ( {in_is_high[i],in_is_low[i]} ) + 2'b01: out[i] <= 1'b0; + 2'b10: out[i] <= 1'b1; + default: begin + if (TREAT_UNSTABLE_AS_HIGH) begin + out[i] <= 1'b1; + end else if (TREAT_UNSTABLE_AS_LOW) begin + out[i] <= 1'b0; + end + end + endcase + end // for + + // resetting flags to initialize new sample window + in_is_high[WIDTH-1:0] <= 0; + in_is_low[WIDTH-1:0] <= 0; + + end else begin + + // collecting data + for (i = 0; i < WIDTH; i=i+1) begin + if ( in[i] ) begin + in_is_high[i] <= 1'b1; + end else begin + in_is_low[i] <= 1'b1; + end + end // for + + end // if + end + +endmodule + diff --git a/debounce_v2_tb.sv b/debounce_v2_tb.sv new file mode 100644 index 0000000..1c53a42 --- /dev/null +++ b/debounce_v2_tb.sv @@ -0,0 +1,220 @@ +//------------------------------------------------------------------------------ +// debounce_v2_tb_tb.sv +// published as part of https://github.com/pConst/basic_verilog +// Konstantin Pavlov, pavlovconst@gmail.com +//------------------------------------------------------------------------------ + +// INFO ------------------------------------------------------------------------ +// debounce_v2 testbench + +// use this define to make some things differently in simulation +`define SIMULATION yes + +`timescale 1ns / 1ps + +module debounce_v2_tb(); + +initial begin + // Print out time markers in nanoseconds + // Example: $display("[T=%0t] start=%d", $realtime, start); + $timeformat(-9, 3, " ns"); + + // seed value setting is intentionally manual to achieve repeatability between sim runs + $urandom( 1 ); // SEED value +end + +logic clk200; +sim_clk_gen #( + .FREQ( 200_000_000 ), // in Hz + .PHASE( 0 ), // in degrees + .DUTY( 50 ), // in percentage + .DISTORT( 10 ) // in picoseconds +) clk200_gen ( + .ena( 1'b1 ), + .clk( clk200 ), + .clkd( ) +); + +logic nrst_once; + +logic [31:0] clk200_div; +clk_divider #( + .WIDTH( 32 ) +) cd1 ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .out( clk200_div[31:0] ) +); + +logic [31:0] clk200_div_rise; +edge_detect ed1[31:0] ( + .clk( {32{clk200}} ), + .anrst( {32{nrst_once}} ), + .in( clk200_div[31:0] ), + .rising( clk200_div_rise[31:0] ), + .falling( ), + .both( ) +); + +// external device "asynchronous" clock +logic clk33; +logic clk33d; +sim_clk_gen #( + .FREQ( 200_000_000 ), // in Hz + .PHASE( 0 ), // in degrees + .DUTY( 50 ), // in percentage + .DISTORT( 1000 ) // in picoseconds +) clk33_gen ( + .ena( 1'b1 ), + .clk( clk33 ), + .clkd( clk33d ) +); + + +logic rst; +initial begin + rst = 1'b0; // initialization + repeat( 1 ) @(posedge clk200); + + forever begin + repeat( 1 ) @(posedge clk200); // synchronous rise + rst = 1'b1; + //$urandom( 1 ); // uncomment to get the same random pattern EVERY nrst + + repeat( 2 ) @(posedge clk200); // synchronous fall, controls rst pulse width + rst = 1'b0; + + repeat( 100 ) @(posedge clk200); // controls test body width + end +end +logic nrst; +assign nrst = ~rst; + + +logic rst_once; +initial begin + rst_once = 1'b0; // initialization + repeat( 1 ) @(posedge clk200); + + repeat( 1 ) @(posedge clk200); // synchronous rise + rst_once = 1'b1; + + repeat( 2 ) @(posedge clk200); // synchronous fall, controls rst_once pulse width + rst_once = 1'b0; +end +//logic nrst_once; // declared before +assign nrst_once = ~rst_once; + + +// random pattern generation +logic [31:0] rnd_data; +always_ff @(posedge clk200) begin + rnd_data[31:0] <= $urandom; + end + +initial forever begin + @(posedge nrst); + $display("[T=%0t] rnd_data[]=%h", $realtime, rnd_data[31:0]); +end + + +// helper start strobe appears unpredictable up to 20 clocks after nrst +logic start; +initial forever begin + start = 1'b0; // initialization + + @(posedge nrst); // synchronous rise after EVERY nrst + repeat( $urandom_range(0, 20) ) @(posedge clk200); + start = 1'b1; + + @(posedge clk200); // synchronous fall exactly 1 clock after rise + start = 1'b0; +end + + +initial begin +// #10000 $stop; +// #10000 $finish; +end + +// sweeping pulses +logic sp = 1'b1; +logic [4:0] sp_duty_cycle = 8'd0; +initial forever begin + if( sp_duty_cycle[4:0] == 0 ) begin + sp = 1'b1; + repeat( 10 ) @(posedge clk200); + end + sp = 1'b0; + repeat( 1 ) @(posedge clk200); + sp = 1'b1; + repeat( 1 ) @(posedge clk200); + sp = 1'b0; + repeat( sp_duty_cycle ) @(posedge clk200); + sp_duty_cycle[4:0] = sp_duty_cycle[4:0] + 1'b1; // overflow is expected here +end + + +// Module under test =========================================================== + +logic [15:0] seq_cntr = '0; + +logic [31:0] id = '0; +always_ff @(posedge clk200) begin + if( ~nrst_once ) begin + seq_cntr[15:0] <= '0; + id[31:0] <= '0; + end else begin + // incrementing sequence counter + if( seq_cntr[15:0]!= '1 ) begin + seq_cntr[15:0] <= seq_cntr[15:0] + 1'b1; + end + + if( seq_cntr[15:0]<300 ) begin + id[31:0] <= '1; + //id[31:0] <= {4{rnd_data[15:0]}}; + end else begin + id[31:0] <= '0; + end + end +end + + +debounce_v2 #( + .WIDTH( 8 ), + .SAMPLING_FACTOR( 2 ) +) M ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .in( rnd_data[7:0] ), + .out( ) +); + +debounce_v2 #( + .WIDTH( 8 ), + .SAMPLING_FACTOR( 2 ), + .TREAT_UNSTABLE_AS_HIGH( 1 ) +) MH ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .in( rnd_data[7:0] ), + .out( ) +); + +debounce_v2 #( + .WIDTH( 8 ), + .SAMPLING_FACTOR( 2 ), + .TREAT_UNSTABLE_AS_LOW( 1 ) +) ML ( + .clk( clk200 ), + .nrst( nrst_once ), + .ena( 1'b1 ), + .in( rnd_data[7:0] ), + .out( ) +); + +endmodule +