我的背景是软件,我是(系统)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
答案 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(无符号)宽或更大(符号不可知)