通过bit twiddling在循环调度中找到下一个

时间:2009-01-26 16:31:26

标签: algorithm bit-manipulation verilog vhdl

考虑以下问题。您有一个位串,表示一个热编码中的当前调度从属。例如,“00000100”(最左边的位为#7,最右边的#0)表示调度了从属#2。

现在,我想在循环调度方案中选择下一个预定的从站,并进行扭曲。我有一个“请求掩码”,说明实际上想要安排哪些奴隶。只会从想要的人那里挑选下一个奴隶。

一些例子(假设通过向左旋转完成循环调度)。 例1:

  • 目前:“00000100”
  • 面具:“01100000”
  • 下一个时间表:“00100000” - 在正常的循环赛中,#3然后#4应该在#2之后,但是他们没有请求,所以选择#5。

例2:

  • 目前:“01000000”
  • 面具:“00001010”
  • 下一个:“00000010” - 因为调度是通过左侧循环完成的,而#1是该顺序中的第一个请求从属。

现在,我知道,这可以很容易地循环编码。但实际上我想通过一个有点笨拙的操作得到我的结果,没有循环。动机:我想用VHDL / Verilog中的硬件(在FPGA中)实现它。

奖励是组成一个对任意数量的奴隶N都是通用的算法。

顺便说一下,这不是一个功课问题。无论何时想要以某种方式安排从属设备,并通过从属设备的请求来调度调度,这都是一个重要的问题。我目前的解决方案有点“沉重”,我想知道我是否遗漏了一些明显的东西。

8 个答案:

答案 0 :(得分:6)

循环不一定是坏事。

我只想做

current[i] = current[i-1] & mask[i] |                         // normal shift logic
                mask[i] & current[i-2] & !mask[i-1] |         // here build logic 
                ...                                          // expression for 
                                                             // remaining 

然后将其放入生成循环(即它将展开到硬件中),这将为表达式生成并行硬件。

此处提到的其他解决方案使用多个“ - ”。我只能劝阻他们,因为这会给你带来非常昂贵的操作。 ESP。在一个热点,你可以轻松获得超过> 32位,在HW中不容易实现,因为借位必须通过所有位(某些fpgas上的死区进位逻辑使其在少量位时可接近)。

答案 1 :(得分:4)

我在Altera高级综合食谱中找到了以下用于实现该任务的Verilog代码。

// 'base' is a one hot signal indicating the first request
// that should be considered for a grant.  Followed by higher
// indexed requests, then wrapping around.
//

module arbiter (
    req, grant, base
);

parameter WIDTH = 16;

input [WIDTH-1:0] req;
output [WIDTH-1:0] grant;
input [WIDTH-1:0] base;

wire [2*WIDTH-1:0] double_req = {req,req};
wire [2*WIDTH-1:0] double_grant = double_req & ~(double_req-base);
assign grant = double_grant[WIDTH-1:0] | double_grant[2*WIDTH-1:WIDTH];

endmodule

它使用减法(虽然只有一次),所以在概念上它与Doug的解决方案非常相似。

答案 2 :(得分:3)

以下解决方案适用于任意数量的从设备(K),并且在FPGA中为O(n)。对于现场的每个位,您将需要三个逻辑门和两个逆变器。我用基本的逻辑模拟器测试了这个概念,它可以工作。

当前和掩码之间的逻辑门链实际上创建了一个优先级系统,它有利于链中的“低位”位。这个链在末尾循环,但当前位用于打破链。

为了使操作可视化,假设在当前字段中设置了位 3 ,并在图中向下跟随信号。位 3 的逻辑1在第一个AND门的输入端放置一个逻辑0,这保证了该AND门的输出也为零(这就是OR门链断开的地方) )。第一个AND门输出端的零点在第二个AND门的输入端放置一个。这使得 next 的位 2 直接依赖于掩码的位 2

现在,OR门链发挥作用。

