Arithmetic right shift on logic (2 dimensional) signal - system-verilog

I am trying to do a Shift Right, Arithmetic (keep sign) on the signal in.
When I set the value in[0] to 16'hbb00, I expect in_sign_extend[0] to be 16'hf760 after it is signed right shifted. But, I notice that the actual result I see on in_sign_extend[0] is 16'h0680.
localparam CHANNELS = 8;
localparam AXI_M_DATA_WIDTH = 32;
logic signed [0:CHANNELS-1] [AXI_M_DATA_WIDTH/2-1:0] in;
logic signed [0:CHANNELS-1] [AXI_M_DATA_WIDTH/2-1:0] in_sign_extend;
assign in_sign_extend[0] = (in[0] >>> 3);
I am trying to understand if the in is actually correctly signed. Or am I missing something here.

The part select of a packed array is always unsigned, even when selecting the entire array. Only the variable as a whole reference (in) is signed. You can change your code to
assign in_sign_extend[0] = (signed'(in[0]) >>> 3);
Or you might prefer to use an unpacked array
logic signed [AXI_M_DATA_WIDTH/2-1:0] in[CHANNELS];
logic signed [AXI_M_DATA_WIDTH/2-1:0] in_sign_extend[CHANNELS];
assign in_sign_extend[0] = (in[0] >>> 3);

Related

Systemverilog expansion rule

When I review some codes, I found something strange.
It seems that it comes from expansion and operation priority.
(I know that because "sig" is declared with 'signed', $signed is not necessary and '-sig' is correct one, anyway..)
reg signed [9:0] sig;
reg signed [11:0] out;
initial
begin
$monitor ("%0t] sig=%0d, out=%0h", $time, sig, out);
sig = 64;
out = $signed(-sig);
#1
out = -$signed(sig);
#1
sig = -512;
out = $signed(-sig);
#1
out = -$signed(sig);
#1
$finish;
end
Simulation result for above codes is,
0] sig=64, out=-64
2] sig=-512, out=-512
3] sig=-512, out=512
When sig=-512, I expected that 10 bits sig would be expanded to 12bits before negation, but it was expanded after negation.
Therefore negation of -512 was still -512, and after expansion, it had a -512.
I guess "$signed() blocks expansion..Any idea what happens??
First of all, -512 and 512 are identical numbers in 10-bit represenntation. 10 bits can actually only hold signed values from -512 to 511. In this scheme negation of -512 should work weirdly, not mentioned in lrm, at least i was not able to locate anything related. This is probably an undefined behavior.
However, it is logical to assume that in this scheme in order to represent a negated value of '-512' just removing signess is sufficient. It seems that all commercial compilers in eda playground do this. So, a result of the unaray - operator in this case will be unsigned value of 512.
So, in out = $signed(-(-512)) the negation operator returns an unsigned value of 512 and it gets converted to a signed by the system task. Therefore, it gets sign extended in out.
out = -$signed(-512) for the same reason the outermost negation operator returns an unsigned value of 512. No sign extension happens here.
You can again make it signed by enclosing in yet another $signed as out = $signed(-$signed(-512))

Long Integer signed Fixed Point to Real convertion SystemVerilog

