Electronic – Reducing absolute value of signed number in VHDL

vhdl

Related to this: Scaling an input in VHDL, but different enough that I feel it warrants a new question.

I feel this should be simpler that I'm making it.

I have a stream of signed 16 bit samples which I want to attenuate by about 0.5 dB, but let's just use nicer numbers for the sake of illustration. I need:

-32000 (almost -1.0) -> -25600
+32000 (almost +1.0) -> +25600

etc

Simple right? I'm stuck because the multiplication operator must take either signed or unsigned operands, not a mixture of both (that makes sense).

Here is what I initially tried:

variable scale_factor : std_logic_vector(15 downto 0) := x"EFFF";
attenuated_sample <= std_logic_vector(unsigned(sample) * unsigned(scale_factor));

This doesn't work; it makes positive samples smaller but negative samples more negative.

The only way I've managed to achieve what I want is by multiplying the signed sample by a signed scale factor, but I've had to make the scale factor have an extra few zeros at the front so that it is always positive. I.e.,

variable temp : std_logic_vector(39 downto 0);
variable scale_factor : std_logic_vector(19 downto 0) := x"0EFFF";

temp := std_logic_vector(resize(signed(sample), 20) * signed(scale_factor);
attenuated_sample <= temp(31 downto 16);

This appears to work, but I don't like it because it's ugly. What is the correct way to go about this?

Best Answer

The reason your unsigned() cast doesn't work is because the result takes the exact bits in the input and just 'calls it unsigned'. ie, 0xFFFC signed is -4, but when cast to unsigned, it's 65532.

Instead, since your incoming signal is always 16 bit signed, just check to see if it is negative (as simple as checking the leading bit). Then, instead of casting to unsigned, negate it (which will have the desired effect) and then multiply by your scale factor, negating again afterwards. I can't think at the moment how to do this in a single step, but this should work for you if you can spare two extra multiplies.