Using don't-care in enumeration typedef in synthesizable SystemVerilog - system-verilog

I have the following code:
typedef enum logic [1:0] {
CMD1 = 2'b1?,
CMD2 = 2'b01,
CMD3 = 2'b00
} cmd_t;
Basically, if the MSB is 1, it's CMD1 (I'll use the LSB for part of the index). And if the MSB is 0, then decode the rest of bits for the command.
I then try to decode using an always_comb:
cmd_t myCmd;
always_comb begin
casez(myCmd)
CMD1: isCmd1 = 1'b1;
CMD2: isCmd1 = 1'b0;
default: isCmd1 = 1'b0;
endcase
end
Unfortunately, I get this message from Spyglass:
[12EE] W467 Based number 2'b1? contains a don't-care (?) - might lead to simulation/synthesis mismatch
This code should be synthesizable, no? Can this Spyglass warning be waived safely?

I doubt this will synthesise correctly. I think the Spyglass message is misleading. In Verilog (and SystemVerilog) ? means exactly the same as z. You are specifying an enum with 4-state base type values, where CMD1 is to be represented exactly by 2'b1z.

Related

32-bit vs. 4-bit in enum declaration

When I change the enumerated type variables from 4 bit to 32 bit, my error is appeased. I am wondering why I cannot keep it at 4 bit in this code.
Here are some pertinent snippets; I have deleted code related to non-pertinent variables:
Testbench:
module ALUtestbench;
//Variable Declaration
typedef enum {ADD = 32'b00, SUB = 32'b01, INV = 32'b10, RED = 32'b11} opcode_t;
opcode_t opcode; //declare typed variable
//Module Instance
alu alu_inst(
.opcode(opcode));
initial begin
opcode = opcode.first();
#10;
do
begin
$display(opcode);
$display("For opcode %s the result is: %0h", opcode.name, result);
opcode = opcode.next;
#10;
end
while (opcode != opcode.first);
end
endmodule
Design:
module ALU;
input reg A [4:0];
inout reg B [4:0];
output reg C [4:0];
initial begin
always # (*)
begin
case(opcode)
ADD : C = A + B;
SUB : C = A - B;
INV : C = ~A;
endcase
end
endmodule
At first, I had
typedef enum {ADD = 4'b00, SUB = 4'b01, INV = 4'b10, RED = 4'b11} opcode_t;
opcode_t opcode; //declare typed variable
and the compiler gave me the error:
SystemVerilog requires the width of a sized constant in this context
to match the width of the enumeration type.
I then changed to 32-bit, and the code now does not have this error. I am wondering why I needed to do that. Does the case statement reject anything less than 32-bit?
From IEEE Std 1800-2017, section 6.19 Enumerations:
In the absence of a data type declaration, the default data type shall
be int. Any other data type used with enumerated types shall require
an explicit data type declaration.
Since int is 32-bit, you do not get an error when your constants are 32-bit.
If you want to use 4-bit constants, you need to explicitly declare your enum as 4-bit. Change:
typedef enum {ADD = 4'b00, SUB = 4'b01, INV = 4'b10, RED = 4'b11} opcode_t;
to:
typedef enum bit [3:0] {ADD = 4'b00, SUB = 4'b01, INV = 4'b10, RED = 4'b11} opcode_t;
This has nothing to do with the case statement.
If you do not explicitly declare a base type for an enumeration, the implicit datatype is int, which has a 32-bit width. Earlier versions of the SystemVerilog LRM also allowed you to use the label assignments form sized literals (i.e. ADD = 32'b00) to define the width explicitly instead of an explicit base type. But now the LRM only allows explicit base types. But it still has this rule in the IEEE 1800-2017 SystemVerilog LRM, section 6.19 Enumerations
If the integer value expression is a sized literal constant, it shall be an error if the size is
different from the enum base type, even if the value is within the representable range.
So either drop the size for the literals
typedef enum {ADD = 'b00, SUB = 'b01, INV = 'b10, RED = 'b11} opcode_t;
or write it as
typedef enum bit [3:0] {ADD = 4'b00, SUB = 4'b01, INV = 4'b10, RED = 4'b11} opcode_t;

How to get rid of illegal operand error in always_comb block when used "==" operator in SystemVerilog

I keep getting the error
illegal operand for operator "==" at the code block below
module ALU(input logic oppCode[2:0], input logic [3:0] rf1, input logic [3:0] rf2
,output logic [3:0] result, output logic EQ );
always_comb begin
if(oppCode == 3'b010) begin // Problem
result <= rf1 - rf2;
end
else if(oppCode == 3'b101) begin // Problem
result <= rf1 + rf2;
end
else if(oppCode == 3'b111) begin // Problem
EQ <= (rf1 == rf2);
end
end
endmodule
What I want to do here, is comparing the oppCode with hard coded instruction types. But I keep getting the Illegal operand for "==" error.
"==" is logical comparison operator so why it is not comparing?
How can I get rid of this?
In your code, oppCode is unpacked (logic oppCode[2:0]).
You can't compare an unpacked value with a packed value (3'b010).
Either make oppCode packed logic [2:0] oppCode or add an intermediate signal to convert the unpacked oppCode to a packed version with an always_comb and a for loop or just manually.
logic [2:0] oppCode_packed;
always_comb begin
for (int i=0; i<3; i++) begin
oppCode_packed[i] = oppCode[i];
end
end
and then use oppCode_packed in your comparisons.
Piece of advice: when you declare an unpacked signal, use the number of bits/elements you want: logic oppCode[3]. That's more lisible and you'll be less likely to confuse them.

Is there a way to cast an SystemVerilog assignment pattern into a packed struct?

Per the System Verilog LRM "Assignment pattern format", a data structure can be printed into a string as follows:
module top;
typedef enum {ON, OFF} switch_e;
typedef struct {switch_e sw; string s;} pair_t;
pair_t va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}};
initial begin
$display("va[int] = %p;",va);
$display("va[int] = %0p;",va);
$display("va[10].s = %p;", va[10].s);
end
endmodule : top
This example may print:
va[int] = '{10:'{sw:OFF, s:"switch10"}, 20:'{sw:ON, s:"switch20"}} ;
va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}} ;
va[10].s = "switch10";
Is there a way to do the reverse? What I'd like to do is to take an assignment pattern string as a plusarg or a line read from a file, and assign that to a variable at run time, e.g.:
string assign_pattern = "'{10:'{sw:OFF, s:"switch10"}, 20:'{sw:ON, s:"switch20"}}";
$cast(va, assign_pattern); // ** This doesn't work **
If not generally possible, is there a way to do that specifically for packed struct types?
You can't do the reverse. SystemVerilog was designed as a compiled languageā€”there's no parser available at run-time. You would have to create a parser in SystemVerilog or C smart enough to decode the assignment patterns you expect to read in.
Another option is converting the file of assignment patterns into code that could be compiled in with the rest of your code.
Another option based on your comments
You can use a bit-stream or streaming operator to parse a bit-string into a struct. The struct does not need to be packed, it just needs to be made up from fixed-sized, integral values.
module top;
typedef enum bit [1:0] {ON, OFF, INBETWEEN} switch_e;
typedef struct {switch_e sw; bit [8*8:1] s; bit [5:0] value;} trio_s; // 72 bits
typedef bit [71:0] uint72_t;
trio_s v,x;
uint72_t l;
initial begin
x = '{sw:OFF, s:"switch10", value:'h0a};
l = uint72_t'(x);
$displayh(l);
v = trio_s'(l);
$displayh("v = %p",v);
$display("v.s = %s",v.s);
end
endmodule
This displays
# 5cddda5d18da0c4c0a
# v = '{sw:OFF, s:8320234785195176240, value:10}
# v.s = switch10
pair_t va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}};
is the same as
pair_t va[int]
initial begin
va[10].sw = OFF;
va[10].s = "switch10";
..
Saying that, you can write your own parser of a +arg string (or strings) which will assign values to the array fields in a task. This is the only possibility. For exmple:
string indx = "1";
string sw = "off";
initial begin
int i = indx.atoi();
va[i].sw = sw == "off" ? OFF : ON;
...