I need to convert a Long number as Fixed point into a Double rappresentation.
The fixed point math is used into the synthesis process and the Real data type only for validation and simulation.
If I make multiple convertion in chain with multiple datatypes to adjust the format then it is not enough or completely wrong .
In my case with a fixed point mantissa of 44 bit I have 3bit integer+sign bit. Q notation like "sfix_44_48"
As example I am doing this to convert a fixed point integer number into a Real value(getting the number 0.5f ):
logic signed [47:0] fp_number = 48'h0800_0000_0000; // it should be 0.5f
real r_val;
real rr_val;
real rrr_val;
real tmp;
initial
begin
r_val = $itor(fp_number)/(2**44); // doesn't solve the problem.
rr_val = real'{fp_number}/(2**44); // doesn't solve the problem.
$cast(tmp,fp_number>>>44); // doesn't solve the problem
rrr_val = tmp;
end
$itor(...) is limited to 32bit integer part.
As result of above I get zero or NaN, on Modelsim simulation.
No luck during all these convertions.
the SV LRM doesn't seem to have a clear way to do this convertion.
What is the SV workaround to allow simulations to analize data greater than 32bit size easily? please.
C.
You want to use
rr_val = real'(fp_number)/(2.0**44);
Do not use any of the $TtoT functions from Verilog. They have fixed datatype inputs and outputs.
2**44 gets computed as a 32-bit 2-complement value and overflows, giving you 0. You can use 2.0 or real'(2) instead.
thanks to #dave_59 I post this piece of code which show the mess with the convertion.
logic signed [47:0] fp_number = 48'h0800_0000_0000; // it should be 0.5f
logic signed [31:0] fp_number2 = 32'h0800_0000; // it should be 0.5f
real r_val;
real rr_val;
real rrr_val;
real rrrr_val;
initial
begin
$display("48bit fp convertion sfix_44_48");
r_val = real'{fp_number}/(2**44); // doesn't solve the problem (curly braces valid sintax but wrong convertion + wrong convertion on the denominator).
rr_val = real'{fp_number}/(2.0**44); // doesn't solve the problem (curly braces valid sintax but wrong convertion + denominator convertion OK).
rrr_val = real'(fp_number)/(2**44); // doesn't solve the problem (numerator OK + the power operation is not properly converted to a Real number as result).
rrrr_val = real'(fp_number)/(2.0**44); // solve the problem with long integer fixed points convertion (the braces are not curly anymore).
$display("r_val[%08f]",r_val,", rr_val[%08f]",rr_val,", rrr_val[%08f]",rrr_val,", rrrr_val[%08f]",rrrr_val); // it should be 0.5 on the fourth data
$display("32bit fp convertion sfix_28_32");
r_val = real'{fp_number2}/(2**28); // result totally different than previous 48bit operation, doesn't solve the problem (curly braces valid sintax but wrong convertion + wrong convertion on the denominator).
rr_val = real'{fp_number2}/(2.0**28); // doesn't solve the problem (curly braces valid sintax but wrong convertion + denominator convertion OK).
rrr_val = real'(fp_number2)/(2**28); // with a 32bit range it apparently solve the problem (numerator OK + the power operation is OK with this range).
rrrr_val = real'(fp_number2)/(2.0**28); // solve the problem with long integer fixed points convertion (the braces are not curly anymore).
$display("r_val[%08f]",r_val,", rr_val[%08f]",rr_val,", rrr_val[%08f]",rrr_val,", rrrr_val[%08f]",rrrr_val); // it should be 0.5 on the fourth data
end
the only valid convertion at 48bit is the fourth case.
For 32bit the third case is valid and also the fourth case.
First: the classic pow(...) operation must be done with this syntax (2.0**BIT) which will create a Real division and not a integer division using (2**BIT) when a scaling fixed point will be applied.
In this case the operation above is managed as float/double(C style) or real(SystemVerilog)
* Second: the real'() cast operation MUST be used with NO curly braces.
I didn't have a Linting tool to proper check the syntax so I would expect a Warning due to the validity of the operation with the curly braces.
Third: the subdle results are ok if the INTEGER denominator is limited at 32bit operations.
As results shown below:
SIM START.
# 48bit fp convertion sfix_44_48
# ** Error (suppressible): (vsim-8604) ./blocks/sim/test_tb.sv(141): NaN (not a number) resulted from a division operation.
# ** Error (suppressible): (vsim-8630) Infinity results from division operation.
# Time: 0 ps Iteration: 0 Process: /test_tb/#INITIAL#138 File: ./blocks/sim/test_tb.sv Line: 143
# r_val[-1.#IND00], rr_val[0.000000], rrr_val[1.#INF00], rrrr_val[0.500000]
# 32bit fp convertion sfix_28_32
SIM END.
# r_val[0.000000], rr_val[0.000000], rrr_val[0.500000], rrrr_val[0.500000]
The solution is to avoid any not protected casting, like a casting with the braces boundary:
r_val = real'{ byteU,byteH,byteL} / (2.0**44) ; // WRONG
rr_val = real'({byteU,byteH,byteL}) / (2.0**44) ; // CORRECT
If the scaling factor occurs then the operation, generally /, must be done with the same type of operands (real/real).
Unsafe way is (real/long) which leads into a nightmare.

