我是VHDL新手,我正在努力解决以下问题。我想我仍然误解了VHDL中计数器和计时器的想法。我将用一个简单的闪烁LED二极管来解释它。 (顺便说一下,我正在学习Spartan-3E FPGA套件。)所以这是我的代码(我还没有使用重置)
entity top_level is
Port( clk : in STD_LOGIC;
led1 : out STD_LOGIC);
end top_level;
architecture Behavioral of top_level is
signal timer : STD_LOGIC_VECTOR(25 downto 0) := "00000000000000000000000000";
signal reset: boolean:= false;
begin
process (clk)
begin
led1 <= '1';
if clk='1' and clk'event then
if reset <= true then
timer <= (others => '0');
end if;
timer <= timer + 1;
end if;
if timer <= "11100100111000011100000000" then
led1 <= '0';
end if;
end process;
end Behavioral;
振荡器频率为50 MHz,因此一个周期为20 ns。如果我想用LED闪烁1.2秒(这使得60 000 000次循环),我需要创建一个26位向量。所以我创建了一个由clk更改触发的过程
我认为应该做什么:
第一行代码是对led1的逻辑1赋值。所以led1应该点亮,直到计数器不会计数6 000 000个周期。当计数器计数60 000 000个周期时,LED逻辑状态应切换为0,这意味着没有光。由于26位数的最大值为67 108 863,LED应点亮60 000 000个周期,并在剩余的7 108 863个周期内关闭。不是吗?
它做了什么:我的印象是,它有点逆转。 LED大部分时间处于关闭状态(67 108 063个周期),并且点亮7 108 863个周期。为什么会这样?我真的不明白。
其他问题:如何只运行一次/两次/ ...?例如。我想用我的LED闪烁3次然后关闭它。因为据我所知,在达到最大26位数后,时间向量将从头开始计数(从0开始)。等等,无休止地。
答案 0 :(得分:3)
首先,您应该将设计划分为多个部分,可以是实体或多个流程。我将使用流程或一些单行程。您还可以将呈现的函数提取到单独的包中。
我建议使用UNSIGNED类型作为计数器,因此为此类型定义了'+'运算符。
我将其命名为timer_us
,因此很容易看出此信号是无符号的。
此外,您也可以在声明中使用(others => '0')
。
signal reset : STD_LOGIC := '0'; -- disabled reset; TODO move this signal to port
signal timer_us : UNSIGNED(25 downto 0) := (others => '0');
signal timer_tick : STD_LOGIC;
process(clk)
begin
if rising_edge(clk) then
if (reset = '1') then
timer_us <= (others => '0');
else
timer_us <= timer_us + 1;
end if;
end if;
end process;
timer_tick <= '1' when (timer_us = to_unsigned(60 * 1000 * 1000), timer_us'length) else '0';
最后一行描述了一个三元运算符(如C中的z = a ? x : y
)。因此,如果计时器达到60,000,000,则滴答信号为“1”。但是有一个错误:你的计数器从0开始,所以max - 1
已达到60,000,000个周期。
更通用的计数器模板的一些改进:
timer_rst
TIMER_MAX
log2ceil
to_sl
这里是扩展的计数器代码:
signal reset : STD_LOGIC := '0'; -- disabled reset; TODO move this signal to port
function log2ceil(arg : positive) return natural is
variable tmp : positive := 1;
variable log : natural := 0;
begin
if arg = 1 then return 0; end if;
while arg > tmp loop
tmp := tmp * 2;
log := log + 1;
end loop;
return log;
end function;
function to_sl(condition : BOOLEAN) return STD_LOGIC is
begin
if condition then
return '1';
else
return '0';
end if;
end function;
constant TIMER_MAX : POSITIVE := 60 * 1000 * 1000 - 1;
constant TIMER_BITS : POSITIVE := log2ceil(TIMER_MAX);
signal timer_rst : STD_LOGIC;
signal timer_us : UNSIGNED(TIMER_BITS - 1 downto 0) := (others => '0');
signal timer_tick : STD_LOGIC;
timer_rst <= reset or timer_tick;
process(clk)
begin
if rising_edge(clk) then
if (timer_rst = '1') then
timer <= (others => '0');
else
timer <= timer + 1;
end if;
end if;
end process;
timer_tick <= to_sl(timer = TIMER_MAX - 1));
如果您对编码功能感兴趣,您还可以创建一个功能,将时间(0.833 Hz信号的周期)转换为给定频率50 MHz的周期计数;)
闪烁的LED是一个简单的T-FF:
signal blink : STD_LOGIC := '0'; -- blink will be mapped to a FF, so initialize it
process(clk)
begin
if rising_edge(clk) then
if (timer_tick = '1') then
blink <= not blink;
end if;
end if;
end process;
LED <= blink;
或作为单行(不需要处理块):
blink <= blink xor timer_tick when rising_edge(clk);
LED <= blink;
所以现在你拥有了构建计数器的所有工具。您可以实现第二个计数器从0到2的计数。如果达到2,您可以设置一个控制信号counter2_ctrl
,然后反馈到闪烁过程以阻止此过程切换。
这里是扩展的单行:
blink_enable <= not counter2_ctrl;
blink <= blink xor (timer_tick and blink_enable) when rising_edge(clk);
反向LED输出可能是由测试板上的低有效电路引起的。