【问题标题】:Ise simulation VHDLIse仿真VHDL
【发布时间】:2015-01-24 00:12:19
【问题描述】:

我想问以下问题。我正在研究 FPGA 俄罗斯方块设计。我的代码语言是 Windows 7 ISE 环境中的 VHDL。

在我进行工作时,我经常使用 isim 模拟检查我的工作。随着设计越来越大,isim 停止工作并关闭。基本上导致此崩溃的模块是一个大 fsm。当我评论 fsm 的某些状态时,ISIM 又可以正常工作了。

我将评论哪些状态并不重要。我可以检查部分工作的 fsm。但我无法将其作为一个整体进行测试。这可能不是代码错误而只是 ISIM 吗?

谢谢

【问题讨论】:

  • 您的代码可能存在一些问题,这让 ISIM 感到不安,但 ISIM 崩溃使诊断变得更加困难。与另一个模拟器交叉检查,例如sourceforge.net/projects/ghdl-updates
  • 问题是我检查我的代码,直到假设我有 150 个状态并且一切正常的点 x。然后我又写了 20 个状态,我得到了这个错误。比我评论我知道他们实际工作的 150 个州中的一些州和最初导致 isim 崩溃的 20 个州也在工作。我在某处读到,在大型设计中,isim 是不稳定的。但我需要确定是 isim 问题还是我的代码!我也会说我的代码,但奇怪的是,当设计变小时,产生问题的状态工作得很好!
  • 如果您的状态机有 150 多个状态,那可能会给综合工具带来一些麻烦。您可能会考虑如何删除其中大约 130 个状态。
  • @Russell 我有 154 个州。但我需要它们,我想我还需要另外 100 个州。我可以使用更多的 fsm 吗?我可以每 30 个州使用一个 fsm 吗?
  • 我真的建议不要使用更多的 FSM。它变得太混乱了。你能在不同的过程中做更多的事情吗..?

标签: embedded hardware vhdl


【解决方案1】:

如何将复杂的 FSM 拆分成多个部分?
这里有一些示例行来说明一个主 FSM 和两个函数 FSM 之间的简单调用/返回握手协议

process(Clock)
begin
    if (Reset = '1') then
        MainFSM_State       <= ST_IDLE;
        Func1FSM_State  <= ST_IDLE;
        Func2FSM_State  <= ST_IDLE;
    else
        MainFSM_State       <= MainFSM_NextState;
        Func1FSM_State  <= Func1FSM_NextState;
        Func2FSM_State  <= Func2FSM_NextState;
    end if;
end process;

process(MainFSM_State, Input, ...,
                Func1FSM_Output, Func1FSM_Return,
                Func2FSM_Output, Func2FSM_Return)
begin
    Func1FSM_Call           <= '0';
    Func2FSM_Call           <= '0';

    MainFSM_Output      <= x"00";

    case MainFSM_State is
        when ST_IDLE =>
            if (Input = '1') then
                Func1FSM_Call               <= '1';
                MainFSM_NextState       <= ST_FUNC1_EXECUTING;
            end if;

        when ST_FUNC1_EXECUTING =>
            MainFSM_Output              <= Func1FSM_Output;

            if (Func1FSM_Return = '1') then
                Func2FSM_Call               <= '1';
            end if;

       ....
    end case;
end process;

您的设计的一些一般提示(基于代码 sn-p)

使用大 FSM 也很容易出错。所以编写大 FSM 的一个目标应该是可读性,这样你就可以更容易地发现错误。

  • 在使用 numeric_std 时不要使用 std_logic_arith、std_logic_unsigned。只使用 numeric_std 就足够了。

  • wait until 不是一个可综合的陈述。使用:

    if rising_edge(clk) then
      ...
    end if;
    
  • 您的外部输入(如 reset_switch)是否已同步和去抖动?像 reset_switch 这样的信号名称意味着这个信号是原始的;)

  • 您可以通过使用 case 语句来提高 if..elsif..else..end if; 语句的可读性(参见第 230 行)

  • 你使用了大量的二进制赋值,比如t_left_most &lt;= "010111"; --23。也许最好用另一种类型(例如受约束的自然)声明 t_left_most,这样您就可以在 FSM 中使用整数文字。为了将这些信号导出到其他模块,信号被转换回 std_logic_vector。 -> 提高可读性

  • 有时引入常量是很好的,例如

    constant C_TOP_MOST : NATURAL := 0;
    
  • 我建议您从您的 FSM 中提取您的计数器 memReadAddress。大 FSM 应该只生成流控制信号,FSM 不应该包含任何数据流逻辑(计数器、多路复用器……)。因此,将计数器提取到一个单独的进程中,并将控制信号添加到您的大型 FSM。

    when T_block_1 =>
      ReadAddressCounter_en  <= '1';
    

    就我个人而言,我使用 _rst、_en、_us、_inc、_dec 之类的前缀作为计数器控制信号。 _us 代表无符号,因为我的计数器是无符号计数器,如果需要,该值将转换为 std_logic。

    FSM“遥控”信号的反例:

    process(Clock)
    begin
      if rising_edge(Clock) then
        if ((Reset or MemAddressCounter_rst) = '1') then
          MemAddressCounter_us      <= (others => '0');
        else
          if (MemAddressCounter_load = '1') then
            MemAddressCounter_us    <= to_unsigned(5, MemAddressCounter_us'length);
          elsif (MemAddressCounter_inc = '1') then
            MemAddressCounter_us    <= MemAddressCounter_us + 1;
          end if;
        end if;
      end if;
    end process;
    
    MemAddress  <= std_logic_vector(MemAddressCounter_us);
    
  • 第 286 行不应该是可综合的(我认为 Xilinx XST 不够聪明,如果是这样,那么 XST 会产生什么是不可预测的......)

    dataIn<=dataOut(39 downto conv_integer(T_left_most+1))&T_square&T_square&T_square&dataOut(conv_integer(T_right_most-1) downto 0);
    

那么你如何修复我的提示列表中的最后一个点呢?

  1. 简单方法:使用 40:1 多路复用器
  2. 更高级的方法:尝试使用掩码和位移操作
  3. 使用另一种数据结构,更适合硬件:)

