当前位置:   article > 正文

FPGA与单片机之间的数据通信_fpga处理fft发送给单片机

fpga处理fft发送给单片机

目录

一、FPGA简介

二、FPGA与单片机之间的通信协议制定

1.通信系统框图

2.通信时序图        

三、实例

总结


一、FPGA简介

          写了这么久的Verilog代码,还没有系统的总结过到底什么是FPGA,它相对于单片机、DSP到底有哪些优势?这里说一下我个人的看法。

          FPGA(Field Programmable Gate Array,简称 FPGA),译文:现场可编程门阵列,一 种主要以数字电路为主的集成芯片,它内部主要是通过无数个门电路搭建起来的,它的优势在于利用硬件并行执行,打破了顺序执行的模式,在每个时钟周期内完成更多的处理任务,超越了数字信号处理器(DSP)的运算能力。这里简单的说一下流水线并行,比如一个工厂生产一件产品,需要10个步骤,如果采用流水线的生产模式,其所有步骤同时执行,那么平均下来,只需要生产该产品一个步骤的时间就可以生产出这一件产品,这就是它的优势所在。同一时刻,不同级在处理不同的数据,那么下一个时刻,就会输出相应的数据,这就是FPAG的优势所在。其次,我们都知道CPU、GPU 其内部构成都属于冯·诺依曼结构,将输入的机器语言翻译成指令后进行执行,同时为了与各个执行单元之间进行通信,还需要一定的内存共享,而FPGA 本质上是无指令、无需共享内存,它是根据硬件描述语言(Verilog)来生成相应的逻辑电路,从而进行驱动控制,大大降低了执行时间。因此相对于大多数的处理芯片来说,FPGA一个最大的优势就是数据处理的速度快,延迟小。

          对于FPGA的应用呢,个人感觉会偏向于通信和算法两方面。首先呢,我们都知道通信领域需要高速的通信协议处理方式,尤其是5g的使用,自动驾驶、智能家居等;另一方面呢通信的协议可能根据不同的要求随时都在修改,不适合做成专门的芯片,所以能够灵活改变的功能的FPGA就成了首选。而对于算法,这就更显而易见了,FPGA能够处理多维信号,满足实时性的要求,在图像处理、信号处理等领域将会有更广阔的应用前景。

         当然啦,上述讨论的都是FPGA未来的发展,而我目前对于FPGA的学习主要还是以实验室的一些项目为主,大部分项目还是通过FPGA进行数据采集,同步采集多路信号,并进行实时的传输,而涉及到的一些控制领域的信号主要还是通过单片机来实现的,比如通过单片机驱动电机、驱动网卡等,那么这就避免不了FPGA与单片机之间进行通信,而它们两者之间的通信,我们是设计了属于他们两者之间特有的通信协议来实现的,下面就详细的介绍一下,它们之间通信的实现过程。

二、FPGA与单片机之间的通信协议制定

        FPGA与单片机在硬件电路上一般采取20~24根线进行连接,我们主要是通过控制这20根数据线来实现两者之间的数据的并行通信。通常而言,单片机作为整个系统的主机,FPGA作为整个系统的从机,因此要实现将FPGA的数据传给单片机,则需要单片机来进行读操作,FPGA完成相应的数据发送;要实现单片机将数据传给FPGA,则需要单片机进行写操作,FPGA完成相应的数据接收。

      接下来我们对这20根数据线进行命名:

      mcu_wr:该信号是单片机发给FPGA的写请求信号

      mcurd_flag:该信号是FPGA发给单片机的读请求信号

      mcu_clk:该信号是单片机发送给FPGA读或写的时钟信号

      mcu_en:该信号是单片机发送给FPGA读或写的使能信号

      mcu_data[15:0]:该信号是FPGA与单片机之间进行数据通信的数据信号(双向)

1.通信系统框图

       如下图所示:

