Source

License

Index

A Binary Up/Down Counter

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

Back to FPGA Design Elements

fpgacpu.ca