两个VHDL进程是否同步?如何生成无门控50%占空比慢时钟
从12MHz时钟生成1MHz 50%占空比时钟的设计疑问与优化方案
核心问题解答
1. 50%占空比慢时钟的生成方法
要从12MHz时钟生成1MHz、50%占空比的慢时钟且不使用门控时钟,核心逻辑是通过计数器跟踪快时钟周期,每经过固定数量的快时钟周期翻转慢时钟电平:
- 1MHz时钟周期为1μs,对应12MHz快时钟的12个周期(12*(1/12μs)=1μs)。
- 要实现50%占空比,慢时钟高低电平需各占6个快时钟周期,因此计数器计数到5(从0开始)时翻转电平,完成半周期;再计数6次后再次翻转,形成完整周期。原代码中
SCLK_COUNTER_MAX := 5的设计完全符合这个逻辑。
2. 原代码的时钟域疑问
原代码中sm_proc以slow_clk_q作为敏感信号,确实会创建新的时钟域:
- 尽管
slow_clk_q由12MHz时钟同步生成,但FPGA/ASIC工具会将所有用作触发信号的信号视为独立时钟。这会导致sm_proc所在逻辑被归类到slow_clk_q时钟域,而slow_clk_proc属于12MHz时钟域,两者构成跨时钟域设计。 - 即便理论上两个时钟完全同步,实际综合、布局布线时仍可能出现时序违规(如建立/保持时间不满足),增加设计风险,因此这种方式不适合用于状态机这类同步逻辑。
优化方案:单时钟域+时钟使能设计
修改后的代码采用**时钟使能(clk_en)**方案,将所有逻辑统一到12MHz时钟域,彻底规避跨时钟域问题,同时满足功能需求:
设计思路
- 用
clk_en脉冲替代慢时钟触发:每12个12MHz时钟周期生成一个高电平脉冲,状态机仅在clk_en='1'时更新状态,等效于1MHz的运行频率。 - 独立生成给DAC的1MHz 50%占空比
spi_clk,逻辑与原慢时钟生成一致,通过计数器每6个快时钟周期翻转一次电平。 - 可选优化:
clk_en_proc和spi_clk_proc可合并为一个进程,共享计数器资源——当计数器到5时翻转spi_clk,到11时置位clk_en,减少逻辑资源占用。
初始代码
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity spi_dac is port ( clk : in std_logic; slow_clk : out std_logic; data_out : out std_logic := '0' ); end spi_dac; architecture behavioral of spi_dac is signal slow_clk_q : std_logic := '0'; constant SCLK_COUNTER_MAX : positive := 5; signal sclk_counter : natural range 0 to SCLK_COUNTER_MAX := 0; begin sm_proc: process(slow_clk_q) begin if rising_edge(slow_clk_q) then -- State machine (hopefully) synchronous to 'clk' end if; end process; slow_clk_proc: process(clk) begin if rising_edge(clk) then if sclk_counter < SCLK_COUNTER_MAX then sclk_counter <= sclk_counter + 1; else sclk_counter <= 0; slow_clk_q <= not slow_clk_q; end if; end if; end process; slow_clk <= slow_clk_q; end behavioral;
修改后代码
library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity spi_dac is port ( clk : in std_logic; spi_clk : out std_logic; data_out : out std_logic := '0' ); end spi_dac; architecture behavioral of spi_dac is signal clk_en : std_logic := '0'; constant EN_COUNT_MAX : positive := 11; signal en_counter : natural range 0 to EN_COUNT_MAX := 0; type state_type is (STATE_0, STATE_1); signal sm_state : state_type := STATE_0; constant SPI_CLK_COUNT_MAX : positive := 5; signal spi_clk_counter : natural range 0 to SPI_CLK_COUNT_MAX := 0; signal spi_clk_q : std_logic := '0'; begin sm_proc: process(clk) begin if rising_edge(clk) then if clk_en = '1' then -- Update state machine at 1/12 the speed of 'clk' case sm_state is when STATE_0 => data_out <= '1'; sm_state <= STATE_1; when STATE_1 => data_out <= '0'; sm_state <= STATE_0; end case; end if; end if; end process; clk_en_proc: process(clk) begin if rising_edge(clk) then clk_en <= '0'; if en_counter < EN_COUNT_MAX then en_counter <= en_counter + 1; else en_counter <= 0; clk_en <= '1'; -- Pulse 'clk_en' high for one clock cycle end if; end if; end process; spi_clk_proc: process(clk) -- Toggle 'spi_clk' at 1 MHz and 50% duty cycle begin if rising_edge(clk) then if spi_clk_counter < SPI_CLK_COUNT_MAX then spi_clk_counter <= spi_clk_counter + 1; else spi_clk_counter <= 0; spi_clk_q <= not spi_clk_q; end if; end if; end process; spi_clk <= spi_clk_q; end behavioral;
内容的提问来源于stack exchange,提问作者user2105392
相关产品推荐
相关产品推荐

