此次实例中,使用的是小梅哥的xilinx a7系列200t的开发板,其中自带一个usb2.0模块,然后根据他的基于slave fifo的fpga数据回环实验改编,主要是小梅哥的例程是基于verilog的,以及是自己编写的一个fifo模块,然后我改成了基于vhdl以及fifo ip核的工程,有需要的小伙伴可以参考参考。
1 首先是根据小梅哥的例程,改成vhdl程序
1.1 第一点,是对fifo0_rd_en ,fx2_slwr,fx2_fdata_out的使能不一样
--fifo0_rd_en <= ((not fx2_slwr_reg ) and (not fifo0_empty ));
--之所以不像小梅哥里面写的去使能fifo0的读使能,是因为小梅哥自己写的fifo好像不会存在延时,所以他的fx2_slwr和fifo_rd(fifo_pop)是同时有效的,根据fx2的原理可以知道,当fx2_slwr为0时,就可以将fifo里面相应的数据传输给fx_data,然后发送到上位机,这个前提是fx2和fifo的时钟是一致的。因为fx2用的是同步(同步就是在IFCLK时钟沿进行数据读写,SLWR和SLRD是作为使能信号。异步就是在SLWR和SLRD下降沿进行数据读写,不需要IFCLK。),而fifo也是根据时钟信号去实现功能的。
--------------------------------------------写一下关于时钟的影响-------------------------------------------------------
在小梅哥的usb模块当中使用的fx2,并且配置成了同步传输,所以最好是关于使用该部分的数据,都使用该时钟,不然会有一些问题。
首先我们来看一下fx2的同步传输的时序图:数据都是使能信号有效时,然后在ifclk的上升沿的时候进行传输的
然后我们来看一下fx2的异步传输的时序图:数据都是使能信号的下降沿时进行传输的
下面我们来看一下,如果对传输数据使用不同时钟去使能传输的话,我们收到的数据有什么区别
第一种,我们任然使用fx的输出时钟(这里配置的是48MHZ),然后可以看到上位机收到的数据和发出的数据是一样的:
第二种,我们使用fpga的输出时钟(这里配置的是50MHZ),然后可以看到上位机收到的数据和发出的数据就会有一些误差的:就是有的数据可能会持续两三个时钟
第二种,我们使用2倍的ifclk的输出时钟(这里配置的是96MHZ),然后可以看到上位机收到的数据和发出的数据就会有一些误差的:就是每个数据会持续两个时钟
---------------------------------------------------------end----------------------------------------------------------------------
基于 FPGA 的 SlaveFIFO 回送测试
基于 SlaveFIFO 接口的数据回环测试,能够实现 PC 通过 FX2 发往 FPGA 的数据再通
过 FX2 芯片发回到 PC。整个系统的数据链路如下所示。
我们将输入数据从上位机传输给fpga的fifo,缓存之后就可以接收数据,进行处理,最后再把结果送给fxdata,就可以通过fx2的ep6端点把数据传输到上位机了。
调试过程中可能遇到的问题:
第一个问题,可能datain可以收到fxdata的数据,但是dataout一直没有数据
可能是fpga缓存的fifo没有进行读使能,或者使用fifo ip核的时候设置了复位,但那是高电平复位,而fx2的芯片是低电平复位,所以要注意一下这点
第二个问题,fxdata收到的数据跟fifodataout的数据不一致,是因为小梅哥的程序里面是自己写的fifo,那么他的fxslwr和fiforden是同时有效的,而且dataout和rden也是一致的。
而正常使用fifo时,会信号延迟,首先当rd_en拉高之后,是要等下一个时钟来时,才会有数据读出,即rd_en下一个时钟,vaild和data_out才有效,所以根据上面的原理,因为是把fifo的data_out给fxdata,所以要等到vaild有效时才能去使能fx2_slwr
新增:其实通过先通过rd_en再去使能slwr,还存在一个问题,就是slwr有效之后,数据才会写入usb,然后flagc的full信号才会有反应,但是这个过程中,rd_en还在使能,所以在这个间隙的时候,就会造成数据丢失,将这部分程序改一下:(2022.11.13)
process (fx2_ifclk,rst_n,fx2_flagc,fifo2_empty,state_c) --usb in状态机
begin
if(rst_n = '0') then --低电平复位
state_c <= s0_wait_flagc;
elsif(rising_edge(fx2_ifclk)) then
case state_c is
when s0_wait_flagc =>
if (fx2_flagc = '1') then
state_c <= s1_wirte;
else
state_c <= s0_wait_flagc;
end if;
when s1_wirte =>
if ((fx2_flagc = '0') or (fifo2_empty='1')) then --or (fifo0_empty='1')
state_c <= s0_wait_flagc;
else
state_c <= s1_wirte;
end if;
when others =>
state_c <= s0_wait_flagc;
end case;
end if;
end process;
--------------------------------------------------------------------------------------------------------------------------------------------------------------
--因为fxdata要在slwr为0且flagc为1时输出,但是fxdata的数据又是从fifo当中读出来的,读的时候会有时间的延迟,也就是要在valid为1时,数据才有效,也就是在valid为1时,slwr和fxdata的使能,但又要保证在flagc为1的情况下,所以就可以在flagc为1时,使能rd_en,然后马上拉低,在输出fxdata之后,再去判断flagc有没有满,然后再决定是否使能rd_en。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
process (fx2_ifclk,rst_n,state_c,fx2_flagc,fifo2_empty,cnt2,state1) --fifo2_rd_en
begin
if(rst_n = '0') then --低电平复位
state1 <= s0;
fifo2_rd_en <= '0';
cnt2 <= (others => '0');
elsif(rising_edge(fx2_ifclk)) then
case state1 is
when s0 =>
fifo2_rd_en <= '0';
if ((state_c = s1_wirte) and (fx2_flagc = '1') and (fifo2_empty='0')) then --or (fifo0_empty='1')
state1 <= s1;
else
state1 <= s0;
end if;
when s1 =>
if ((state_c = s1_wirte) and (fx2_flagc = '1') and (fifo2_empty='0')) then --or (fifo0_empty='1')
fifo2_rd_en <= '1';
cnt2 <= cnt2 +'1';
state1 <= s2;
else
fifo2_rd_en <= '0';
cnt2 <= cnt2 ;
state1 <= s0;
end if;
when s2 =>
fifo2_rd_en <= '0';
state1 <= s0;
when others =>
state1 <= s0;
end case;
end if;
end process;
process(fifo2_valid) --fx2_slwr_reg fx2_fdata_out
begin
if( fifo2_valid='1' ) then --not full
fx2_slwr_reg <= '0';
fx2_fdata_out <= fifo2_data_out(15 downto 0);
else
fx2_slwr_reg <= '1';
fx2_fdata_out <= (others =>'Z');
end if;
end process;
第三个问题,fifodatain 和fifo dataout都是0,没有收到fxdata,可能是因为process里面的敏感信号没有写完整,因为小梅哥的程序里面是用的verilog,always(*),他就把所有敏感信号都包含了,但是vhdl里面不可以这样写,我就只写了输入的影响信号,比如我没有写fx2_fdata,结果就是下面这样
process(fx2_slrd_reg)
begin
if(fx2_slrd_reg = '0') then
fifo_data_in <= fx2_fdata;
else
fifo_data_in <= (others => '0');
end if;
end process;
添加上fx2_fdata敏感信号之后,就可以传输正常了:
process(fx2_slrd_reg,fx2_fdata)
begin
if(fx2_slrd_reg = '0') then
fifo_data_in <= fx2_fdata;
else
fifo_data_in <= (others => '0');
end if;
end process;
2.更改ila的部分
vhdl的语法会比verilog严格很多,例如同样是ila调用,verilog可以直接使用,但是vhdl里面由于ila ip核会把只有一位的信号定义成STD_LOGIC_VECTOR(0 DOWNTO 0),而我们一般在程序里面会直接定义成STD_LOGIC,这就导致了不能直接观测改信号,会报错说信号类型不一致。那么有两种解决办法,一是将程序中要观测的信号都改成STD_LOGIC_VECTOR(0 DOWNTO 0),但是这很麻烦,因为要将该信号的赋值都改成“0”或“1”,就是要把单引号都改成双引号;二是重新定义一个信号,即st_ila :STD_LOGIC_VECTOR(0 DOWNTO 0); st :STD_LOGIC; st_ila <= conv_std_logic_vector(st,1); 然后观测时,连接到st_ila就可以了,其中conv_std_logic_vector函数要包含一个库文件use ieee.std_logic_arith.all;
具体的代码分析,我录了一个是视频,大家感兴趣的话,可以看看:基于 FPGA 的 SlaveFIFO 回送测试(VHDL)_哔哩哔哩_bilibili
(ps:不管是fpga烧录eprom文件 还是fx2烧录eprom文件 烧录完成后 一定要掉电 才会进行相应功能)