0
\$\begingroup\$

I have a requirement to make a verilog module that takes a Gray code integer i and returns the Gray coded integer i-1 using combinatorial logic only. When I look up examples of Gray decoding, for instance, a lot of them use sequential logic. I also need it to work on integers of size >20 bits.

The solution I came up with is to write a Python script that generates a verilog file for the appropriate bit width. I first tried using a big case statement in an always @(*) block as in this 3-bit example:

module gray_decrement(input [2:0] x, output [2:0] xm1);

always @(*) begin
    case( x )
        3'b001: xm1 = 3'b000;
        3'b011: xm1 = 3'b001;
        3'b010: xm1 = 3'b011;
        3'b110: xm1 = 3'b010;
        3'b111: xm1 = 3'b110;
        3'b101: xm1 = 3'b111;
        3'b100: xm1 = 3'b101;
        default: xm1 = 0;
    endcase
end
endmodule

That doesn't compile with an Error (10137): Verilog HDL Procedural Assignment error at graycode.v(1051): object "xm1" on left-hand side of assignment must have a variable data type. If I make xm1 an output reg it doesn't synthesize how I need it to. So I went back and redid it as a really big assign statement using the ? operator as in this 3-bit example:

module gray_decrement(input [2:0] x, output [2:0] xm1);

assign xm1 = x == 3'b001 ? 3'b000 :
    x == 3'b011 ? 3'b001 :
    x == 3'b010 ? 3'b011 :
    x == 3'b110 ? 3'b010 :
    x == 3'b111 ? 3'b110 :
    x == 3'b101 ? 3'b111 :
    x == 3'b100 ? 3'b101 :
    3'd0;

endmodule

This works great and synthesizes properly, but after 10 bits (i.e. 1024 elements in my nested ? operation), Quartus errors out with parser stack overflow error.

So the question is what is the best way to extend my assign statement so that it could have ~2^20 lines in it? Is there some generate magic that I could use since the arithmetic is dirt simple? Or will I have to do some of the logic simplification in my Python preprocessor?

\$\endgroup\$
2
  • \$\begingroup\$ In VHDL you'd simply declare a constant array and initialise it in the declaration. Surely Verilog would let you do the same? \$\endgroup\$ Jan 6 at 23:04
  • \$\begingroup\$ Your first example errors because you must use a variable data type on the left hand side in a procedural block - i.e. xml must be a variable (reg,integer,etc.), not a net (wire). Outputs default to being wire unless otherwise specified. \$\endgroup\$ Jan 6 at 23:31
1
\$\begingroup\$

Your first example errors because you must use a variable data type on the left hand side in a procedural block.

xml is the left hand side of the assignment, and so must be a variable (reg,integer,etc.), not a net (wire). Outputs default to being wire unless otherwise specified. You can fix this with an output reg [2:0] xml in your port declaration list.

Alternatively, given you are basically describing a ROM, you can use an initial block to set the default values for your lookup table:

localparam SIZE = 1024;
reg [2:0] rom [SIZE-1:0];

initial begin
    $readmemb("rominit.txt", rom);
end

assign xm1 = rom[x];

Alternatively, given you are using Quartus, you could use a MIF file:

localparam SIZE = 1024;
(* ram_init_file = "rominit.mif" *) reg [2:0] rom [SIZE-1:0];
assign xm1 = rom[x];

Finally, and perhaps more simply, your mapping can easily be described algorithmically:

module gray_decrement #(
    parameter WIDTH = 3
)(
    input  [WIDTH-1:0] x, 
    output [WIDTH-1:0] xm1
);

genvar i;
generate

// First convert to binary
wire [WIDTH-1:0] bin;
assign bin[WIDTH-1] = x[WIDTH-1];
for (i = 1; i < WIDTH; i = i + 1) begin : gray2bin
    localparam idx = WIDTH-1-i;
    assign bin[idx] = x[idx] ^ bin[idx+1];
end

// Then subtract one
wire [WIDTH-1:0] binm1;
assign binm1 = bin - {{{WIDTH-1}{1'b0}},1'b1};

// Then convert back to gray code
assign xm1[WIDTH-1] = binm1[WIDTH-1];
for (i = 1; i < WIDTH; i = i + 1) begin : bin2gray
    localparam idx = WIDTH-1-i;
    assign xm1[idx] = binm1[idx] ^ binm1[idx+1];
end

endgenerate

endmodule

The above should optimise away to a lookup table which should decrement the gray code value.

(As a side note, your lookup table is wrong - the default value should be 4 not 0, otherwise a value of 0 maps to 0, and nothing maps to 4).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks, the generate-based code was the solution I was looking for. \$\endgroup\$
    – Andrew
    Jan 7 at 18:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.