我正在尝试使用缓冲区实现SPI主模块。我使用此FSM模块对其进行测试,并通过UART将接收到的数据传输到我的串行控制台。
library IEEE;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
entity FSM_SPI_buf is
Port ( clk: in STD_LOGIC;
increase: in STD_LOGIC;
reset: in STD_LOGIC;
busy : in STD_LOGIC;
tx : out STD_LOGIC_VECTOR (7 downto 0);
rx : in STD_LOGIC_VECTOR (7 downto 0);
transmit : out STD_LOGIC;
loadFromRXBuf : out STD_LOGIC;
loadToTxBuf : out STD_LOGIC;
rxBufEmpty : in STD_LOGIC;
rxBufFull : in STD_LOGIC;
txBufEmpty : in STD_LOGIC;
txBufFull : in STD_LOGIC;
led: out STD_LOGIC_VECTOR (7 downto 0);
uartTXData: out STD_LOGIC_VECTOR(7 downto 0);
uartRXData: in STD_LOGIC_VECTOR(7 downto 0);
uartTXSig: out STD_LOGIC;
uartTXRdy: in STD_LOGIC ;
uartRXCont: out STD_LOGIC;
uartRXSig: in STD_LOGIC;
uartRXFrameError: in STD_LOGIC
);
end FSM_SPI_buf;
architecture Behavioral of FSM_SPI_buf is
type statex is (start,updateTX, closeTX, send,openRX, receive, closeRX,
sendUART,closeUART, stop);
signal state: statex:=start;
signal counter: integer range 0 to 5 := 0;
signal bytes:STD_LOGIC_VECTOR(31 downto 0) := x"030001FF";
signal bytes_rec:STD_LOGIC_VECTOR(31 downto 0):=x"03040506";
begin
process(clk, reset) begin
if(clk'event and clk = '1') then
case state is
when start =>
if(increase = '0') then
state <= updateTX;
counter <= 0;
uartrxCont <= '1';
else
state <= start;
end if;
when updateTX =>
if(counter < 4) then
loadToTxBuf <= '1';
tx<=bytes(31 - counter * 8 downto 32 - (counter+1) * 8);
counter <= counter + 1;
state <= closeTX;
else
state <= send;
end if;
when closeTX =>
loadToTxBuf <= '0';
state <= updateTX;
when send =>
transmit <= '1';
counter <= 0;
if (rxbuffull = '1') then
state <=openRX;
end if;
when openRX =>
transmit <= '0';
if(counter < 4) then
loadFromRxBuf <= '1';
state <=closeRX;
else
counter <= 0;
state <= sendUART;
end if;
when closeRX =>
loadFromRXBuf <= '0';
state <= receive;
when receive =>
bytes_rec(31 - (counter) * 8 downto 32 - (counter+1) * 8)<=rx;
counter <= counter + 1;
state <= openRX;
when sendUART =>
if(counter < 4) then
if uarttxRdy = '1' then
uarttxData <=bytes_rec(31 - (counter) * 8 downto 32 - (counter+1) * 8);
uarttxSig <= '1';
counter <= counter + 1;
state <= closeUART;
end if;
else
state <= stop;
end if;
when closeUART =>
if (uarttxRdy= '0') then
uarttxSig <= '0';
state <= sendUART;
else
state <= closeUART;
end if;
when stop =>
if (uarttxRdy= '0') then
uarttxSig <= '0';
end if;
if(increase = '1') then
state <= start;
else
state <= stop;
end if;
end case;
elsif(reset = '1') then
counter <= 0;
state <= updateTX;
end if;
end process;
end Behavioral;
以下是SPI模块的摘录,其中我移出了接收的字节
if(rx_upd <='0' and loadFromRxBuf ='1') then
rx_upd <='1';
rx <= rx_buffer(d_width*buffer_size-1 downto d_width*(buffer_size-1));
rx_buffer<= rx_buffer(d_width*(buffer_size-1)-1 downto 0) & x"00";
elsif(rx_upd ='1' and loadFromRxBuf ='0') then
rx_upd <='0';
end if;
通过模拟来判断在发生UART传输之前,bytes_rec从其起始值转换为x“FFFFFFFF”(味噌总是很高)。
但是当我将生成的位文件上传到我的FPGA(Mojo Board v3上的XC6SLX9)时,即使我将味噌连接到3.3v源,我也只通过UART接收零。我通过发送信号“bytes”检查了我正在使用的UART实现,它工作正常,所以我认为不应该责怪。
这是我第一次编程FPGA,如果你不计算我复制的一些教程,所以我希望错误归因于此。 但请指出可能的来源。如果需要,我可以提供我的代码的其他部分。 先感谢您!
答案 0 :(得分:0)
这是非常好的代码,总的来说,你通过将它写成具有正确灵敏度列表的单个进程状态机来躲过很多潜在的子弹。
我做了一个观察,UART的发送器和接收器一半通常是不同进程中的独立状态机,以允许全双工通信。但是,我不清楚这是否实际上是状态名称建议的UART,或SPI(同步)接口,以便观察可能不相关。
但是,有一个至关重要的错误,至少适用于increase, rxBufFull, uarttxRdy
,可能还有其他错误。
让我们看看closeUart
州。
if uarttxRdy = '0' then
uarttxSig <= '0';
state <= sendUART;
-- else
-- state <= closeUART;
end if;
(不是主要观点,但我已经注释掉了冗余的行,因为state
的当前值被保留了 - 并且不必要的代码通常是一个坏主意,如果只是添加详细程度和杂乱。我还清理了布尔表达式周围的C风格线噪声
这里的错误是uarttxRdy
显然是FPGA的外部输入,未与clk
同步。
因此,信号时序和同步设计的通常保证,如described here,并不适用于它。
所以你期望这个陈述有两个结果:没有任何反应,或者uarttxRdy
被清除,你开始发送。
但是想象一下,合成过程会产生非常合理的转换(将逻辑与保存门相结合,或缩短关键信号路径):
if uarttxRdy= '0' then
uarttxSig <= '0';
end if;
if uarttxRdy= '0' then
state <= sendUART;
end if;
从输入引脚到每个比较的信号延迟是不同的。
我认为您可以看到现在有四种可能的结果,包括在不清除uarttxSig
的情况下开始发送,或清除它并保持此状态至少一个时钟周期。这些中的任何一个都可以使你的状态机搞砸了。
因此很清楚异步输入需要特殊处理才能将它们带入同步域。
只需在时钟边缘复制它们,然后使用同步副本。您可以在单独的同步过程中执行此操作,但添加&#34;默认操作&#34;也是非常安全和正常的做法。在状态机中的Case语句之前。例如:
if rising_edge(clk) then
-- default actions : synch external inputs
uarttxRdy_s <= uarttxRdy;
-- state machine
case state is
when closeUART =>
if uarttxRdy_s = '0' then
此问题经常被错误标记&#34; metastability&#34;但它并不是:它只是不同信号路径长度对异步输入的不同用途的结果。
当异步输入到达(几乎)与时钟边缘相同的时刻(在飞秒内)时发生亚稳定性,因此寄存器看不到有效的&#39; 0&#39;或者&#39; 1&#39;逻辑级别,并且非级别锁存。这就像在销钉点上平衡滚珠一样,球会迟早掉下来。
使用当前的FPGA,其概率极小,但如果您仍然担心,请在每个外部信号上添加第二阶段的同步。