Author Topic: How to convert a 16-bit unsigned to 16-bit signed for multipliers  (Read 4750 times)

0 Members and 1 Guest are viewing this topic.

Offline DmeadsTopic starter

  • Regular Contributor
  • *
  • Posts: 171
  • Country: us
  • who needs deep learning when you have 555 timers
I am very new to filter design on FPGA, but I am doing one for a hobby project. I have a 16-bit unsigned value coming from my ADC, which then needs to go into an IIR filter. The FPGA has 16-bit multipliers, and since some of my coefficients are negative, I think I need both operands to be signed in the multiplication. The ADC is unipolar, positive voltage only.

What is the proper way to truncate or round the 16-bit unsigned ADC value into a 16-bit signed number (twos compliment)?
« Last Edit: October 12, 2024, 01:03:55 am by Dmeads »
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4834
  • Country: nz
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #1 on: October 12, 2024, 01:11:39 am »
You can only sensibly convert an unsigned value into a signed one with the same numerical value by making sure the top bit is clear.

What are you going to do it it's not clear e.g. your ADC gave a value between 0x8000 and 0xFFFF ?  Do you want to set all such values to 0x7FFF?  That's not very nice. Maybe you want instead to halve all values -- either digitally, or by limiting the input range to the ADC?

But all this is unnecessary anyway, as it's perfectly well possible to multiply an unsigned 16 bit number by a signed 16 bit number to give the CORRECT 32 bit result.

CPU instruction sets such as RISC-V provide three different instructions to get the hi bits of a multiply: MULH, MULHU, and MULHSU (SxS, UxU, SxU). The lo bits are the same regardless.

You can probably configure your FPGA's DSP blocks to do signed*unsigned multiplication.

Or you can write code like...

Code: [Select]
module signed_unsigned_multiplier (
    input signed [15:0] a,  // Signed 16-bit input
    input [15:0] b,         // Unsigned 16-bit input
    output [31:0] result    // 32-bit result
);
    assign result = $signed(a) * b;
endmodule

... which notionally extends a to 32 bits first (which should get optimised, hopefully just by putting the multiplier block into signed*unsigned mode)). A lot of FPGAs actually have 18 bit multipliers, in which case the signed variable only has to be extended to 18 bits (and the unsigned one zero-extended) and all will be well.

« Last Edit: October 12, 2024, 01:14:40 am by brucehoult »
 
The following users thanked this post: Dmeads

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15899
  • Country: fr
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #2 on: October 12, 2024, 01:29:20 am »
Yes, you can keep it unsigned.

But if you really want to turn the value into a signed one, I'm assuming you have a 0x0000-0xFFFF range of values from min to max, and would like to turn that into a signed integer, 0x8000 becoming zero, 0x0000 becoming -32768 and 0xFFFF becoming 32767.

If so, you'd just have to subtract 0x8000 from the unsigned value.

Note that subtracting 0x8000 in that case doesn't even require a complete subtraction. If bit 15 is set, just clear it. If bit 15 is not set, set it. So all in all => just invert bit 15 and you should turn your unsigned value into a signed one, for which 0 corresponds to the middle value.

 
The following users thanked this post: Dmeads

Online radiolistener

  • Super Contributor
  • ***
  • Posts: 4156
  • Country: 00
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #3 on: October 14, 2024, 09:53:00 am »
Just subtract 0x8000.

But first check ADC documentation, because usually they already send the data in two's compliment signed format. And some have configuration register/pin to select desired format.

Note that bit truncation is not a safe operation because it introduces noise. Proper rounding should be used to minimize this noise. Similarly, bit expansion is also not safe, as it can introduce bias if scaling is involved.
« Last Edit: October 14, 2024, 03:41:37 pm by radiolistener »
 
The following users thanked this post: glenenglish

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4834
  • Country: nz
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #4 on: October 14, 2024, 10:42:43 am »
omg
 

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4881
  • Country: dk
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #5 on: October 14, 2024, 10:58:45 am »
For values greater than 32767 (0x7FFF), subtract 65536 (0x10000) to get the equivalent negative signed value.
For values less than or equal to 32767, the value remains the same.

But first check ADC documentation, because usually they already send the data in two's compliment signed format. And some have configuration register/pin to select desired format.

Note that bit truncation is not a safe operation because it introduces noise. Proper rounding should be used to minimize this noise. Similarly, bit expansion is also not safe, as it can introduce bias if scaling is involved.

wait, what??

subtracting 0x10000 from 16 bit number and putting into 16 bit does nothing ...

truncation and rounding result in the same noise except for a ~1/2 bit bias
 

Online radiolistener

  • Super Contributor
  • ***
  • Posts: 4156
  • Country: 00
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #6 on: October 14, 2024, 11:33:02 am »
subtracting 0x10000 from 16 bit number and putting into 16 bit does nothing ...

No.
32768 - 65536 = -32768
32769 - 65536 = -32767
...
65534 - 65536 = -2
65535 - 65536 = -1

truncation and rounding result in the same noise except for a ~1/2 bit bias

No.
Simple truncation without rounding introduces more noise compared to truncation with rounding.

For example, in DSP code, truncating a clean sine wave without rounding can introduce new frequency spurs that were absent in the original signal.
« Last Edit: October 14, 2024, 11:39:44 am by radiolistener »
 
The following users thanked this post: glenenglish

