我的 VDHL 代码运行不正确 - vhdl 中的平方根
My VDHL code runs incorrectly - square root in vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity RaccinCarreSequentielle is
generic(
N: natural:= 16
);
port(
X: unsigned(2*N-1 downto 0);
reset : in std_logic; --clear signal
clk : in std_logic;
state_done : in std_logic; --start
result_done : out std_logic;
result : out unsigned(2*N-1 downto 0)
);
end entity;
architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
type state is (s0_wait,s1_init,s2_calcul,s3_fini);
signal pre_state,next_state: state;
begin
state_register:process(reset,clk)
begin
if reset = '1' then
pre_state <= s0_wait;
elsif rising_edge(clk) then
pre_state <= next_state;
end if;
end process state_register;
state_machine: process(pre_state,state_done)
variable rx,rz,rv : unsigned(2*N-1 downto 0);
variable ri: unsigned(N-1 downto 0);
begin
case pre_state is
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
else
next_state <= s0_wait;
end if;
when s1_init =>
next_state <= s2_calcul;
rx := x;
rz := (others => '0');
rv := (2*N-2 => '1', others => '0');
ri := to_unsigned(N-1,N);
when s2_calcul =>
if ri > 0 then
ri := ri - 1;
rz := rz + rv;
if rx > rz then
rx := rx - rz;
rz := rz + rv;
else
rz := rz - rv;
end if;
rz := '0' & rz(2*N-1 downto 1);
rv := "00" & rv(2*N-1 downto 2);
next_state <= s2_calcul;
else
next_state <= s3_fini;
end if;
when s3_fini =>
result <= rz;
if state_done = '0' then
next_state <= s0_wait;
else
next_state <= s3_fini;
end if;
when others =>
null;
end case;
end process state_machine;
result_proc: process(pre_state)
begin
if pre_state = s3_fini then
result_done <= '1';
else
result_done <= '0';
end if;
end process result_proc;
end architecture;
当我在 state2 中使用 for 循环时,我的代码将 运行 正确并且我的结果很好。例如,当我想求 255 的平方根时,我会得到 15。但是当我不想在 state2_calcul 中使用 For 循环时,如您所见。所以我每次进入 state2 时都做了一个 if 语句来减少变量 RI,如下所示。我做了一个模拟,但是状态总是停在状态2,它不能像我的link。
when s2_calcul =>
if ri > 0 then
ri := ri - 1;
rz := rz + rv;
if rx > rz then
rx := rx - rz;
rz := rz + rv;
else
rz := rz - rv;
end if;
rz := '0' & rz(2*N-1 downto 1);
rv := "00" & rv(2*N-1 downto 2);
next_state <= s2_calcul;
else
next_state <= s3_fini;
end if;
我认为问题出在嵌套 IF 上,但是当我更改它时,没有任何变化。有人可以帮我解释这个问题,我该如何解决。谢谢
你无法记住设计的组合部分中的任何内容。所以你需要 ri
、rx
、rv
和 rz
的寄存器。这些寄存器在某种程度上是你的全局状态的一部分,它确实是它们的组合,也是 pre_state
寄存器的一部分。
让我们继续您已经使用的样式:一个用于寄存器的同步过程,另一个用于组合部分,以及一个用于每个 xxx
寄存器输入的 next_xxx
信号。
architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
type state is (s0_wait, s1_init, s2_calcul, s3_fini);
signal pre_state, next_state: state;
signal rx, rz, rv, next_rx, next_rz, next_rv: unsigned(2*N-1 downto 0);
signal ri, next_ri: unsigned(N-1 downto 0);
begin
state_register:process(reset,clk)
begin
if reset = '1' then
pre_state <= s0_wait;
rx <= (others => '0');
rz <= (others => '0');
ri <= (others => '0');
rv <= (others => '0');
elsif rising_edge(clk) then
pre_state <= next_state;
rx <= next_rx;
rz <= next_rz;
ri <= next_ri;
rv <= next_rv;
end if;
end process state_register;
state_machine: process(pre_state, state_done, x, rx, rz, rv, ri)
variable tmpz : unsigned(2*N-1 downto 0);
begin
next_state <= pre_state;
next_rx <= rx;
next_rz <= rz;
next_rv <= rv;
next_ri <= ri;
tmpz := (others => '0');
case pre_state is
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
end if;
when s1_init =>
next_state <= s2_calcul;
next_rx <= x;
next_rz <= (others => '0');
next_rv <= (others => '0');
next_rv(2*N-2) <= '1';
next_ri <= to_unsigned(N-1,N);
when s2_calcul =>
if ri > 0 then
next_ri <= ri - 1;
tmpz := rz + rv;
if rx > tmpz then
next_rx <= rx - tmpz;
tmpz := tmpz + rv;
else
tmpz := tmpz - rv;
end if;
next_rz <= '0' & tmpz(2*N-1 downto 1);
next_rv <= "00" & rv(2*N-1 downto 2);
else
next_state <= s3_fini;
end if;
when s3_fini =>
if state_done = '0' then
next_state <= s0_wait;
end if;
end case;
end process state_machine;
result <= rz;
result_done <= '1' when pre_state = s3_fini else '0';
end architecture;
看到了吗?组合进程的敏感列表包含进程读取的all个信号,它赋值的信号总是赋值(感谢一开始的默认赋值),唯一的变量总是在使用之前分配(也感谢一开始的默认分配)。
请注意,由于一开始的默认分配,在综合时没有获得闩锁的风险。默认情况下:
next_xxx <= xxx;
赋值表示默认情况下 xxx
寄存器不应更改。一个有趣的副作用是您不再需要某些 else
语句。您可以替换:
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
else
next_state <= s0_wait;
end if;
作者:
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
end if;
因为 else
子句已经是默认情况。
当然,也可能因为结构完全不同,所以不能完全按照您的意愿工作。但至少这应该是调试的良好起点。请记住合成过程的主要原则:
- 敏感列表必须包含进程读取的所有信号(VHDL 2008标准的
all
关键字是有帮助,如果你的工具支持的话),
- 它分配的信号(它的输出)必须分配每次过程恢复(最开始的默认分配可以是对初学者很方便),
- 变量必须总是在使用之前赋值(一开始的默认赋值对初学者来说很方便)。
小心,这 3 条黄金法则并不那么容易检查,尤其是在复杂的嵌套 if
和 case
语句中。这就是为什么我总是建议初学者在一开始就使用默认分配的技巧。规则 #2 和 #3 变得微不足道。对于规则 #1,如果您的工具支持 VHDL 2008 标准的 all
关键字,请将其用于所有组合过程(不适用于同步过程):
state_machine: process(all)
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity RaccinCarreSequentielle is
generic(
N: natural:= 16
);
port(
X: unsigned(2*N-1 downto 0);
reset : in std_logic; --clear signal
clk : in std_logic;
state_done : in std_logic; --start
result_done : out std_logic;
result : out unsigned(2*N-1 downto 0)
);
end entity;
architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
type state is (s0_wait,s1_init,s2_calcul,s3_fini);
signal pre_state,next_state: state;
begin
state_register:process(reset,clk)
begin
if reset = '1' then
pre_state <= s0_wait;
elsif rising_edge(clk) then
pre_state <= next_state;
end if;
end process state_register;
state_machine: process(pre_state,state_done)
variable rx,rz,rv : unsigned(2*N-1 downto 0);
variable ri: unsigned(N-1 downto 0);
begin
case pre_state is
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
else
next_state <= s0_wait;
end if;
when s1_init =>
next_state <= s2_calcul;
rx := x;
rz := (others => '0');
rv := (2*N-2 => '1', others => '0');
ri := to_unsigned(N-1,N);
when s2_calcul =>
if ri > 0 then
ri := ri - 1;
rz := rz + rv;
if rx > rz then
rx := rx - rz;
rz := rz + rv;
else
rz := rz - rv;
end if;
rz := '0' & rz(2*N-1 downto 1);
rv := "00" & rv(2*N-1 downto 2);
next_state <= s2_calcul;
else
next_state <= s3_fini;
end if;
when s3_fini =>
result <= rz;
if state_done = '0' then
next_state <= s0_wait;
else
next_state <= s3_fini;
end if;
when others =>
null;
end case;
end process state_machine;
result_proc: process(pre_state)
begin
if pre_state = s3_fini then
result_done <= '1';
else
result_done <= '0';
end if;
end process result_proc;
end architecture;
当我在 state2 中使用 for 循环时,我的代码将 运行 正确并且我的结果很好。例如,当我想求 255 的平方根时,我会得到 15。但是当我不想在 state2_calcul 中使用 For 循环时,如您所见。所以我每次进入 state2 时都做了一个 if 语句来减少变量 RI,如下所示。我做了一个模拟,但是状态总是停在状态2,它不能像我的link。
when s2_calcul =>
if ri > 0 then
ri := ri - 1;
rz := rz + rv;
if rx > rz then
rx := rx - rz;
rz := rz + rv;
else
rz := rz - rv;
end if;
rz := '0' & rz(2*N-1 downto 1);
rv := "00" & rv(2*N-1 downto 2);
next_state <= s2_calcul;
else
next_state <= s3_fini;
end if;
我认为问题出在嵌套 IF 上,但是当我更改它时,没有任何变化。有人可以帮我解释这个问题,我该如何解决。谢谢
你无法记住设计的组合部分中的任何内容。所以你需要 ri
、rx
、rv
和 rz
的寄存器。这些寄存器在某种程度上是你的全局状态的一部分,它确实是它们的组合,也是 pre_state
寄存器的一部分。
让我们继续您已经使用的样式:一个用于寄存器的同步过程,另一个用于组合部分,以及一个用于每个 xxx
寄存器输入的 next_xxx
信号。
architecture state_machine_raccincarre_arc of RaccinCarreSequentielle is
type state is (s0_wait, s1_init, s2_calcul, s3_fini);
signal pre_state, next_state: state;
signal rx, rz, rv, next_rx, next_rz, next_rv: unsigned(2*N-1 downto 0);
signal ri, next_ri: unsigned(N-1 downto 0);
begin
state_register:process(reset,clk)
begin
if reset = '1' then
pre_state <= s0_wait;
rx <= (others => '0');
rz <= (others => '0');
ri <= (others => '0');
rv <= (others => '0');
elsif rising_edge(clk) then
pre_state <= next_state;
rx <= next_rx;
rz <= next_rz;
ri <= next_ri;
rv <= next_rv;
end if;
end process state_register;
state_machine: process(pre_state, state_done, x, rx, rz, rv, ri)
variable tmpz : unsigned(2*N-1 downto 0);
begin
next_state <= pre_state;
next_rx <= rx;
next_rz <= rz;
next_rv <= rv;
next_ri <= ri;
tmpz := (others => '0');
case pre_state is
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
end if;
when s1_init =>
next_state <= s2_calcul;
next_rx <= x;
next_rz <= (others => '0');
next_rv <= (others => '0');
next_rv(2*N-2) <= '1';
next_ri <= to_unsigned(N-1,N);
when s2_calcul =>
if ri > 0 then
next_ri <= ri - 1;
tmpz := rz + rv;
if rx > tmpz then
next_rx <= rx - tmpz;
tmpz := tmpz + rv;
else
tmpz := tmpz - rv;
end if;
next_rz <= '0' & tmpz(2*N-1 downto 1);
next_rv <= "00" & rv(2*N-1 downto 2);
else
next_state <= s3_fini;
end if;
when s3_fini =>
if state_done = '0' then
next_state <= s0_wait;
end if;
end case;
end process state_machine;
result <= rz;
result_done <= '1' when pre_state = s3_fini else '0';
end architecture;
看到了吗?组合进程的敏感列表包含进程读取的all个信号,它赋值的信号总是赋值(感谢一开始的默认赋值),唯一的变量总是在使用之前分配(也感谢一开始的默认分配)。
请注意,由于一开始的默认分配,在综合时没有获得闩锁的风险。默认情况下:
next_xxx <= xxx;
赋值表示默认情况下 xxx
寄存器不应更改。一个有趣的副作用是您不再需要某些 else
语句。您可以替换:
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
else
next_state <= s0_wait;
end if;
作者:
when s0_wait =>
if state_done = '1' then
next_state <= s1_init;
end if;
因为 else
子句已经是默认情况。
当然,也可能因为结构完全不同,所以不能完全按照您的意愿工作。但至少这应该是调试的良好起点。请记住合成过程的主要原则:
- 敏感列表必须包含进程读取的所有信号(VHDL 2008标准的
all
关键字是有帮助,如果你的工具支持的话), - 它分配的信号(它的输出)必须分配每次过程恢复(最开始的默认分配可以是对初学者很方便),
- 变量必须总是在使用之前赋值(一开始的默认赋值对初学者来说很方便)。
小心,这 3 条黄金法则并不那么容易检查,尤其是在复杂的嵌套 if
和 case
语句中。这就是为什么我总是建议初学者在一开始就使用默认分配的技巧。规则 #2 和 #3 变得微不足道。对于规则 #1,如果您的工具支持 VHDL 2008 标准的 all
关键字,请将其用于所有组合过程(不适用于同步过程):
state_machine: process(all)