Reducción del valor absoluto del número firmado en VHDL

3

Relacionado con esto: Escalado de una entrada en VHDL , pero lo suficientemente diferente que creo que merece una nueva pregunta.

Siento que esto debería ser más simple que lo estoy haciendo.

Tengo un flujo de muestras firmadas de 16 bits que quiero atenuar en aproximadamente 0,5 dB, pero solo usemos números más bonitos para ilustrar. Necesito:

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

etc

simple ¿verdad? Estoy atascado porque el operador de multiplicación debe tomar los operandos firmados o sin firmar , no una mezcla de ambos (eso tiene sentido).

Esto es lo que intenté inicialmente:

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

Esto no funciona; hace que las muestras positivas sean más pequeñas, pero las muestras negativas más negativas.

La única manera de lograr lo que quiero es multiplicando la muestra firmada por un factor de escala firmada , pero he tenido que hacer la escala El factor tiene unos pocos ceros adicionales en la parte frontal para que siempre sea positivo. Es decir,

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);

Esto parece funcionar, pero no me gusta porque es feo. ¿Cuál es la forma correcta de hacer esto?

    
pregunta benjwy

3 respuestas

2

La razón por la que su conversión unsigned () no funciona es porque el resultado toma los bits exactos en la entrada y simplemente 'lo llama unsigned'. es decir, 0xFFFC firmado es -4, pero cuando se convierte a unsigned, es 65532.

En cambio, como su señal entrante siempre tiene una firma de 16 bits, solo verifique que sea negativa (tan simple como verificar el bit inicial). Luego, en lugar de lanzar a unsigned, nánquelo (que tendrá el efecto deseado) y luego multiplíquelo por su factor de escala, negándolo de nuevo más tarde. No puedo pensar en este momento cómo hacer esto en un solo paso, pero esto debería funcionar para usted si puede ahorrar dos multiplicados adicionales.

    
respondido por el Kevin H
2

El más simple (aunque mantengo la flexibilidad al calcular los rangos vectoriales) puedo hacer esto:

constant scale_factor : signed(16 downto 0) := '0' & x"EFFF";
variable temp : signed(sample'high+scale_factor'high-1 downto 0);

temp := resize(sample, scale_factor'length) * scale_factor;
attenuated_sample <= temp(temp'high downto temp'high-attenuate_sample'length);

También puede querer hacer un cálculo más complejo para la muestra atenuada que el simple truncamiento hecho aquí.

Mi primera respuesta cuando la gente quiere escalar muestras es "no hagas eso, hazlo al final", una vez que el resto del procesamiento haya finalizado. Solo haz un seguimiento del factor de escala en tu cabeza. Puede que tengas una buena razón por la que eso no es factible ...

    
respondido por el Martin Thompson
1

Dependiendo de la precisión "sobre" 0.5dB debe ser ... si 0.56dB es lo suficientemente bueno, multiplíquelo por 15/16, o simplemente reste 1/16 de sí mismo.

sample : signed(15 downto 0);
scaled : signed(15 downto 0);
output : signed(15 downto 0);

scaled <= SHIFT_RIGHT(sample,4);
output <= sample - scaled;
    
respondido por el Brian Drummond

Lea otras preguntas en las etiquetas