用于包裹范围的Verilog模数运算符

时间:2017-11-22 02:06:23

标签: verilog system-verilog hdl caesar-cipher

我的背景是软件,我是(系统)Verilog的新手所以当负责实现凯撒移位器时(用字母移动字母串中的每个字母,必要时缠绕,例如ABCXYZ移位3变为DEFABC),我写了以下内容,希望能够减少代码重复,就像我在软件中一样:

  /* 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

我的问题是(假设它是一个前向移位器):对于“%26”部分,我可以期望合成器推断出它在那时得到的可能值的范围是[26,26 + 25 + 25]([26,76])所以只有2种情况逻辑需要区分(> 26和> 52),而不是[处理所有可能的256种不同输入时的智能调用 - (将是考虑案例> 26,> 52,> 78等......?还是有更好的方法?我离题......)]?

我总能做到以下几点:

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;

...但很好奇并且想要问这个,以及相关的一个(我希望更多的人能够回答):它可以用于制作一个普通的非2次幂计数器(例如     count&lt; =(count + 1)%6; )?通过hgleamon1对以下线程的响应,似乎(至少一个)VHDL合成工具可能按预期解释它:https://forums.xilinx.com/t5/Synthesis/Modulus-synthesizable-or-non-synthesizable/td-p/747493

1 个答案:

答案 0 :(得分:1)

除非存在专门的宏单元,否则2模数的非幂将占用大量门并且具有相对长的传播延迟,尤其是如果作为纯组合逻辑完成的话。

请注意,根据您的合成器,变量'alphabet_start','alphabet_position'和'new_alphabet_position'是推断锁存器。你使用它们的方式是作为中介逻辑,所以如果你不在它之外引用它们总是阻塞你的合成器有适当的优化,那么它就不会是一个锁存器。为了保证它们不是锁存器,必须在if语句之外给出默认值。

您声明除'direction'之外的所有变量都是'byte'类型,这意味着'shift_by'的值可能大于25或小于-25(默认情况下'byte'是有符号值)。通过使用带符号值并在使用模数之前添加三个值(26 + (alphabet_position + shift_by)),可以在10位有符号值上评估mod26。这将比使用8位值时使用更多的逻辑。您的合成器可能会进行一些更改,但可能不是很好。

如果你能保证'shift_by'小于26且大于-26(如果是无符号则大于或等于0),那么你不需要'alphabet_position'或'new_alphabet_position'。只需添加或减去'shift_by'并计算是否超出范围。对于范围检查,请先检查8'(shifted_char-26) >= alphabet_start。这样做的原因是为了确保我们正在比较正数。 “z”+25是147,对于带符号的8位值是负的。 8'()将其转换为8位无符号值,以修剪任何非零中间第9位+位。如果不需要进行调整,请检查hifted_char < alphabet_start是否已经处理了溢出为负数的可能性。

如果您无法保证'shift_by'在范围内,那么您无需选择修改它。幸运的是,这是一个8位有符号值,比具有10位有符号值的原始更糟糕的情况更好。这不是理想的,但我能提供的最好。让'shift_by'的驱动程序分配合法值然后添加更多逻辑来修改它更为理想。

由于您使用的是SystemVerilog,因此您可能需要考虑使用fresh_char inside { ["A":"Z"] },其功能与"Z" >= fresh_char && fresh_char >= "A"相同。 inside是关键字是可合成的,但我不知道它是否得到普遍支持。

请考虑以下代码。它可能不是最优化的,但它比原始代码更优化:

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

注意:如果'direction'不是'byte'类型,那么对于每个匹配“f”,它必须至少为7bits(无符号)宽或更大(符号不可知)

交叉发布帖子answer question