我在VHDL中实现了一个简单的SPI主控。我面临的问题是,在合成期间,为ss
和data_rdy
创建了两个触发器。我认为总是指定那些端口的输出,为什么这些寄存器被创建,我怎么能摆脱它们呢?
我的代码如下,其中省略了ss
或data_rdy
的状态。
实体:
library IEEE;
use IEEE.std_logic_1164.ALL;
entity mcu is
port(clk : in std_logic;
res : in std_logic;
pc : in std_logic_vector(7 downto 0);
pc_new : in std_logic;
data_ack : in std_logic;
miso : in std_logic;
data : out std_logic_vector(12 downto 0);
data_rdy : out std_logic;
mosi : out std_logic;
sclk : out std_logic;
ss : out std_logic);
end mcu;
架构:
library IEEE;
use IEEE.std_logic_1164.ALL;
architecture behaviour of mcu is
-- r: send read command
-- a: send address
-- rx: receive data
type state_t is (r0, r1, r2, r3, r4, r5, r6, r7,
a0, a1, a2, a3, a4, a5, a6, a7,
rx0, rx1, rx2, rx3, rx4, rx5, rx6, rx7, rx8, rx9, rx10, rx11, rx12,
idle, starting, done);
signal state : state_t := idle;
signal datasig : std_logic_vector(12 downto 0);
begin
sclk <= clk;
mosi <= datasig(12);
sync : process(clk) is
begin
if rising_edge(clk) then
data_rdy <= '0';
ss <= '0';
if res = '1' then
state <= idle;
else
datasig <= datasig(11 downto 0) & miso;
if pc_new = '1' then
state <= starting;
else
case state is
when idle =>
ss <= '1';
datasig <= (others => '0');
state <= idle;
...
when rx12 =>
data <= datasig;
data_rdy <= '1';
state <= done;
when done =>
if data_ack = '1' then
state <= idle;
else
state <= done;
end if;
end case;
end if;
end if;
end if;
end process sync;
end behaviour;
相关的合成器输出:
===============================================================================
| Register Name | Type | Width | Bus | MB | AR | AS | SR | SS | ST |
===============================================================================
| data_rdy_reg | Flip-flop | 1 | N | N | N | N | Y | N | N |
| ss_reg | Flip-flop | 1 | N | N | N | N | Y | N | N |
| data_reg | Flip-flop | 13 | Y | N | N | N | N | N | N |
| state_reg | Flip-flop | 3 | Y | N | N | N | N | Y | N |
| state_reg | Flip-flop | 2 | N | N | N | N | Y | Y | N |
| datasig_reg | Flip-flop | 13 | Y | N | N | N | N | N | N |
===============================================================================
另外,为什么state
分成两个寄存器?
答案 0 :(得分:1)
注册输出通常是一件好事:它允许更好的定义时序,这可以转化为更可靠的操作,尤其是对于SPI外设等外部设备。
您遇到的问题(我认为)当您希望每当您处于某种状态时输出为真时,在您处于该状态时(在时钟进程内)分配该输出将引入一个时钟周期延迟。
David Koontz给出了一个答案:将所有分配移动到时钟部分的输出 out - 或者实际上完全取决于该过程,仅取决于“状态”信号。
但另一种方法是保持寄存器的清洁时序,并且仍然消除循环延迟。 这是为了将输出分配提前到任何状态,将转换为该状态,或者(一旦进入)状态不会转换出状态。例如,修改
when rx12 =>
data <= datasig;
data_rdy <= '1';
state <= done;
并转换到状态rx12
when rx11 =>
if ready_to_go_to_rx12 then
data_rdy <= '1'; -- keep data_rdy synched with rx12
state <= rx12;
end if;
when rx12 =>
data <= datasig;
-- data_rdy <= '1'; -- no, because we leave state rx12 immediately
state <= done;
同样适用于具有相同问题的其他输出。
很可能这不会产生额外的逻辑,因为合成工具通常可以识别出data_rdy
和rx12
总是同时被断言,并为它们共享一个寄存器。
data <= datasig;
也被注册了,因此也是一个周期晚了:虽然我只考虑data_rdy
作为你提到的信号之一,但你需要考虑是否还应推进其他任务,如data <= datasig;
。 (我猜是这样的:在新数据出现之前在循环中发出信号data_rdy
是违反直觉的!)
答案 1 :(得分:0)
我面临的问题是在合成期间,为ss和data_rdy创建了两个触发器。我认为总是指定那些端口的输出,为什么这些寄存器被创建,我怎么能摆脱它们呢?
此处创建了data_rdy
和ss
的注册表:
if rising_edge(clk) then
data_rdy <= '0';
ss <= '0';
摆脱它们的方法只分配给评估data_rdy
的if语句之外的ss
和clk
。
另外,为什么州分为两个寄存器?
你有32个状态,所以5个触发器听起来正确。如果您查看报告中最后两列用于这两组触发器,您会发现其中三个是同步设置的,并且两组触发器都是同步重置的。
这是出现:
if rising_edge(clk) then
data_rdy <= '0';
ss <= '0';
if res = '1' then
state <= idle;
else
datasig <= datasig(11 downto 0) & miso;
if pc_new = '1' then
state <= starting;
else
case state is
when idle =>
ss <= '1';
datasig <= (others => '0');
state <= idle;
因为您的同步干扰状态为空闲,启动或使用由状态驱动的输入多路复用器。
如果你有能力看到映射设计的示意图,那将是有启发性的。