Ross MacArthur
Ross MacArthur

Reputation: 5449

Parameterizing an incomplete case statement in Verilog

I am trying to parameterize some code I have written. The non parameterized version below is written for WIDTH = 4. I have a reg digits that cycles a zero. Based on where this zero is I want to output a particular vector to segments from my array SS (I assign values to it elsewhere).

reg [6:0] SS [3:0];    

always @(posedge clk)
    if (reset) digits <= 4'b1110;
    else       digits <= { digits[2:0], digits[3] };

always @(*)
    if (reset) segments <= ~7'h0;
    else case(~digits)
        4'b1    : segments[6:0] <= ~SS[0];
        4'b10   : segments[6:0] <= ~SS[1];
        4'b100  : segments[6:0] <= ~SS[2];
        4'b1000 : segments[6:0] <= ~SS[3];
        default : segments[6:0] <=  7'h0;
    endcase

How can I parameterize this case statement for a different width of digits? I've tried using a for loop instead of a case statement like this:

for (genvar i = 0; i < WIDTH; i = i+1) begin
    if (~digits[i]) segments[6:0] <= ~SS[i];
end

But it won't synthesize because it says segments has multiple drivers and if I do it in a different way like below it says digits is not constant so it can't synthesize:

for (genvar i = 0; i < WIDTH; i = i+1) begin
    if (~digits == (1 << i)) segments[6:0] <= ~SS[i];
end

How can I do this? I have thought of taking the log base 2 of ~digits and checking if that is equal to i within the loop but I'm not sure how to do that in hardware. Is there a better way?

Upvotes: 1

Views: 495

Answers (1)

nguthrie
nguthrie

Reputation: 2685

You can do a loop and take advantage of the "last assignment wins". See below. I used some SystemVerilog syntax here, but you could do it without it.

PARAMETER SS_SIZE = 4
logic [6:0] SS [SS_SIZE];
logic [SS_SIZE-1:0] digits;

always_ff @(posedge clk or posedge reset)
    if (reset) digits <= {{(SS_SIZE-1){1'b1}},1'b0};
    else       digits <= { digits[SS_SIZEE-2:0], digits[SS_SIZE-1] };

always_comb begin
    segments = '0;
    for(int i=0; i<SS_SIZE; i++) begin
       if(digits[i]==1'b0) begin
          segments = ~SS[i];
       end
    end
 end

I would go further and replace the shift register with a binary counter:

PARAMETER SS_SIZE = 4
logic [6:0] SS [SS_SIZE];
logic [$clog2(SS_SIZE)-1:0] count;

always_ff @(posedge clk or posedge reset)
    if (reset) count <= '0;
    else if(count == (SS_SIZE-1)) count <= '0
    else       count <= count+1;

assign segments = ~SS[count];

Upvotes: 1

Related Questions