如果设置掩码的位 2 ,则直接在其左侧的OR门的逻辑输出也将是1,这将是逻辑输出在当前的位 2 下面的AND门输入端(由于当前中只有一位可以设置为0,因此它将为零)时间)。顶部AND门的输出处的逻辑1在底部AND门的输入处放置逻辑0,因此将 next 的位 1 设置为等于零。

如果掩码的位 2 未设置,则OR门的两个输入都将为零,因此AND门的输出位于 2 < 当前的/ i>将为零,在底部AND门的输入端放置一个,因此将 1 设为 next 依赖于掩码的位 1

该逻辑遵循OR门“向上”的位,从左侧向后循环到右侧,确保 next 中只有一位可以设置为1。由于该位被置位,循环一旦返回到当前的位 3 就会停止。这可以防止电路停留在永久循环中。

我没有使用Verilog或VHDL的经验,因此我会将实际代码留给您and the rest of stackoverflow

alt text http://img145.imageshack.us/img145/5125/bitshifterlogicdiagramkn7.jpg

说明:

  1. 这个解决方案只是部分解决方案。它仍然需要某种锁存机制来保持位字段。
  2. 请记住,随着位数的增加,栅极电压稳定所需的时间也会增加。
  3. 必须有一些逻辑来处理当前字段等于零的情况。见this stackoverflow question

答案 3 :(得分:2)

假设两个补码表示,请在C中调用您的两个单词maskcurrent

mask_lo = (current << 1) - 1; // the bits to the right and including current
mask_hi = ~mask_lo;           // the bits to the left of current
                              // the left bits, otherwise right:
next = (mask & mask_hi) ? (mask & mask_hi) : (mask & mask_lo);
return (next & -next);        // the least significant bit set

答案 4 :(得分:2)

减去1是这里的基本想法。它用于级联借位以找到下一个任务。

bits_before_current = ~(current-1) & ~current
bits_after_current = current-1
todo = (mask & bits_before_current) 
if todo==0: todo = (mask & bits_after_current) // second part is if we have to wrap around
next = last_bit_of_todo = todo & -todo

这将在内部使用循环...

答案 5 :(得分:2)

有趣的问题!我不禁想知道你是否无法简化你的调度程序操作,所以这种操作是必要的。

鉴于您了解VHDL,我不会详细介绍,但我的建议如下:

使用3位编码器将当前计划的任务转换为数字:

01000000 - &gt; 6

然后使用桶形移位器将掩码旋转数字+ 1(跳过当前任务):

00001010 - &gt; 00010100

然后使用优先级编码器查找第一个可用的“下一个”任务:

00010100 - &gt; 00000100 - &gt; 2

然后通过添加反转桶移位:

(2 + 7)%8 = 1

当重新编码时,将提供下一个计划任务:

00000010

应该是非常快速和直接的,虽然桶状变速器在房地产方面是“昂贵的”,但我目前还没有看到解决这个问题的简单方法。

编辑:Doug的解决方案显得更加优雅......

- 亚当

答案 6 :(得分:1)

这应该做你想要的:

number_of_tasks= <number of tasks, in the example this is 8>
next_mask= current | (current - 1);
next_barrel= next | (next << number_of_tasks);
next_barrel&= ~number_of_tasks;
next_barrel&= -next_barrel;
next_barrel|= next_barrel >> number_of_tasks;
next_task_mask= next_barrel & -next_barrel;

基本上,复制下一个任务掩码的位,屏蔽掉我们不想考虑的位,找到最低设置位,将高位折回,然后取最低位设置。这是在恒定的时间内运行。

编辑:更新以考虑当前== 00010000和next_mask == 00111000

答案 7 :(得分:1)

未经测试,但是如果不能产生合理的合成,我会感到惊讶......相对于典型的比特笨拙的黑客而言,具有相对可读性的优势(无论如何)。< / p>

for i in current'range loop
  current := rotate_left(current, 1);
  if or_reduce(mask and current) = '1' then
     current:= mask and current;
  end if;
end loop;