Operador de módulo Verilog para envolver alrededor de un rango

1

Mi experiencia es en software y soy nuevo en (Sistema) Verilog, así que cuando tengo la tarea de implementar un cambio de caesar (desplazar cada letra en una cadena con N letras, envolviéndolo si es necesario, por ejemplo, ABCXYZ desplazado por 3 se convierte en DEFABC), Escribí lo siguiente, con la esperanza de poder reducir la duplicación de código, como lo haría en el software:

  /* every variable except 'direction' has the type 'byte' */
  always_comb
  begin
    shifted_char = fresh_char; /* don't touch bytes that aren't letters */
    is_lower_case = "z" >= fresh_char && fresh_char >= "a";
    is_upper_case = "Z" >= fresh_char && fresh_char >= "A";
    if (is_lower_case || is_upper_case)
      begin
        unique if (is_lower_case)
          alphabet_start = "a";
        else if (is_upper_case)
          alphabet_start = "A";
        alphabet_position = fresh_char - alphabet_start;
        if (direction == "f") /* direction is a module parameter: f for forwards results in a shifter, any other value results in an 'unshifter' */
          new_alphabet_position = (26 + (alphabet_position + shift_by)) % 26;
        else
          new_alphabet_position = (26 + (alphabet_position - shift_by)) % 26;
        shifted_char = new_alphabet_position + alphabet_start;
      end
  end

Mi pregunta es (asumiendo que es un desplazador hacia adelante): con respecto a la parte "% 26", ¿puedo esperar que el sintetizador deduzca que el rango de valores posibles que va a llegar a ese punto es [26, 26 + 25 +? 25] ([26, 76]) y, por lo tanto, solo hay 2 casos que la lógica debe distinguir entre (> 26 y > 52), en lugar de [lo que sea la llamada inteligente cuando se manejan todas las 256 entradas posibles ”(sería ¿Sería considerar los casos > 26, > 52, > 78 etc ...? ¿O hay una mejor manera? Estoy divagando ...)]?

Siempre podría hacer lo siguiente:

new_alphabet_position = alphabet_position + shift_by;
if (new_alphabet_position > 25)
  new_alpahbet_position -= 26;

/* Or, for the reverse shifter: */

new_alphabet_position = alphabet_position - shift_by;
if (new_alphabet_position < 0)
  new_alpahbet_position += 26;

... pero tenía curiosidad y quería preguntarle eso, así como uno relacionado (que espero que más personas puedan responder): ¿Se puede usar para hacer un contador normal sin poder de 2? (p.ej     cuenta < = (cuenta + 1)% 6; )? Siguiendo la respuesta de hgleamon1 al siguiente hilo, parece que (al menos una) la herramienta de sintetizador VHDL podría interpretarlo como se pretende: enlace

    
pregunta PistoletPierre

1 respuesta

0

A menos que exista una macro celda especializada, las no potencias de 2 módulos tomarán un gran número de puertas y tendrán demoras de propagación relativamente largas, especialmente si se hacen como lógica de combinación pura.

Tenga en cuenta, según el sintetizador, las variables 'alphabet_start', 'alphabet_position' y 'new_alphabet_position' my be infered latches. La forma en que los usó es como lógica intermedia, por lo que si no hace referencia a ellos fuera de este bloque siempre y su sintetizador tiene una optimización decente, entonces no será un bloqueo. Para garantizar que no serán cierres, se les debe dar valores predeterminados fuera de la declaración if.

Usted declara que todas las variables excepto 'dirección' son de tipo 'byte', esto significa que 'shift_by' puede tener un valor mayor que 25 o menor que -25 ('byte' es un valor firmado de forma predeterminada). Al usar valores con signo y agregar tres valores ( 26 + (alphabet_position + shift_by) ) antes de usar el módulo, hay un cambio decente de que el mod26 se evaluará en un valor con signo de 10 bits. Eso usará más lógica que si se usara en un valor de 8 bits. Hay un cambio en el que su sintetizador puede hacer alguna optimización, pero puede que no sea grandioso.

Si puede garantizar que 'shift_by' sea menor que 26 y mayor que -26 (mayor o igual que 0 si no está firmado), entonces no necesita 'alphabet_position' o 'new_alphabet_position'. Simplemente agregue o reste el 'shift_by' y calcule si está fuera del rango. Para la verificación de rango, primero verifique si 8'(shifted_char-26) >= alphabet_start . La razón de esto es asegurarse de que estamos comparando números positivos. "z" +25 es 147, que es negativo para un valor de 8 bits firmado. El 8'() lo convierte como un valor sin signo de 8 bits para recortar cualquier noveno intermedio 9º + bit (s). Si no es necesario un ajuste, compruebe si hifted_char < alphabet_start como ahora, la posibilidad de desbordarse a un número negativo ya se ha manejado.

Si no puede garantizar que 'shift_by' esté dentro del rango, entonces no puede elegir modificarlo. Afortunadamente, este es un valor firmado de 8 bits que es mejor que su peor caso original con un valor firmado de 10 bits. Esto no es ideal pero lo mejor que puedo ofrecer. Es más óptimo que el controlador de 'shift_by' asigne un valor legal y luego agregue más lógica para modificarlo.

Ya que está usando SystemVerilog, puede considerar usar fresh_char inside { ["A":"Z"] } que es funcionalmente lo mismo que "Z" >= fresh_char && fresh_char >= "A" . La palabra clave inside está destinada a ser sintetizable, pero no sé si es comúnmente compatible.

Considere el siguiente código. Puede que no sea el más optimizado, pero está más optimizado que su código original:

always_comb
begin
  shift_by_mod26 = shift_by % 26; // %26 is not need if guaranteed asb(value) < 26
  alphabet_start = (fresh_char inside { ["A":"Z"] }) ? "A" : "a";
  if ( fresh_char inside { ["A":"Z"], ["a":"z"] } )
  begin
     if (direction == "f")
       shifted_char = fresh_char + shift_by_mod26;
     else
       shifted_char = fresh_char - shift_by_mod26;

     // subtract 26 first in case shifted_char is >127
     // bring back to a positive if signed (>127 unsigned is negative signed)
     if (8'(shifted_char-26) >= alphabet_start)
       shifted_char -= 26;
     else if (shifted_char < alphabet_start)
       shifted_char += 26;
  end
  else
  begin
    /* don't touch bytes that aren't letters */
    shifted_char = fresh_char;
  end
end

Nota: si 'direction' no es un tipo 'byte', entonces debe tener al menos un ancho de 7bits (sin signo) o mayor (signo agnóstico) para cada coincidencia "f"

Publicación cruzada respuesta para una publicación cruzada question

    
respondido por el Greg

Lea otras preguntas en las etiquetas