How to pass signal name through $value$plusargs in system verilog

I am writing assertions in system verilog. This assertion check for a signal "lock" which is passed as an test argument through switch $value$plusargs as GPIO_SEL="test_bench.gpio"
So my code is :
module ab
string abc;
$value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always #*
begin
lock = abc;
end
endmodule
The problem here is that signal lock is not getting value as test_bench.gpio. Is there any way i can pass this signal value from testplaus args
SystemVerilog is a compiled language, not interpreted. You can't access identifiers directly using a string. You can use compiler directives on the command line
module ab;
bit lock;
always_comb
begin
lock = `GPIO_SEL;
end
endmodule
Then when compiling your code use a +define switch
vlog your_file.sv +define+GPIO_SEL=test_bench.gpio
If you think re-compiling your testbench/dut is a significant burden (most tools offer incremental compilation options), and you have a fixed number of paths to access, then you can use a case statement
module ab
string abc;
initial $value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always_comb
case (abc)
"test_bench.gpio": lock = test_bench.gpio;
"test_bench.gpio1": lock = test_bench.gpio1;
endcase
end
And you could just use a simple number instead of a string to select.
Looks like you need to conver the string to an integer type. Look at the string conversion functions: atoi, atobin, ...
for example
module parg;
string abc;
bit [3:0] sig;
initial begin
$value$plusargs("GPIO_SEL=%s" , abc);
sig = abc.atoi();
$display("sig = %b", sig);
end
endmodule // parg

How to assign an unpacked array of real?

While updating Modelsim from 10.3c to 10.6a, I encountered an error on this piece of code that used to not work without warning:
module test(
input bit clk,
input bit signed[31:0] data
);
real rdata_dl[19:0] = '{20{0}};
real rdata = 0;
always #(posedge clk) begin
rdata_dl = {rdata_dl[18:0], rdata};
rdata = data;
end
endmodule
-- Compiling module test
** Note: test.sv(10): (vlog-13177) Promoting concatenation '{rdata_dl[18:0],rdata}' to an assignment pattern: Assigning to a real/wreal array.
** Error (suppressible): (vlog-13215) test.sv(10): Assignment pattern element 'rdata_dl[18:0]': Cannot assign an unpacked type 'real $[18:0]' to a packed type 'real'.
** Error (suppressible): test.sv(10): (vlog-13174) Illegal assignment pattern. The number of elements (2) doesn't match with the type's width (20).
I managed to fix it by using this line instead: rdata_dl = {rdata_dl[18:0], real'(rdata)};.
However, I fail to understand why it failed and why the new version would work. Can anyone explain?
not sure what you are trying to do with this code. real type is 64-bit. When concatenating it with 19-bits, you're getting a 83-bit bus, where the rdata is in the LSBs.
Now, when assigning this 83-bit vector to a 20-bit bus, it will take the 20 LSBs, meaning that it is equivalent to writing the following assignment:
always #(posedge clk) begin
rdata_dl = rdata[19:0];
rdata = data;
end