Offline coppice

  • Super Contributor
  • ***
  • Posts: 10121
  • Country: gb
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #7 on: October 14, 2024, 11:37:42 am »
Simple truncation without rounding introduces additional noise in comparison with truncation with rounding.
Its more accurate to say you are removing signal rather than adding noise. If instead of rounding you dither when reducing word lengths you are very specifically adding noise, yet its a good thing as you end up with more signal left after the operation.
 

Online radiolistener

  • Super Contributor
  • ***
  • Posts: 4156
  • Country: 00
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #8 on: October 14, 2024, 11:50:47 am »
Its more accurate to say you are removing signal rather than adding noise.

I disagree. When you truncate bits without rounding, the truncation error is signal-dependent and introduces a random error, which is effectively added noise. Rounding helps to minimize the amplitude of this noise by providing a more controlled error.

When I added rounding to the DSP of my SDR receiver, its spurious performance improved significantly. I noticed that some high spurs disappeared completely, while others were greatly reduced.
 
The following users thanked this post: glenenglish

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4834
  • Country: nz
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #9 on: October 14, 2024, 11:51:50 am »
subtracting 0x10000 from 16 bit number and putting into 16 bit does nothing ...

No.
32768 - 65536 = -32768
32769 - 65536 = -32767
...
65534 - 65536 = -2
65535 - 65536 = -1

I have a question for you.

What is the 16 bit unsigned binary number for 65534?

What is the 16 bit signed binary number for -2?
 

Offline coppice

  • Super Contributor
  • ***
  • Posts: 10121
  • Country: gb
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #10 on: October 14, 2024, 11:56:06 am »
Its more accurate to say you are removing signal rather than adding noise.

I disagree. When you truncate bits without rounding, the truncation error is signal-dependent and introduces a random error, which is effectively added noise. Rounding helps to minimize the amplitude of this noise by providing a more controlled error.

When I added rounding to the DSP of my SDR receiver, its spurious performance improved significantly. I noticed that some high spurs disappeared completely, while others were greatly reduced.
Sure, but if you had dithered instead of rounding you would have had a hard time finding any signs of those spurs at all, and yet you would have added around 1/2 a bit of noise to the signal.
 

Online radiolistener

  • Super Contributor
  • ***
  • Posts: 4156
  • Country: 00
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #11 on: October 14, 2024, 12:05:10 pm »
What is the 16 bit unsigned binary number for 65534?

What is the 16 bit signed binary number for -2?

Both are 16’b1111111111111110 = 16'hfffe

The difference lies in how these numbers are mapped to signal amplitude within the ADC. Therefore, it is important to consider the format in which the ADC transmits data and to perform the necessary conversions if your code operates with a different representation.

In a signed representation, the number 0 corresponds to the center of the ADC scale, which roughly aligns with the average signal level. In contrast, for an unsigned representation, the number 32768 corresponds to the center of the ADC scale.

As you can see unsigned number 0 is not equals to signed number 0 in context of ADC output format.

In reality, the situation is a bit more complex since there is no specific code that represents the center of the scale due to the even number of codes. However, for the sake of simplicity in processing, the AES17 standard considers the signed number 0 as the center of the ADC scale. While this results in an asymmetrical scale, it simplifies the signal processing, and the value -32768 in this standard is considered as exceeding full-scale, because 16-bit full scale of signed number range is assumed as -32767..+32767.


To make it more clear, here is table with numbers for 4-bit ADC.
1-st column is a signal value in signed decimal format (which is used for processing)
2-nd column is a hex ADC output with unsigned format
3-rd column is a hex ADC output with signed format

Code: [Select]
-8 0 8
-7 1 9
-6 2 a
-5 3 b
-4 4 c
-3 5 d
-2 6 e
-1 7 f
+0 8 0
+1 9 1
+2 a 2
+3 b 3
+4 c 4
+5 d 5
+6 e 6
+7 f 7


Sure, but if you had dithered instead of rounding you would have had a hard time finding any signs of those spurs at all, and yet you would have added around 1/2 a bit of noise to the signal.

I understand what you're saying, but I'm not sure this approach can yield the same effective results as rounding. Dithering might be preferable if random noise is more acceptable than stable spurs, but it depends on the specific requirements of the application.
« Last Edit: October 14, 2024, 02:54:27 pm by radiolistener »
 
The following users thanked this post: glenenglish, Murphy86

Offline MarginallyStable

  • Regular Contributor
  • *
  • Posts: 72
  • Country: us
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #12 on: October 14, 2024, 02:51:07 pm »
If the ADC output is linear, I.E:
  0x0000 -> -5V
  0x8000 -> 0V
  0xFFFF  -> ~5V (-1bit value)

You just exclusive-or the MSB to get a signed value

   0x0000 ^ 0x8000 -> 0x8000 (most negative value signed value)
   0x8000 ^ 0x8000 -> 0x0000
   0xFFFF ^ 0x8000 -> 0x7FFF (most positive signed value)

EDIT: Just saw you mention unipolar
« Last Edit: October 14, 2024, 02:58:42 pm by MarginallyStable »
 

Offline gael

  • Newbie
  • Posts: 6
  • Country: gr
Re: How to convert a 16-bit unsigned to 16-bit signed for multipliers
« Reply #13 on: January 17, 2025, 09:11:15 pm »
16-bit unsigned needs to get to 17-bit as signed, with a zero MSB added.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf