缺少Minimal Complete and Verifiable example 使得很难就阻止代码准确冒泡排序的所有问题提供答案。这些可以按照您在故障排除时遇到的顺序进行描述。
proc1 : process(clk, reset)
begin
if reset = '0' then
if (clk'event and clk = '1') then
while (i <= 15) loop
if (in_array_sig(i) < in_array_sig(i + 1)) then
temp <= in_array_sig(i);
in_array_sig(i) <= in_array_sig(i + 1);
in_array_sig(i + 1) <= temp;
end if;
i <= i + 1;
end loop;
end if;
end if;
end process;
在开始之前,请注意时钟是通过复位门控的。您可以使用重置来限定分配,使其成为启用。
问题
我们会发现生成 MCVe 和测试台的第一件事是进程永远不会暂停。这是由 while 循环中的条件引起的,具体取决于 i 和 i 在进程中更新的信号。我在这里不应该是一个信号(或者你可以在这里使用一个 for 循环)。
这也指出 temp 是一个信号并且遇到同样的问题,在进程暂停和恢复之前,您不能使用 temp 的“新”值。信号被安排更新,没有包含 after 子句的波形元素的信号分配具有零延迟的隐式 after 子句。当任何计划恢复的进程尚未恢复并随后暂停时,不会发生信号更新。这允许在顺序语句中找到分配的信号的并发性(并发语句具有包含等效顺序语句的等效过程)。所以 i 和 temp 在执行一系列语句的过程中都不能更新,并且都想成为变量。
使用 in_array_sig 的信号我们也会被咬。当您增加 i 时,先前索引的 in_array_sig(i + 1) 将成为下一个循环迭代的 in_array_sig(i)。如果没有中间进程挂起和恢复,原始值是可用的。 in_array_sig 也想成为一个变量。
如果我们要修复所有这些问题,我们可能还会注意到 i 未初始化(这将在 for 循环迭代方案中处理)并且我们还可能会发现在一行中使用in_array_sig 的 (i + 1) 索引。如果问题的作者没有提供 MCVe,则不清楚数组大小是 16(编号为 0 到 15)还是 17。如果前者 i = 15 + 1 将超出 in_array 的未公开数组类型的索引范围, in_array_sig 和 sorted_array。
如果我们要确保满足索引范围,并注意到我们只需要比数组中的元素数量少 1 次测试和交换,我们会发现该过程不是完整的冒泡排序。我们会看到 in_array_sig 的最大二进制值最终成为 sorted_array 最右边的元素。但是不能保证剩余元素的顺序。
要执行完整的冒泡排序,我们需要另一个循环嵌套第一个循环。此外,现在的“内部” for 循环可以减少要遍历的元素数量,因为每次迭代都会在最右边留下一个最大的剩余元素,直到确保订单完成为止。
修复
修复上述问题会给我们带来如下所示的内容:
architecture foo of bubblesort is
use ieee.numeric_std.all;
begin
BSORT:
process (clk)
variable temp: std_logic_vector (3 downto 0);
variable var_array: bubble;
begin
var_array := in_array;
if rising_edge(clk) then
for j in bubble'LEFT to bubble'RIGHT - 1 loop
for i in bubble'LEFT to bubble'RIGHT - 1 - j loop
if unsigned(var_array(i)) > unsigned(var_array(i + 1)) then
temp := var_array(i);
var_array(i) := var_array(i + 1);
var_array(i + 1) := temp;
end if;
end loop;
end loop;
sorted_array <= var_array;
end if;
end process;
end architecture foo;
注意循环迭代方案是根据气泡边界类型来描述的,对于每次迭代,外部比长度短一个,内部比长度短一个。另请注意, sorted_array 分配已移至 in_array_sig 变量替换 var_array 可见的过程中。
另外值得注意的是无符号大于运算符的使用。 std_logic_vector 的“>”允许元值和“H”和“L”值扭曲关系比较,而无符号运算符是算术。
结果
引入包和实体声明:
library ieee;
use ieee.std_logic_1164.all;
package array_type is
type bubble is array (0 to 15) of std_logic_vector(3 downto 0);
end package;
library ieee;
use ieee.std_logic_1164.all;
use work.array_type.all;
entity bubblesort is
port (
signal clk: in std_logic;
signal reset: in std_logic;
signal in_array: in bubble;
signal sorted_array: out bubble
);
end entity;
连同一个测试台:
library ieee;
use ieee.std_logic_1164.all;
use work.array_type.all;
entity bubblesort_tb is
end entity;
architecture fum of bubblesort_tb is
signal clk: std_logic := '0';
signal reset: std_logic := '0';
signal in_array: bubble :=
(x"F", x"E", x"D", x"C", x"B", x"A", x"9", x"8",
x"7", x"6", x"5", x"4", x"3", x"2", x"1", x"0");
signal sorted_array: bubble;
begin
DUT:
entity work.bubblesort(foo)
port map (
clk => clk,
reset => reset,
in_array => in_array,
sorted_array => sorted_array
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if now > 30 ns then
wait;
end if;
end process;
end architecture;
我们得到:
有效的东西。
reset as enable 未包含在架构中的进程 BSORT 中,可以添加到带有时钟沿条件的 if 语句中。
关于这里,我们在关于描述硬件的评论中谈到了 Matthew Taylor 的观点。
根据合成工具的不同,该过程可能会或可能不会作为硬件实现。如果不是,则需要中间变量来保存内部循环的每次连续迭代中使用的数组部分。
还有一个时钟周期可以做多少的问题。最坏的情况是延迟深度由 15 个元素比较和 15 个 2:2 选择器有条件地交换元素对组成。
如果您要选择与合成延迟不兼容的时钟速度,您需要将实现从软件循环仿真重新架构为跨连续时钟运行的东西。
这可能就像允许更多时钟周期一样简单,通过使用该启用来确定冒泡排序何时有效加载到 sorted_array 寄存器。它可能更复杂,还允许使用不同且性能更好的排序方法,或者对冒泡排序进行修改以表示不再需要检测交换。