signed and unsigned fixed point multiplication in SystemVerilog

I have a specific question and a request for more general guidance.
My question is what is the cleanest way to multiply a signed number by an unsigned number in SystemVerilog?
Below is a little test code that illustrates the problem. 'a' is the unsigned number. 'b' is the signed number. In order to produce a correct signed result SystemVerilog seems to require that both operands of the multiplication be signed. To make that work here I had to add an extra '0' to the front of the unsigned number to make it a valid signed number. I'm thinking there must be a cleaner way.
More generally, I am just getting started doing fixed point math in SystemVerilog. In VHDL there is very concrete syntax and even a standard package to support signed and unsigned fixed point math, with rounding, etc. Is there something like that in the SystemVerilog world?
Thanks,
module testbench ();
localparam int Wa = 8;
localparam int Wb = 8;
logic [Wa-1:0] a; // unsigned
logic [Wa:0] a_signed; // signed word with extra bit to hold unsigned number.
logic [Wb-1:0] b; // signed
logic [Wa+Wb-1:0] c; // result
localparam clk_period = 10;
assign a_signed = {1'b0, a};
assign c = $signed(a_signed)*$signed(b);
initial begin
a = +5;
b = +10;
#(clk_period*1);
$display("Hex: a=%x,b=%x, c=%x; Dec: a=%d, b=%d, c=%d", a, b, c, a, $signed(b), $signed(c));
a = +5;
b = -10;
#(clk_period*1);
$display("Hex: a=%x,b=%x, c=%x; Dec: a=%d, b=%d, c=%d", a, b, c, a, $signed(b), $signed(c));
a = +255;
b = +10;
#(clk_period*1);
$display("Hex: a=%x,b=%x, c=%x; Dec: a=%d, b=%d, c=%d", a, b, c, a, $signed(b), $signed(c));
a = +255;
b = -10;
#(clk_period*1);
$display("Hex: a=%x,b=%x, c=%x; Dec: a=%d, b=%d, c=%d", a, b, c, a, $signed(b), $signed(c));
$stop;
end
endmodule
system verilog rules say
If any operand is unsigned, the result is unsigned, regardless of the operator
Propagate the type and size of the expression (or self-determined subexpression) back down to the context-determined operands of the expression
When propagation reaches a simple operand as defined in 11.5, then that operand shall be converted to the propagated type and size.
So, in other words, when you multily signed and unsigned numbers, the expression type will be determined as unsigned. This will be propagated back to operands and all signed will be treated as unsigned as well.
so, your result will be identical to the one of multiplying two unsigned numbers. So, the cleanest way, if you need signed result, is to convert all operands to signed number.
You will also need an extra bit in your operands to have a place for the sign bit. Otherwise 255 will be treated as -1 in 8-bit sign conversion.

Matlab ask for integer loop

I'm creating a program to simulate a random walk and it requires the user to input an integer number of steps to take for the walk.
The prompt for this uses code very similar to this:
**% Ask user for a number.
defaultValue = 45;
titleBar = 'Enter a value';
userPrompt = 'Enter the integer';
caUserInput = inputdlg(userPrompt, titleBar, 1,{num2str(defaultValue)});
if isempty(caUserInput),return,end; % Bail out if they clicked Cancel.
% Round to nearest integer in case they entered a floating point number.
integerValue = round(str2double(cell2mat(caUserInput)));
% Check for a valid integer.
if isnan(integerValue)
% They didn't enter a number.
% They clicked Cancel, or entered a character, symbols, or something else not allowed.
integerValue = defaultValue;
message = sprintf('I said it had to be an integer.\nI will use %d and continue.', integerValue);
uiwait(warndlg(message));
end**
However, I want it to simply display the "Enter a value" prompt again if the user does not enter an integer the first time i.e. 4.4.
Any ideas?
Thanks!
if (mod(integerValue,1) == 0)
will evaluate to true if integerValue is an integer. Simply augment your if statement w/ this logic. You might want to consider changing to using a while loop so the user can enter bad input more than once.
The first answer is totally correct for checking for an integer value, but to address the "show prompt again" issue you can just use a loop conditioning it to get the exact kind of data you want:
caUserInput = nan; %or anything worng for that matter
while isempty(caUserInput) || isnan(caUserInput)
caUserInput = inputdlg(userPrompt, titleBar, 1,{num2str(defaultValue)});
end
if you want you can start it again with different argument lines in a more fancy style:
inputiswrong = 1; %or anything worng for that matter
while inputiswrong
inputiswrong = 0;
caUserInput = inputdlg(userPrompt, titleBar, 1,{num2str(defaultValue)});
if isempty(caUserInput )
userPrompt = 'Try again with an input';
inputiswrong = 1;
end
if isnan(caUserInput )
userPrompt = 'not really a number';
inputiswrong = 1;
end
%and so on
end
In both scenarios you should consider transforming the caUserInput to something you could use, i think inputdlg returns a cell so maybe a cell2mat() around the inputdlg().
Remember that unspecified inputs in MATLAB are double-precision by default. For instance a=3 is not an integer. So you should consider two cases:
Integer type
If you are talking about integer type in MATLAB the easiest way is to use isinteger function by MATLAB:
tf = isinteger(A)
for instance:
isinteger(4.4)
=
0
as I mentioned before, 3 is not an integer:
isinteger(3)
=
0
but this one is integer actually:
isinteger(uint8(3))
=
1
To repeat the input query also easily use the same function in a while loop
while ~isinteger(a)
disp('enter an integer');
....
end
Constant double-precision with no decimal
But if you are considering normal constant inputs to be integers you could convert them to integer and compare the result with the original value:
while a ~= double(int64(a))
disp('enter an integer');
....
end
int64 converts the double type to integer, and double converts it back to double. If in this process the number remains unchanged, then you could consider that it was intended to be an integer.
Recommendation for you specific program
I would use a fix function to get rid of the decimal parts. Usually when you receive a double-precision number including decimal values, the main intention is the the number before the floating point. So in many algorithms it is common practice to use fix to round each element of the given number to the nearest integer toward zero.

Floating point bug when writing to file in Matlab?

Im not sure if Im missing something simple but the following code fails (a and b are meant to be the same):
a=single(2147483584)
f=fopen('test','wb');
fwrite(f,a,'int32')
fclose(f);
f=fopen('test','rb');
b=fread(f,inf,'int32');
fclose(f)
a
b
with output:
a =
2.1475e+009
b =
-2.1475e+009
and the following code succeeds:
a=single(2147483583)
f=fopen('test','wb');
fwrite(f,a,'int32')
fclose(f);
f=fopen('test','rb');
b=fread(f,inf,'int32');
fclose(f)
a
b
with output:
a =
2.1475e+009
b =
2.1475e+009
Does anyone know why?
I don't know Matlab well, but it seems fairly clear what's happening here. You're converting a to a float and then storing the result of that conversion as a 32-bit signed integer. But the nearest single-precision IEEE 754 float to the integer 2147483584 is 2147483648.0, or 2**31. A 32-bit integer can only represent values in the range [-2**31, 2**31-1], so it looks as though when you write this value as an integer, it gets wrapped modulo 2**32 to give -2**31 instead of 2**31.
In contrast, the nearest single-precision float to 2147483583 is 2147483520.0, which does fit in a 32-bit integer.