# How to design a module that counts logic 1s in multiple inputs in Verilog?

For example, I have two inputs, whose value can be either St1 or St0. The changes of states are synchronous to the same clock, say 1MHz of frequency. I want to design a Verilog module that counts how many St1 have appeared in a certain amount of time.

For instance, within 5ms,

input A is 1 1 0 1 1

input B is 1 0 0 0 1

My problem is that when both signals are 1, how to make sure that the counter +2.

My first thought is just using a case:

reg counter;
wire [1:0] inputs;
always @(posedge clk)
begin
if(reset)
counter <= 0;
else
begin
case(inputs)
2'b00: counter <= counter;
2'b01, 2'b10: counter <= counter + 1;
2'b11: counter <= counter + 2;
endcase
end
end


But if we have 16 or maybe 32 inputs, is there a method better than writing a very long case?

An additional question is that if these lines are synchronous to clocks with different frequencies, how should we handle the case where multiple lines want to increment 1 to the counter at the same time?

Use a for loop to add up within a cycle. Depending on the clock frequency and number of inputs, you may be able to directly increment the counter or you may have to store the intermediate sum in a register and pipeline adding that to the accumulator on the next cycle.

Handling multiple clocks is a different issue. What you could do is count in each clock domain separately, then dump the counters and synchronize the value every n (say 128) clock cycles into one clock domain where you accumulate all of these counts as they arrive.

I have a little PRBS checker module that does exactly this to count up errors at like 400 MHz, I'll have to dig it up.

Edit: Here we go:

// snip...

reg prbs_invert_reg = 1'b0, prbs_invert_next;
reg prbs_gate_en_reg = 1'b0, prbs_gate_en_next;

reg gate_sync_reg_1 = 1'b0, gate_sync_reg_2 = 1'b0, gate_sync_reg_3 = 1'b0;

always @(posedge output_clk) begin
gate_sync_reg_1 <= gate;
gate_sync_reg_2 <= gate_sync_reg_1;
gate_sync_reg_3 <= gate_sync_reg_2;
end

wire [WIDTH*4-1:0] data_err;

// lfsr_prbs_check #(
//     .LFSR_WIDTH(7),
//     .LFSR_POLY(7'h41),
//     .LFSR_INIT(7'h7f),
//     .LFSR_CONFIG("FIBONACCI_FF"),
//     .REVERSE(1),
//     .INVERT(0),
//     .DATA_WIDTH(WIDTH*4),
//     .STYLE("AUTO")
// )
// lfsr_prbs_check_inst (
//     .clk(output_clk),
//     .rst(1'b0),
//     .data_in(output_q),
//     .data_in_valid(1'b1),
//     .data_out(data_err)
// );