2.通信时序图        

         通信协议制定:

      (1).FPGA向单片机发送数据,即单片机读数据

           首先,FPGA向单片机发送一个mcurd_flag信号,该信号持续一段时间的高电平(默认为低),即代表两者之间通信的开始,而单片机如果检测到该信号的上升沿则进入中断,向FPGA发送读取数据的使能信号mcu_en和时钟信号mcu_clk,FPGA根据接收到的使能和时钟信号将所要发送的数据传递到数据线mcu_data[15:0]上。信号mcu_en、mcu_clk默认状态都为高电平,其时序图如下:

     (2).单片机向FPGA发送数据,即单片机写数据

         首先,单片机向FPGA发送一个mcu_wr信号,该信号持续一段时间的高电平(默认为低),即代表两者之间通信的开始,同样FPGA如果检测到该信号的上升沿则进入数据接收状态,根据单片机发来的数据使能信号mcu_en、数据时钟信号mcu_clk、数据信号mcu_data来进行数据的接收。其时序图如下:

三、实例

       要求:FPGA将外接采集到的数据存入RAM中,为了同时便于单片机读取FPGA采集到的数据,FPGA内部采用两块RAM,通过乒乓操作分别对两块RAM进行不同时刻的读写操作。同时单片机也可以在读取完数据后对FPGA进行写操作,即完成两者之间的半双工通信。

     其系统框图如下图所示:

        其中,ram_chioce信号表示选择两块ram的使能信号,ramend_flag信号表示ram已经写满,这两个信号由前一级模块给出。 

       接下来给出实验的Verilog代码:

  1. module fpga_mcu(
  2. input sys_clk,
  3. input sys_res,
  4. input ram_chioce, //选择两块ram(乒乓ram)的使能信号,低电平选择ram1,高电平选择ram2
  5. input ramend_flag, //某一块RAM写满的结束标志
  6. input [15:0] ram1_data, //ram1的输入的数据
  7. input [15:0] ram2_data, //ram2的输入的数据
  8. input mcu_clk, //单片机输入读或写数据的时钟(不发送时为高电平)
  9. input mcu_en, //单片机输入读或写数据的使能(低电平有效)
  10. input mcu_wr, //单片机输出写数据的标志(高电平有效),持续一段时间的高电平,代表即将向FPGA中写数据
  11. output reg [7:0] ram1_raddr, //输出读ram1的地址
  12. output reg [7:0] ram2_raddr, //输出读ram2的地址
  13. output reg mcurd_flag, //FPGA输出数据读取标志
  14. inout [15:0] mcu_data //FPGA与单片机之间通信的数据
  15. );
  16. parameter IDLE =6'b000001; //空闲状态
  17. parameter READ =6'b000010; //单片机读取数据状态
  18. parameter WAIT_WR =6'b000100; //等待单片机写数据
  19. parameter WRITE =6'b001000; //单片机写数据状态
  20. parameter STOP =6'b010000; //结束
  21. parameter addr_max =10-1; //最后一个数据地址+1(RAM的深度为10)
  22. parameter waittime =1000; //超时时间
  23. reg [7:0] ram_addr;
  24. reg [5:0] stata;
  25. reg [3:0] mcuclk_buff;
  26. reg [3:0] mcuen_buff;
  27. wire mcuclk_flag;
  28. wire mcuen_flag;
  29. reg [3:0] cnt_mcuflag; //对输出信号高电平的持续时间进行计数
  30. wire [15:0] mcu_rddata;
  31. //单片机写数据变量定义
  32. wire mcuwr_flag;
  33. reg [3:0] mcuwr_buff;
  34. reg [31:0] mcu_wrdata;
  35. reg [15:0] wr_data;
  36. reg wr_rd; //判断读写状态,默认低电平是单片机读
  37. //单片机读取FPGA发送过去的数据
  38. //对单片机输入的信号打两拍,消除亚稳态
  39. assign mcuclk_flag = mcuclk_buff[3] & (~mcuclk_buff[2]) ; //取下降沿,同时为了让时钟信号相对于使能信号滞后一个时钟周期
  40. assign mcuen_flag = mcuen_buff[1] & (~mcuen_buff[0]); //取下降沿
  41. always@(posedge sys_clk or negedge sys_res)
  42. if(!sys_res)begin
  43. mcuclk_buff <= 4'd0;
  44. mcuen_buff <= 4'd0;
  45. end
  46. else begin
  47. mcuclk_buff <= {mcuclk_buff[2:0],mcu_clk};
  48. mcuen_buff <= {mcuen_buff[2:0],mcu_en};
  49. end
  50. always@(posedge sys_clk or negedge sys_res)
  51. if(!sys_res)
  52. stata <= IDLE;
  53. else begin
  54. case(stata)
  55. IDLE: if(mcuen_flag == 1'b1)
  56. stata <= READ;
  57. else if(mcuwr_flag == 1'b1)
  58. stata <= WAIT_WR;
  59. else
  60. stata <= IDLE;
  61. READ: if((ram_addr == addr_max && mcuclk_flag == 1'b1)|| mcu_en == 1'b1)
  62. stata <= STOP;
  63. else
  64. stata <= READ;
  65. WAIT_WR:if(mcuen_flag == 1'b1)
  66. stata <= WRITE;
  67. else
  68. stata <= WAIT_WR;
  69. WRITE: if(mcu_en == 1'b1)
  70. stata <= STOP;
  71. else
  72. stata <= WRITE;
  73. STOP: stata <= IDLE;
  74. default:;
  75. endcase
  76. end
  77. always@(posedge sys_clk or negedge sys_res)
  78. if(!sys_res)
  79. ram_addr <= 'd0;
  80. else if((stata == READ && mcuclk_flag == 1'b1 && ram_addr == addr_max) || (stata == STOP))
  81. ram_addr <= 'd0;
  82. else if(stata == READ && mcuclk_flag == 1'b1)
  83. ram_addr <= ram_addr + 1;
  84. else
  85. ram_addr <= ram_addr;
  86. always@(posedge sys_clk or negedge sys_res)
  87. if(!sys_res)begin
  88. ram1_raddr <= 8'bzzzz_zzzz;
  89. ram2_raddr <= 8'bzzzz_zzzz;
  90. end
  91. else if(ram_chioce == 0)begin
  92. ram1_raddr <= ram_addr;
  93. ram2_raddr <= 8'bzzzz_zzzz;
  94. end
  95. else begin
  96. ram2_raddr <= ram_addr;
  97. ram1_raddr <= 8'bzzzz_zzzz;
  98. end
  99. always@(posedge sys_clk or negedge sys_res)
  100. if(!sys_res)
  101. mcurd_flag <= 1'b0;
  102. else if((cnt_mcuflag != 0) || (ramend_flag == 1'b1))
  103. mcurd_flag <= 1'b1;
  104. else
  105. mcurd_flag <= 1'b0;
  106. always@(posedge sys_clk or negedge sys_res)
  107. if(!sys_res)
  108. cnt_mcuflag <= 'd0;
  109. else if(ramend_flag == 1'b1 || cnt_mcuflag != 0)
  110. cnt_mcuflag <= cnt_mcuflag + 1;
  111. else if(cnt_mcuflag == 7)
  112. cnt_mcuflag <= 0;
  113. else
  114. cnt_mcuflag <= cnt_mcuflag;
  115. always@(posedge sys_clk or negedge sys_res)
  116. if(!sys_res)
  117. wr_rd <= 1'b0;
  118. else if((stata == WAIT_WR && mcuen_flag == 1'b1) || stata == WRITE)
  119. wr_rd <= 1'b1;
  120. else
  121. wr_rd <= 1'b0;
  122. assign mcu_rddata =(ram_chioce==0)? ram1_data:ram2_data ;
  123. assign mcu_data = (wr_rd == 1'b0)? mcu_rddata: 16'bzzzz_zzzz;
  124. //fpga接收单片机发送来的数据
  125. assign mcuwr_flag = mcuwr_buff[2] & (~mcuwr_buff[3]); //取上升沿
  126. always@(posedge sys_clk or negedge sys_res)
  127. if(!sys_res)begin
  128. mcuwr_buff <= 4'd0;
  129. end
  130. else begin
  131. mcuwr_buff <= {mcuwr_buff[2:0],mcu_wr};
  132. end
  133. always@(posedge sys_clk or negedge sys_res)
  134. if(!sys_res)
  135. mcu_wrdata <= 'd0;
  136. else if(wr_rd == 1'b1)
  137. mcu_wrdata <= {mcu_wrdata[15:0],mcu_data};
  138. else
  139. mcu_wrdata <= 'd0;
  140. always@(negedge mcuclk_buff[2] or negedge mcuen_buff[2])
  141. if(mcuen_buff[2])begin
  142. wr_data <= 'd0;
  143. end
  144. else
  145. wr_data <= mcu_wrdata[31:16];
  146. endmodule

    仿真tb文件代码:

  1. `timescale 1ns/1ns
  2. module tb_c();
  3. reg clk;
  4. reg res;
  5. reg ram_chioce;
  6. reg ramend_flag;
  7. reg [15:0]ram1_data;
  8. reg [15:0]ram2_data;
  9. reg mcu_clk;
  10. reg mcu_en;
  11. wire [7:0] ram1_raddr;
  12. wire [7:0] ram2_raddr;
  13. wire mcu_flag;
  14. wire [15:0]mcu_data;
  15. reg we_rd; //低电平代表单片机读,默认为低
  16. reg mcu_wr;
  17. reg [15:0]data; //单片机发送的数据
  18. reg [15:0]mem_ram1[9:0]; //定义两块ram,fpga从ram中读取数据发送给单片机
  19. reg [15:0]mem_ram2[9:0];
  20. reg [14:0]cnt_ramchoice; //对两块ram使能信号的计数
  21. initial begin
  22. clk <= 1'b0;
  23. res <= 1'b0;
  24. mcu_en <= 1'b1;
  25. mcu_wr <= 1'b0;
  26. we_rd <= 1'b0;
  27. #100 res <= 1'b1;
  28. end
  29. always #10 clk <= ~clk;
  30. always@(posedge clk or negedge res)
  31. if(!res)begin
  32. mem_ram1[0] <= 16'd0;
  33. mem_ram1[1] <= 16'd0;
  34. mem_ram1[2] <= 16'd0;
  35. mem_ram1[3] <= 16'd0;
  36. mem_ram1[4] <= 16'd0;
  37. mem_ram1[5] <= 16'd0;
  38. mem_ram1[6] <= 16'd0;
  39. mem_ram1[7] <= 16'd0;
  40. mem_ram1[8] <= 16'd0;
  41. mem_ram1[9] <= 16'd0;
  42. end
  43. else begin
  44. mem_ram1[0] <= 16'h1111;
  45. mem_ram1[1] <= 16'h2222;
  46. mem_ram1[2] <= 16'h3333;
  47. mem_ram1[3] <= 16'h4444;
  48. mem_ram1[4] <= 16'h5555;
  49. mem_ram1[5] <= 16'h6666;
  50. mem_ram1[6] <= 16'h7777;
  51. mem_ram1[7] <= 16'h8888;
  52. mem_ram1[8] <= 16'h9999;
  53. mem_ram1[9] <= 16'haaaa;
  54. end
  55. always@(posedge clk or negedge res)
  56. if(!res)begin
  57. mem_ram2[0] <= 16'd0;
  58. mem_ram2[1] <= 16'd0;
  59. mem_ram2[2] <= 16'd0;
  60. mem_ram2[3] <= 16'd0;
  61. mem_ram2[4] <= 16'd0;
  62. mem_ram2[5] <= 16'd0;
  63. mem_ram2[6] <= 16'd0;
  64. mem_ram2[7] <= 16'd0;
  65. mem_ram2[8] <= 16'd0;
  66. mem_ram2[9] <= 16'd0;
  67. end
  68. else begin
  69. mem_ram2[0] <= 16'h1234;
  70. mem_ram2[1] <= 16'haaaa;
  71. mem_ram2[2] <= 16'h1234;
  72. mem_ram2[3] <= 16'haaaa;
  73. mem_ram2[4] <= 16'h1234;
  74. mem_ram2[5] <= 16'haaaa;
  75. mem_ram2[6] <= 16'h1234;
  76. mem_ram2[7] <= 16'haaaa;
  77. mem_ram2[8] <= 16'h1234;
  78. mem_ram2[9] <= 16'haaaa;
  79. end
  80. always@(*)begin
  81. ram1_data <= mem_ram1[ram1_raddr];
  82. ram2_data <= mem_ram2[ram2_raddr];
  83. end
  84. always@(posedge clk or negedge res)
  85. if(!res)begin
  86. cnt_ramchoice <= 'd0;
  87. end
  88. else if(cnt_ramchoice == 4000-1)
  89. cnt_ramchoice <= 'd0;
  90. else
  91. cnt_ramchoice <= cnt_ramchoice + 1;
  92. always@(posedge clk or negedge res)
  93. if(!res)begin
  94. ram_chioce <= 1'b1;
  95. ramend_flag <= 1'b0;
  96. end
  97. else if(cnt_ramchoice == 2000-1)begin
  98. ram_chioce <= 1'b0;
  99. ramend_flag <= 1'b1;
  100. end
  101. else if(cnt_ramchoice == 4000-1)begin
  102. ram_chioce <= 1'b1;
  103. ramend_flag <= 1'b1;
  104. end
  105. else begin
  106. ram_chioce <= ram_chioce;
  107. ramend_flag <= 1'b0;
  108. end
  109. always @(*)
  110. begin
  111. if(mcu_flag==1)
  112. begin
  113. #100; mcu_en = 0;
  114. #1200; mcu_en = 1;
  115. #5000; mcu_wr = 1 ;
  116. #100; mcu_en = 0;
  117. mcu_wr = 0;
  118. #20 we_rd = 1;
  119. #1050; mcu_en = 1;
  120. #30 we_rd = 0;
  121. end
  122. end
  123. always @(posedge mcu_clk or negedge mcu_en)
  124. if(mcu_en)
  125. data <= 16'd0;
  126. else
  127. data <= data + 1;
  128. always @(posedge clk)
  129. begin
  130. if(mcu_en==0)
  131. #50 mcu_clk=~mcu_clk;
  132. else
  133. mcu_clk=1;
  134. end
  135. assign mcu_data = (we_rd == 1'b0)? 16'bzzzz_zzzz:data;
  136. fpga_mcu u_fpga_mcu(
  137. . sys_clk ( clk ) ,
  138. . sys_res ( res ) ,
  139. . ram_chioce ( ram_chioce ) ,
  140. . ramend_flag ( ramend_flag ) ,
  141. . ram1_data ( ram1_data ) ,
  142. . ram2_data ( ram2_data ) ,
  143. . mcu_clk ( mcu_clk ) ,
  144. . mcu_en ( mcu_en ) ,
  145. . mcu_wr ( mcu_wr ) ,
  146. . ram1_raddr ( ram1_raddr ) ,
  147. . ram2_raddr ( ram2_raddr ) ,
  148. . mcurd_flag ( mcu_flag ) ,
  149. . mcu_data ( mcu_data )
  150. );
  151. endmodule

        下面给出仿真结果:图1是整体的仿真时序图;图2是单片机读数据部分的仿真时序图;图3是单片机写数据部分的仿真时序图。

                                                                             图1

                                                                             图2

                                                                            图3


总结

         其实关于FPGA与单片机之间的通信协议还有很多种,比如两者之间只通过16根数据线进行数据通信,两者在相同时刻、按照相同的波特率进行数据的发送与接收。这些都可以人为拟定,只要注意在编写代码的时候两者能够统一即可。本文只是介绍了一种我在做项目的过程中用到的一种通信协议,当然可能还存在许多不足的地方,还望读者在阅读的过程中能够批评指正。

        初次创作,难免文章中存在错误,希望读者能够及时纠正并给予私信,望大家共同进步!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/429339
推荐阅读
相关标签
  

闽ICP备14008679号