This counter counts by the INCREMENT
parameter value, up or down, when
run
is high. Set up_down
to 0 to count up, or to 1 to count down.
Load overrides counting, so you can load a load_count
value even if run
is low. clear
puts the counter back at INITIAL_COUNT. The counter will
wrap around if it goes below zero or above (2^WORD_WIDTH)-1
and set
overflow
for that cycle.
The INCREMENT
parameter allows you to deal with situations where, for
example, you need to count the number of bytes transferred, but your
interface transfers multiple bytes per cycle.
When chaining counters, which may happen if you are counting in unusual
bases where each digit has its own counter, AND the carry_out
of the
previous counter with the signal fed to the run
input of the next
counter. The carry_in
is kept for generality, as are the carries
into
each bit.
`default_nettype none module Counter_Binary #( parameter WORD_WIDTH = 0, parameter [WORD_WIDTH-1:0] INCREMENT = 0, parameter [WORD_WIDTH-1:0] INITIAL_COUNT = 0 ) ( input wire clock, input wire clear, input wire up_down, // 0/1 --> up/down input wire run, input wire load, input wire [WORD_WIDTH-1:0] load_count, input wire carry_in, output wire carry_out, output wire [WORD_WIDTH-1:0] carries, output wire overflow, output wire [WORD_WIDTH-1:0] count ); localparam WORD_ZERO = {WORD_WIDTH{1'b0}};
First we calculate the next counter value using a Binary Adder/Subtractor. Having a dedicated module for this allows us to change how the counter works (e.g.: BCD or other counting schemes) without altering any other logic. It also hides the tricks needed for correct arithmetic logic inference.
wire [WORD_WIDTH-1:0] incremented_count; wire carry_out_internal; wire [WORD_WIDTH-1:0] carries_internal; wire overflow_internal; Adder_Subtractor_Binary #( .WORD_WIDTH (WORD_WIDTH) ) calc_next_count ( .add_sub (up_down), // 0/1 -> A+B/A-B .carry_in (carry_in), .A (count), .B (INCREMENT), .sum (incremented_count), .carry_out (carry_out_internal), .carries (carries_internal), .overflow (overflow_internal) );
Then calculate which value to load into the counter, and when.
reg [WORD_WIDTH-1:0] next_count = WORD_ZERO; reg load_counter = 0; reg clear_counter = 0; reg load_flags = 0; reg clear_flags = 0; always @(*) begin next_count = (load == 1'b1) ? load_count : incremented_count; load_counter = (run == 1'b1) || (load == 1'b1); clear_counter = (clear == 1'b1); load_flags = (run == 1'b1); clear_flags = (load == 1'b1) || (clear == 1'b1); end
Finally, store the next count value and flags, using a Register.
Register #( .WORD_WIDTH (WORD_WIDTH), .RESET_VALUE (INITIAL_COUNT) ) count_storage ( .clock (clock), .clock_enable (load_counter), .clear (clear_counter), .data_in (next_count), .data_out (count) ); Register #( .WORD_WIDTH (WORD_WIDTH), .RESET_VALUE (WORD_ZERO) ) carries_storage ( .clock (clock), .clock_enable (load_flags), .clear (clear_flags), .data_in (carries_internal), .data_out (carries) ); Register #( .WORD_WIDTH (1), .RESET_VALUE (1'b0) ) carry_out_storage ( .clock (clock), .clock_enable (load_flags), .clear (clear_flags), .data_in (carry_out_internal), .data_out (carry_out) ); Register #( .WORD_WIDTH (1), .RESET_VALUE (1'b0) ) overflow_storage ( .clock (clock), .clock_enable (load_flags), .clear (clear_flags), .data_in (overflow_internal), .data_out (overflow) ); endmodule