Why doesn't 'd0 extend the full width of the signal (as '0 does)? - system-verilog

Using SystemVerilog and Modelsim SE 2020.1, I was surprised to see a behavior:
bus_address is a 64b signal input logic [63:0] bus_address
Using '0
.bus_address ('0),
Using 'd0
.bus_address ('d0),
Riviera-Pro 2020.04 (too buggy, we gave up using it and we are in a dispute with Aldec)
'd0:
'0:
Investigation/Answer:
11.3.3 Using integer literals in expressions: An unsized, based integer (e.g., 'd12 , 'sd12 )
5.7.1 Integer literal constants:
The number of bits that make up an unsized number (which is a simple
decimal number or a number with a base specifier but no size
specification) shall be at least 32. Unsized unsigned literal
constants where the high-order bit is unknown ( X or x ) or
three-state ( Z or z ) shall be extended to the size of the expression
containing the literal constant.
That was tricky and I thought it would set 0 all the other bits as '0 does.
I hope specs' authors will think more when defining such non-sense behaviors.

This problem has more with port connections with mismatched sizes than anything to do with numeric literals. It's just that the issue does not present itself when using the fill literals. This is because the fill literal automatically sizes itself eliminating port width mismatch.
The problem you see exists whether you use literals or other signals like in this example:
module top;
wire [31:0] a = 0;
dut d(a);
endmodule
module dut(input wire [63:0] p1);
initial $strobeb(p1);
endmodule
According to section 23.3.3.7 Port connections with dissimilar net types (net and port collapsing), the nets a and p1 might get merged into a single 64-bit net, but only the lower 32-bits remain driven, or 64'hzzzzzzzz00000000.
If you change the port connection to a sized literal, dut d(32'b0);, you see the same behavior 64'hzzzzzzzz00000000.
Now let's get back to the unsized numeric literal 'd0. Unsized is a misnomer—all numbers have a size. It's just that the size is implicit and never the size you want it to be. 😮 How many people write {'b1,'b0,'b1,'b0} thinking they've just wrote the same thing as 4'b1010? This is actually illegal in the LRM, but some tools silently interpret it as {32'b1,32'b0,32'b1,32'b0}.
Just never use an unsized literal.

Related

Modbus MSW is always 0

I have a energy meter EM340 with the following modbus spec:
Modicom address
Physical address
Length (words)
VARIABLE ENG. UNIT
Data
Format
Notes
301025
0400h
2
kWh (+) TOT – INTeger part
INT32
Value=INT(kWh)*1
Example: if kWh=1234.567, value=1234
301027
0402h
2
kWh (+) TOT – DECimal part
INT32
Value=DEC(kWh)*1000
Example: if kWh=1234.567, value=567 (Note: the MSW is always 0)
What does this mean: the MSW is always 0 ?
Anything in minimalmodbus I need to be aware of when reading 0402h?
The relevant manual for the device is available here. Page 7 of this states:
For all the formats the byte order (inside the single word) is MSB->LSB. In INT32, UINT32 and UINT64 formats, the word order is LSW-> MSW
So "MSW" = "Most Significant Word". A Modbus holding/input register is 16 bits (or one word); to hold larger values (e.g. INT32) the value is spread across multiple registers. The Modbus spec does not provide any guidance as to how this should be done so it's device specific. The above tells you that, for this device, the least significant word will be stored in the lowest register. For example 0xFFFF0000 could be stored as reg1 = 0x0000 and reg2 = 0xFFFF.
Note: the MSW is always 0
"kWh (+) TOT – INTeger part" is an INT32 which is two 16-bit words. As per the above this will be stored over two registers (LSW = reg 0x402 and MSW = reg 0x403). The comment indicates that the MSW (register 0x403) will always be 0 and can, potentially, be ignored (indicating that the data is actually a UINT16).
One element of this that I am uncertain of is that the spec says this is an INT32, so a signed value. Generally this would mean that the highest bit is a sign bit (set if the value is negative). However as the MSB is always 0 the full value cannot be negative. This makes sense because this is the decimal portion of a larger number (reg 0x400/0x401) so the sign bit will be in that value.
DECimal part
Value=DEC(kWh)*1000
This appears to confirm the above. As this value is the decimal portion multiplied by 1000 the valid values will be 0-999 (because 1000/1000 = 1 which is not part of the decimal part). So all potential values fit within an INT16.
Note: The above is my interpretation of the document; I've never used one of these units! (and may have misread).

How would you write a SystemVerilog Function that adds an even parity bit to a 7-bit vector?

Would you take a 7-bit logic variable as an input and return an 8-bit logic output with the parity bit being the MSB (leftmost bit) of the output?
In a system that uses even parity you want the total number of 1 bits in the output vector to be even, right?
Can the the unary ^ operator can be used to calculate even parity?
Trying to figure out what that would look like?
I started something like this:
Code for parity
Sorry if this is more than one question.
I would do this like this:
input logic [N-1:0] data_in;
output logic [N:0] data_out;
assign data_out = {^data_in, data_in};
The ^ reduction operator does an XOR operation on all the bits in the operand. (So, it will return '1 if data_in has an odd number of ones.)

The best way to convert a real valued number to an integer value larger than 32 bits?

Are there any system functions that support more than 32 bits in System Verilog? I want to convert a real-valued quantity to an integral value that contains more than 32 bits. The $rtoi() system function does precisely what I want for values that can be represented in 32 bits or less. Is there a built-in for this, or would I need to write my own?
For a concrete example, I would like to be able to do something like the following:
logic [41:0] test_value;
initial begin
test_value = $rtoi($pow(2.0, 39.5));
end
Where, instead of $rtoi(), I would use the unknown sought after system function. Given the correct function, I would expect this to result in test_value being initialized with the bit sequence 42'b1011010100000100111100110011001111111001 or possibly 42'b1011010100000100111100110011001111111010 if rounding is supported.
I can write my own function, but I would like to avoid reinventing the wheel unless there is no wheel.
A implicit cast from real to integral gives you what you want with rounding
test_value = 2.0**39.5;
Or you can use an explicit cast
typedef logic [41:0] uint42_t;
test_value = uint42_t'(2.0**39.5);

Function of $clog2(N) in Mojo IDE

I am a beginner in this, but I was wondering what exactly is the function of $clog2(N) in general? Some websites say that it is the number of address bits needed for a memory of size N and not the number of bits needed to express the value N. What does that mean?
IEEE Std 1800-2012 § 20.8.1 Integer math functions
The system function $clog2 shall return the ceiling of the log base 2 of the argument (the log rounded up to an integer value). The argument can be an integer or an arbitrary sized vector value. The argument shall be treated as an unsigned value, and an argument value of 0 shall produce a result of 0.
This system function can be used to compute the minimum address width necessary to address a memory of a given size or the minimum vector width necessary to represent a given number of states.
For example:
integer result;
result = $clog2(n);

fortran90 reading array with real numbers

I have a list of real data in a file. The real data looks like this..
25.935
25.550
24.274
29.936
23.122
27.360
28.154
24.320
28.613
27.601
29.948
29.367
I write fortran90 code to read this data into an array as below:
PROGRAM autocorr
implicit none
INTEGER, PARAMETER :: TRUN=4000,TCOR=1800
real,dimension(TRUN) :: angle
real :: temp, temp2, average1, average2
integer :: i, j, p, q, k, count1, t, count2
REAL, DIMENSION(0:TCOR) :: ACF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
open(100, file="fort.64",status="old")
do k = 1,TRUN
read(100,*) angle(k)
end do
Then, when I print again to see the values, I get
25.934999
25.549999
24.274000
29.936001
23.122000
27.360001
28.153999
24.320000
28.613001
27.601000
29.948000
29.367001
32.122002
33.818001
21.837000
29.283001
26.489000
24.010000
27.698000
30.799999
36.157001
29.034000
34.700001
26.058001
29.114000
24.177000
25.209000
25.820999
26.620001
29.761000
May I know why the values are now 6 decimal points?
How to avoid this effect so that it doesn't affect the calculation results?
Appreciate any help.
Thanks
You don't show the statement you use to write the values out again. I suspect, therefore, that you've used Fortran's list-directed output, something like this
write(output_unit,*) angle(k)
If you have done this you have surrendered the control of how many digits the program displays to the compiler. That's what the use of * in place of an explicit format means, the standard says that the compiler can use any reasonable representation of the number.
What you are seeing, therefore, is your numbers displayed with 8 sf which is about what single-precision floating-point numbers provide. If you wanted to display the numbers with only 3 digits after the decimal point you could write
write(output_unit,'(f8.3)') angle(k)
or some variation thereof.
You've declared angle to be of type real; unless you've overwritten the default with a compiler flag, this means that you are using single-precision IEEE754 floating-point numbers (on anything other than an exotic computer). Bear in mind too that most real (in the mathematical sense) numbers do not have an exact representation in floating-point and that the single-precision decimal approximation to the exact number 25.935 is likely to be 25.934999; the other numbers you print seem to be the floating-point approximations to the numbers your program reads.
If you really want to compute your results with a lower precision, then you are going to have to employ some clever programming techniques.