lfsr_prbs_check_sel #(
.REVERSE(1),
.DATA_WIDTH(WIDTH*4),
.STYLE("AUTO")
)
lfsr_prbs_check_inst (
.clk(output_clk),
.rst(1'b0),
.select(prbs_select_reg),
.invert(prbs_invert_reg),
.data_in(output_q),
.data_in_valid(1'b1),
.data_out(data_err)
);

// sum errors
// reg [$clog2(WIDTH*4)+1-1:0] cycle_error_count_reg = 0; // reg [$clog2(WIDTH*4)+1-1:0] cycle_error_count_temp = 0;

// // probably will need to pipeline this
// integer i;

// always @* begin
//     cycle_error_count_temp = 0;
//     for (i = 0; i < WIDTH*4; i = i + 1) begin
//         cycle_error_count_temp = cycle_error_count_temp + data_err[i];
//     end
// end

// always @(posedge output_clk) begin
//     cycle_error_count_reg <= cycle_error_count_temp;
// end

reg [$clog2(WIDTH*4)+1-1:0] cycle_error_count_reg = 0; reg [$clog2(WIDTH*2)+1-1:0] cycle_error_count_1_reg = 0;
reg [$clog2(WIDTH*2)+1-1:0] cycle_error_count_2_reg = 0; reg [$clog2(WIDTH*2)+1-1:0] cycle_error_count_1_temp = 0;
reg [$clog2(WIDTH*2)+1-1:0] cycle_error_count_2_temp = 0; // probably will need to pipeline this integer i; always @* begin cycle_error_count_1_temp = 0; cycle_error_count_2_temp = 0; for (i = 0; i < WIDTH*2; i = i + 1) begin cycle_error_count_1_temp = cycle_error_count_1_temp + data_err[i]; cycle_error_count_2_temp = cycle_error_count_2_temp + data_err[i+WIDTH*2]; end end always @(posedge output_clk) begin cycle_error_count_1_reg <= cycle_error_count_1_temp; cycle_error_count_2_reg <= cycle_error_count_2_temp; cycle_error_count_reg <= cycle_error_count_1_reg + cycle_error_count_2_reg; end // accumulate errors, dump every 256 cycles reg [7:0] count_reg = 8'd0; reg [8:0] word_count_acc_reg = 0; reg [8:0] word_count_reg = 0; reg [8+$clog2(WIDTH*4)+1:0] error_count_acc_reg = 0;
reg [8+\$clog2(WIDTH*4)+1:0] error_count_reg = 0;
reg error_count_flag_reg = 0;

always @(posedge output_clk) begin
if (count_reg == 8'd255) begin
count_reg <= 8'd0;
word_count_acc_reg <= 0;
error_count_acc_reg <= 0;
if (!prbs_gate_en_reg || gate_sync_reg_3) begin
word_count_acc_reg <= 1;
error_count_acc_reg <= cycle_error_count_reg;
end
word_count_reg <= word_count_acc_reg;
error_count_reg <= error_count_acc_reg;
error_count_flag_reg <= ~error_count_flag_reg;
end else begin
count_reg <= count_reg + 1;
if (!prbs_gate_en_reg || gate_sync_reg_3) begin
word_count_acc_reg <= word_count_acc_reg + 1;
error_count_acc_reg <= error_count_acc_reg + cycle_error_count_reg;
end
end
end

// synchronize dumped counts to control clock domain
reg flag_sync_reg_1 = 1'b0;
reg flag_sync_reg_2 = 1'b0;
reg flag_sync_reg_3 = 1'b0;

always @(posedge clk) begin
flag_sync_reg_1 <= error_count_flag_reg;
flag_sync_reg_2 <= flag_sync_reg_1;
flag_sync_reg_3 <= flag_sync_reg_2;
end

// snip...

always @* begin

// snip...

cycle_count_next = cycle_count_reg + 1;
if (cycle_count_reg[31] && ~cycle_count_next[31]) begin
cycle_count_next = 32'hffffffff;
end

update_count_next = update_count_reg;
prbs_word_count_next = prbs_word_count_reg;
prbs_error_count_next = prbs_error_count_reg;
if (flag_sync_reg_2 ^ flag_sync_reg_3) begin
update_count_next = update_count_reg + 1;
prbs_word_count_next = word_count_reg + prbs_word_count_reg;
prbs_error_count_next = error_count_reg + prbs_error_count_reg;

// saturate
if (update_count_reg[31] && ~update_count_next[31]) begin
update_count_next = 32'hffffffff;
end
if (prbs_word_count_reg[31] && ~prbs_word_count_next[31]) begin
prbs_word_count_next = 32'hffffffff;
end
if (prbs_error_count_reg[31] && ~prbs_error_count_next[31]) begin
prbs_error_count_next = 32'hffffffff;
end
end

// snip...

end

// snip...

always @(posedge clk) begin
if (rst) begin
// snip...
cycle_count_reg <= 32'd0;
update_count_reg <= 32'd0;
prbs_word_count_reg <= 32'd0;
// snip...
prbs_error_count_reg <= 32'd0;
prbs_select_reg <= 2'b00;
prbs_invert_reg <= 1'b0;
prbs_gate_en_reg <= 1'b0;
// snip...
end else begin
// snip...
cycle_count_reg <= cycle_count_next;
update_count_reg <= update_count_next;
prbs_word_count_reg <= prbs_word_count_next;
// snip...
prbs_error_count_reg <= prbs_error_count_next;
prbs_select_reg <= prbs_select_next;
prbs_invert_reg <= prbs_invert_next;
prbs_gate_en_reg <= prbs_gate_en_next;
// snip...
end

// snip...
end

// snip...


Where this was used, WIDTH was set to 16, so it's doing a PRBS check and error count accumulation of a 64 bit input. And it was running at around 20 Gbps, so output_clk was around 300 MHz. However, it passed timing analysis with a clock constraint of closer to 400 MHz (assuming a data rate of 25 Gbps). The internal clock clk was 125 MHz. The target was a Virtex Ultrascale FPGA. Readout and clearing of the final accumulators is not shown, but the counters are set up to saturate at 0xffffffff if they do not get read out and cleared before overflowing. Clock domain crossing is a simple one-way pulse synchronizer (no two-way handshake) with a copy of the count being directly read from the internal clock domain. This might not be the most robust way to do this, but is simple and it worked fine for the application.