如果您想对此点进行更详细的讨论,请发表评论,我将尝试扩展我的答案。

扩展答案-位运算解决方案

让我们总结一下第 286 行:

dataIn <= dataOut(39 downto conv_integer(T_left_most+1)) &
          T_square & T_square & T_square &
          dataOut(conv_integer(T_right_most-1) downto 0);

您的结果由 3 部分组成:

  • 数据输出从 MSB 切分到动态标记
  • 3 位
  • 数据输出从动态标记切分到 0

这种位插入也可以通过按位设置/清除操作来完成:

  • 设置:result &lt;= input or set_mask;
  • 清除:result &lt;= input and not clear_mask;

这可以组合成一个位覆盖操作:

  • 覆盖:result &lt;= (input and not override_mask) or override_value;
  • 安全覆盖result &lt;= (input and not override_mask) or (override_value and override_mask);

如果您不能假设 override_value 仅在 override_mask 范围内设置位,则必须使用后者。

下一步是将 T_square 移动到正确的位置。可以通过桶形移位器或乘法器(DSP 模块)快速移动位。因为您的向量有 40 位,所以桶形移位器比 DSP 模块更快。

64 位桶形移位器有一个 6 位 shift_value 输入。输入的每一位指定该值是否应移动 2 的幂。

entity arith_BarrelShifter is
  generic (
    BITS            : POSITIVE      := 32
  );
  port (
    Input           : in    STD_LOGIC_VECTOR(BITS - 1 downto 0);
    ShiftAmount     : in    STD_LOGIC_VECTOR(log2ceilnz(BITS) - 1 downto 0);
    Output          : out   STD_LOGIC_VECTOR(BITS - 1 downto 0)
  );
end;

architecture rtl of arith_BarrelShifter is
  constant STAGES       : POSITIVE      := log2ceilnz(BITS);

  subtype   T_INTERMEDIATE_RESULT is STD_LOGIC_VECTOR(BITS - 1 downto 0);
  type  T_INTERMEDIATE_VECTOR is array (NATURAL range <>) of T_INTERMEDIATE_RESULT;

  signal IntermediateResults    : T_INTERMEDIATE_VECTOR(STAGES downto 0);

begin
  IntermediateResults(0)    <= Input;
  Output                    <= IntermediateResults(STAGES);

  for i in 0 to STAGES - 1 generate
    process(IntermediateResults(i), ShiftRotate, LeftRight, ArithmeticLogic)
    begin
      if (ShiftAmount(i) = '0') then
        IntermediateResults(i + 1) <= IntermediateResults(i);
      else
        IntermediateResults(i + 1) <= IntermediateResults(i)((BITS - i**2 - 1) downto 0) & ((i**2 - 1) downto 0 => '0');                                                        -- SLA, SLL
      end if;
    end process;
  end generate;
end;

所以现在我们可以合并所有步骤:

override_value <= (39 downto 3 => '0') & (2 downto 0 => T_square);
override_mask  <= (39 downto 3 => '0') & (2 downto 0 => '1');

shift_value : entity PoC.arith_BarrelShifter
  generic map (
    BITS        => 40
  )
  port map (
    Input       => override_value,
    ShiftAmount => T_left_most,
    Output      => shifted_value
  );

shift_mask : entity PoC.arith_BarrelShifter
  generic map (
    BITS        => 40
  )
  port map (
    Input       => override_mask,
    ShiftAmount => T_left_most,
    Output      => shifted_mask
  );

--           clear bits from source   / set bits from value
dataIn <= (dataOut and not shifted_mask) or shifted_value;

希望我没有犯错:)

【讨论】:

  • 首先非常感谢您花费的时间。我想请你详细说明最后一点,你说我可以使用掩码和位移操作,我在某个时候尝试过这个,但遇到了一些麻烦,所以我放弃了这个想法,你能帮我看看吗请。>其次,考虑到常数 C_TOP_MOST : NATURAL := 0,我可以从 c_top_most 中添加或减去数字吗?
  • @DimitriC 我根据要求使用位操作解决方案扩展了我的答案。函数 log2ceilnz 计算二进制对数。
  • 再次非常感谢您。我根据您的建议,我更改了很多代码,并且管理了 21 个状态的相同功能。 (最初为 154)。现在综合问题已经开始了。当我合成我的代码时,它会永远挂在 hdl 细化上,并且无论有多少,它都会消耗所有可用的 ram。我什至尝试在具有 16 GB 内存的计算机上合成它,它消耗 15.2 克内存!
  • 您的设计中有多少个桶形移位器?你能把 40 位减少到 20 位吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多