我正在尝试在VHDL中实现类似SPI接口的东西,以便与FPGA一起使用。
我对VHDL的理解是有限的,因为我只使用它2天,我想我还没有理解如何使用时钟边缘控制数据流。
我目前的理解是,您可以将VHDL视为过程编程语言,只要您希望在过程中执行的每条指令按顺序完成,每个时钟周期一条指令。
这可能听起来很奇怪,所以这就是我到目前为止所做的。有一个名为SPI_REG的寄存器,它包含8位数据,由外部时钟输入和输出。 (主从SPI设备时钟.FPGA是从设备。主设备是Arduino,Raspberry Pi,手机等。)
有一个串行输入和串行输出端口,它连接到主设备。 (MISO和MOSI数据线。)
有一个计数器,用于计算时钟数。它有8个时钟,从0到7,然后翻转。当主设备将8位数据输入寄存器时,该计数器激活一个标志。
FPGA内部时钟的运行速度比SPI外部时钟高得多(50 MHz?)。在内部时钟的每个上升沿,我们检查标志是否已设置,这意味着已经传输了8位数据,并且FPGA必须将此数据复制出来,对其执行某些操作并将一些返回数据重新放入下一个SPI时钟。因此,为什么内部时钟必须高得多。
这个“字节改组”过程必须分阶段完成。
总共有3个寄存器。 SPI寄存器,发送寄存器和接收寄存器。
为了测试程序,我决定填充发送寄存器,接收寄存器的内容与零字节进行异或。 “00000000”(这还没有“做”任何事情。)
我希望我解释得很好,这里有一些代码。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity interface is
port
(
-- SPI
SPI_MOSI: in std_logic;
SPI_MISO: out std_logic;
SPI_CLK: in std_logic;
-- FPGA clocks
SYSTEM_CLK: in std_logic;
);
end interface;
architecture Behavioral of interface is
-- SPI
signal SPI_REG: std_logic_vector(7 downto 0) := (others => '0');
signal SPI_BUFFER_OUT: std_logic_vector(7 downto 0) := (others => '0'); -- Holds most recent 8 bits transferred over SPI
signal SPI_BUFFER_IN: std_logic_vector(7 downto 0) := (others => '0'); -- Holds next 8 bits to be transferred over SPI
-- Shift register data
--DATA_IN: in std_logic_vector(7 downto 0) := (others => '0');
--DATA_OUT: out std_logic_vector(7 downto 0) := (others => '0');
begin
-- SPI interface
process(SYSTEM_CLK, SPI_CLK)--, AUDIO_CLK, BUSY)
-- SPI interface
variable spi_clk_count: integer := 0; -- count 8 data clocks
variable system_clk_count: integer := 0; -- count system clocks for moving data out and in to spi register
variable system_clk_flag: boolean := false;
variable send_data_count: integer := 0;
-- ADC control
variable read_data_clock_count: integer := 0; -- count clock pulses for timing the reading of the ADC
variable convst_flag: integer := 0; -- flag for the convst signal to the ADC
begin
-- SPI external clock
if rising_edge(SPI_CLK) then
-- Clock data out of serial_out (MISO or MOSI)
if system_clk_flag = false then
-- Complete transfers
SPI_MISO <= SPI_REG(7);
-- Clock data in spi_reg along 1 bit and feed in 1 bit from serial_in (MOSI or MISO)
SPI_REG <= SPI_REG(6 downto 0) & SPI_MOSI; -- Concatinate bits
end if;
-- Check for full 8 bits
if spi_clk_count = 7 then
spi_clk_count := 0;
-- Signal system clock to move byte out of and byte in to register
if system_clk_flag = false then
-- Set buffer overrun error flag
-- Signal data to be clocked out and clocked in anyway
system_clk_flag := true;
end if;
else
-- Increment spi clock counter
spi_clk_count := spi_clk_count + 1;
end if;
end if;
-- System internal clock
if rising_edge(SYSTEM_CLK) then
-- To test, return the XOR of the bits with zero (do nothing)
-- Continue shuffling data if required
if system_clk_count = 1 then
-- Increment system_clock_count
-- Change flag to "step 2"
system_clk_count := 2;
-- This is for debugging purposes
SPI_BUFFER_IN <= SPI_BUFFER_OUT xor "00000000";
elsif system_clk_count = 2 then
-- Increment system_clock_count
-- Change flag to "step 3"
system_clk_count := 3;
-- Move data from SPI_BUFFER_IN to register
SPI_REG <= SPI_BUFFER_IN;
elsif system_clk_count = 3 then
-- Signal that we are done
system_clk_flag := false;
-- Reset clock count
system_clk_count := 0;
-- Check for signal for system clock to move data to and from SPI register
elsif system_clk_flag = true then -- This must be done here because it is the lowest priority of the statements
-- Signal for byte shuffling, clock data in and out
system_clk_count := 1;
-- Move data from SPI register to SPI_BUFFER_OUT register
SPI_BUFFER_OUT <= SPI_REG;
end if;
end if;
end Behavioral;
我有一种感觉,我构建它的方式是完全错误的。我可以绘制一个可以(我认为)工作的电路图,但是我在使用VHDL设计一些东西时遇到了麻烦。
它没有语法错误,但不能合成。非常感谢任何帮助。
答案 0 :(得分:0)
某些综合工具在同一过程中不支持多个时钟。修复也会指出您正在尝试使用两个时钟加载SPI_REG。
作为建议,对每个时钟域使用一个进程,不要使用共享变量,不要使用标志。您需要跨时钟域握手的唯一地方是SPI_BUFFER_OUT。 SPI_BUFFER_IN由系统时钟加载,_OUT由SPI时钟加载。
注意由时钟边界交叉握手引起的亚稳态。应该只对具有更高速度时钟和/或异步操作的系统。没有定时模型就无法模拟亚稳性。这种模式通常只是抱怨
使用异步系统接口,您可以使用单个锁存器来缓冲从系统端或SPI端加载的缓冲区。这将需要一些输入多路复用和OR两个使能一起。
如果您的系统时钟和SPI时钟源自更高速度的时钟并且彼此之间具有整数关系,则可以使用时钟使能和更高速度的时钟。