thenewguy617
thenewguy617

Reputation: 121

Reduce array to sum of elements

I am trying to reduce a vector to a sum of all it elements. Is there an easy way to do this in verilog?

Similar to the systemverilog .sum method.

Thanks

Upvotes: 4

Views: 13162

Answers (4)

Gaslight Deceive Subvert
Gaslight Deceive Subvert

Reputation: 20400

Here is a module that works for arbitrarily sized arrays and does not require extra storage:

module arrsum(input            clk,
              input            rst,
              input            go,
              output reg [7:0] cnt,
              input wire [7:0] buf_,
              input wire [7:0] n,
              output reg [7:0] sum);
   always @(posedge clk, posedge rst) begin
      if (rst) begin
         cnt <= 0;
         sum <= 0;
      end else begin
         if (cnt == 0) begin
           if (go == 1) begin
              cnt <= n;
              sum <= 0;
           end
         end else begin
            cnt <= cnt - 1;
            sum <= sum + buf_;
         end
      end
   end
endmodule
module arrsum_tb();
   localparam N = 6;
   reg        clk = 0, rst = 0, go = 0;
   wire [7:0] cnt;
   reg [7:0] buf_, n;
   wire [7:0] sum;
   reg [7:0]  arr[9:0];
   integer    i;

   arrsum dut(clk, rst, go, cnt, buf_, n, sum);
   initial begin
      $display("time clk rst sum cnt");
      $monitor("%4g   %b   %b %d    %d",
               $time, clk, rst, sum, cnt);

      arr[0] = 5;
      arr[1] = 6;
      arr[2] = 7;
      arr[3] = 10;
      arr[4] = 2;
      arr[5] = 2;

      #5 clk = !clk;
      #5 rst = 1;
      #5 rst = 0;
      #5 clk = !clk;

      go = 1;
      n = N;
      #5 clk = !clk;
      #5 clk = !clk;
      for (i = 0; i < N; i++) begin
         buf_ = arr[i];
         #5 clk = !clk;
         #5 clk = !clk;
         go = 0;
      end
      #5 clk = !clk;
      $finish;
   end
endmodule

I designed it for 8-bit numbers but it can easily be adapted for other kinds of numbers too.

Upvotes: 0

alxlr8
alxlr8

Reputation: 29

In critiquing the other answers delivered here, there are some comments to make.

The first important thing is to provide space for the sum to be accumulated. statements such as the following, in RTL, won't do that:

sum = sum + array[i]

because each of the unique nets created on the Right Hand Side (RHS) of the expression are all being assigned back to the same signal called "sum", leading to ambiguity in which of the unique nets is actually the driver (called a multiple driver hazard). To compound the problem, this statement also creates a combinational loop issue because sum is used combinationally to drive itself - not good. What would be good would be if something different could be used as the load and as the driver on each successive iteration of the loop....

Back to the argument though, in the above situation, the signal will be driven to an unknown value by most simulator tools (because: which driver should it pick? so assume none of them are right, or all of them are right - unknown!!). That is if it manages to get through the compiler at all (which is unlikely, and it doesn't at least in Cadence IEV).

The right way to do it would be to set up the following. Say you were summing bytes:

parameter NUM_BYTES = 4;
reg [7:0] array_of_bytes [NUM_BYTES-1:0];
reg [8+$clog2(NUM_BYTES):0] sum [NUM_BYTES-1:1];

always @* begin
    for (int i=1; i<NUM_BYTES; i+=1) begin
         if (i == 1) begin
             sum[i] = array_of_bytes[i] + array_of_bytes[i-1];
         end
         else begin
             sum[i] = sum[i-1] + array_of_bytes[i];
         end
    end
end

// The accumulated value is indexed at sum[NUM_BYTES-1]

Upvotes: 2

agrestu_zjadacz
agrestu_zjadacz

Reputation: 61

My combinational solution for this problem:

//example array
parameter cells = 8;
reg [7:0]array[cells-1:0] = {1,2,3,4,5,1,1,1};

//###############################################

genvar i;
wire [7:0] summation_steps [cells-2 : 0];//container for all sumation steps
generate
    assign summation_steps[0] = array[0] + array[1];//for less cost starts witch first sum (not array[0])
    for(i=0; i<cells-2; i=i+1) begin
        assign summation_steps[i+1] = summation_steps[i] + array[i+2];
    end
endgenerate
wire [7:0] result;
assign result = summation_steps[cells-2];

Upvotes: 6

Greg
Greg

Reputation: 19112

Verilog doesn't have any built-in array methods like SV. Therefore, a for-loop can be used to perform the desired functionality. Example:

parameter N = 64;
integer i;
reg [7:0] array [0:N-1]
reg [N+6:0] sum; // enough bits to handle overflow

always @*
begin
  sum = {(N+7){1'b0}}; // all zero
  for(i = 0; i < N; i=i+1)
    sum = sum + array[i];
end

Upvotes: 4

Related Questions