精华内容
下载资源
问答
  • 在串行数据传输中,数据接收端需要一些特定的信息来恢复出正确的字边界,以确定串行码流中哪些比特属于原始并行数据里的同一时钟节拍里的数据,这一处理过程称为字对齐(Word Aligner)。一些标准的协议会定义特殊的...
  • 而在这种应用场景之下,IIC主机数据接收发送控制器就成为FPGA工程师所必须掌握的模块。本文设计了一种可进行读写双向操作的IIC主机控制器,但由于未根据传输数据量大小设立足够的缓冲区,因此通过了指示信号来进行...

    绪论:

    IIC总线是一种常用的片级总线,它集成于很多器件当中。在构造一个FPGA系统框架中,往往会使用IIC总线对存储器等一些重要的外围器件进行读写。而在这种应用场景之下,IIC主机数据接收发送控制器就成为FPGA工程师所必须掌握的模块。本文设计了一种可进行读写双向操作的IIC主机控制器,但由于未根据传输数据量大小设立足够的缓冲区,因此通过了指示信号来进行信号传输沟通。当实际应用时,使用者可根据实际数据大小标定缓冲区,从而减少控制信号标定带来的复杂性。另外,IIC总线控制器实际上是一个低速设备,由于当中必须使用到计数器,因此会造成关键路径上的大延时。如果需要使整个系统运行在更高的时钟频率下,请使用者使用锁相环为该模块分频出一个小频率时钟,并通过FIFO连接到FPGA内的其它模块。

    一、IIC总线

    1.概论

    IIC总线介绍:https://blog.csdn.net/qq_38410730/article/details/80312357       

           I²C(Inter-Integrated Circuit)即集成电路总线,它是一种串行通信总线,使用多主从架构,实现白双工通信。IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。IIC总线支持多主控, 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

           IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。

           通常,数据线SDA和时钟线SCL都是处于上拉电阻状态。因为:在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。IIC上,SDA传输的有效数据只能在SCL低电平时改变。在SCL为高时,SDA上的电平代表着数据的高低。

                                                                                  

    2.起始信号与终止信号时序图

    • 起始信号:当时钟线SCL为高期间,数据线SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号;
    • 停止信号:当时钟线SCL为高期间,数据线SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。

                                                                      

    3.主机读写时序图

                                         

           如图所示,IIC数据传输为大端模式。其中DEVICE ADDRESS为所要交互的从机地址,地址的最低位表示着读写操作(0写1读)。图上所示的即为主机写从机操作,主机从START开始状态出发,逐位传送一字节数据,等待从机应答信号ACK(ACK为0则应答,为1则一边表示接收有误)。之后,WORD ADDRESS可以为数据,也可以为地址。图上标注为地址是因为它是用于写存储器,故而将第一字节数据作为地址,第二字节作为实际需写入的数据。

           同理可得,读操作即为DEVICE ADDRESS传输完毕后,从机应答。主机将SDA总线交给从机控制,主机自己控制SCL时钟信号,并在接收到从机一字节数据后向其提供ACK应答。当主机不想继续从从机读取数据(突发),主机在ACK应答1,然后发出STOP信号。当为正常约定时,主机ACK回复0,然后主机拉STOP信号。

    二、FPGA程序设计

        IIC总线主机控制器是以状态机的形式进行数据读写。由于采用了简化性操作,状态机的状态分量比较少,在这里就不予给出。读者可自行阅读注释完备的代码,自己绘制出状态转移图。读者有兴趣可以对下列代码进行仿真,并注意inout口的问题。

    1.程序代码

    module IIC(
        clk,rst_n,sda,scl,data_rd,data_in,data_v,wr,rd
    );
    ///
    //定位:主机控制模块
    //发送设计目标:可整体发送一帧数据码,数据帧由地址信息作为起始,每节数据依赖于data_v进行识别采样;
    //接收设计目标:可连续接收数据,并利用rd脉冲标记接收缓冲数据可被读出
    //硬件时钟为50MHz,SCL通讯时钟100KHz,可通过宏定义修改
    //不对传输结果有效性进行判断、不对总线冲突进行仲裁
    //IIC总线通信应当提前约定好接收/发送数据每帧的数据长度
    //本设计通过宏定义规定:
    //      发送数据:操作地址(非设备地址)为8位,数据量为16bit分两次发送
    //      接受数据:操作地址为8位,数据量为16bit分两次接收
    //由于IIC为低速总线,建议使用PLL分频较低时钟控制,并在末端配置FIFO以降低其对整体设计性能的影响
    //IIC总线数据传递形式为大端,请注意读写顺序
    ///
    input clk,rst_n;
    
    inout sda;                  //数据接收发送口
    output scl;                 //IIC数据时钟线
    
    output[7:0]data_rd;         //数据接收后并行输出
    output rd;                  //接收数据可读出脉冲
    
    input[7:0]data_in;          //发送数据输入
    input data_v;               //发送数据输入指示脉冲
    output wr;                  //发送数据缓冲区可写入数据
    ///
    //IIC模块输出信号定义
    reg scl;                    
    reg rd;
    reg[7:0]data_rd;
    reg wr;
    wire sda;
    //
    reg SDA;                    //sda信号的中介信号,inout综合为三台门,不应直接用寄存器存z值
    reg link;                   //sda传输方向控制信号
                                //0:写
                                //1:读
    reg[7:0]data_t;             //寄存需要向外发送的数据
    reg[7:0]data_r;             //接收缓冲寄存器
    ///
    parameter END_FRAME     =   8'hff;              //发送数据停止帧,IIC接收到该帧后停止数据发送
                                                     //也可以提前约定好数据帧的长度,利用计数器判定。
                                                     //在一般使用中应当提前约定好一次发送的数据长度
    reg SDA_link;                                   //SDA读/写方向控制
    reg[3:0]cnt;                                    //发送/接收一次的8位数据计数
    reg[3:0]cnt_frame;                              //发送/接收数据帧数计数,如提前约定好可以使用状态机分量实现,避免计数器带来的时钟瓶颈
    parameter FRAME_R   =   4'd1;                   //主机一次接收一帧数据,第一帧为从机地址,第二帧为从机发送的数据
    parameter FRAME_T   =   4'd2;                   //主机一次发送两帧数据,包含第一帧的设备地址
    //reg[7:0]add_dev;                                //本次发送的从机地址:使用发送数据寄存器替代
    
    ///
    //状态机参数定义
    //如需操作存储器,添加已注释的状态可使整个过程更清晰
    reg[3:0]STATE;                                   //状态机
    parameter IDLE          =   4'b0000;            //空闲状态
    parameter START         =   4'b0001;            //开始信号生成状态
    parameter ADD_DEV       =   4'b0010;            //从机地址数据发送状态:一次发送8位地址
    parameter ACK_DEV_ADD   =   4'b0011;            //从机设备地址应答识别状态
    //parameter ADD_WORD      =   4'b0100;            //操作字地址发送状态:可以根据实际地址长度进行修改,本设计以8位地址为例
    //parameter ACK_WORD_ADD  =   4'b0101;            //操作字地址应答与读写识别状态
    parameter DATA_T        =   4'b0110;            //数据发送状态
    parameter ACK_DATA_T    =   4'b0111;            //数据发送应答接收状态
    //parameter DEV_ADD_R     =   4'b1000;            //从机设备地址接收状态
    //parameter ACK_DEV_R     =   4'b1001;            //应答从机设备地址状态
    parameter DATA_R        =   4'b1010;            //数据接收状态
    parameter ACK_DATA_R    =   4'b1011;            //数据接收应答状态
    parameter STOP          =   4'b1100;            //发送停止信号生成状态
    ///
    ///
    //SCL时钟线状态定义与延时控制模块
    //由于设定中时钟频率为50MHz,通信频率100KHz,因此分频计数500,4个阶段各分配125
    `define SCL_POS     (cnt_delay == 9'd499)   //延时计数器计数值为499时,SCL拉高
    `define SCL_HIG     (cnt_delay == 9'd124)   //延时计数器计数值为124时,SCL为稳定高电平,此时可以读取SDA上的数据
    `define SCL_NEG     (cnt_delay == 9'd249)   //延时计数器计数值为249时,SCL为拉低
    `define SCL_LOW     (cnt_delay == 9'd374)   //延时计数器计数值为374时,SCL为稳定低电平,此时可以改变SDA上发送的数据
    reg[8:0]cnt_delay;                           //延时计数器
    //延时计数器模块
    always@(posedge clk or negedge rst_n)
        if(!rst_n)  cnt_delay <= 9'd0;
        else if(cnt_delay == 9'd499)   cnt_delay <= 9'd0; 
        else    cnt_delay <= cnt_delay + 9'd1;
    //scl电平控制
    always@(posedge clk or negedge rst_n)
        if(!rst_n)  scl <= 1'b1;
        else   
            case(STATE)
            IDLE : begin
                        scl <= 1'b1;                                        //空闲状态,scl拉高            
                    end
            START : begin
                        if(`SCL_HIG)    scl <= 1'b1;                        //开始状态,scl拉高,等待计数器计数到sdr拉低,通讯开始
                    end
            STOP : begin
                        if(`SCL_POS)    scl <= 1'b1;                        //通讯结束状态,scl拉高
                    end
            default : begin
                        if(`SCL_POS)    scl <= 1'b1;                        //其余状态遵循计数器宏定义状态改变
                        else if(`SCL_NEG)    scl <= 1'b0;
                        else    scl <= scl;
                        end
            endcase
    ///
    //输入数据监测模块:生成数据有效标志,通过检测外部输入数据有效标志上升沿
    reg flag_data_in;                               //并行输入数据有效标志
    reg data_v_1;                                   //外部输入数据有效标志寄存
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            begin
                flag_data_in <= 1'b0;
                data_v_1 <= 1'b0;
            end
        else
            begin
                data_v_1 <= data_v;
                flag_data_in <= ~data_v_1 & data_v;
            end
    ///
    //模块控制状态机转移控制模块
    always@(posedge clk or negedge rst_n)
        if(!rst_n)  STATE <= 4'b0000;
        else if(STATE == IDLE)                                              //空闲状态
            begin
                if(flag_data_in)    STATE <= START;                         //如果检测到有数据需要通过IIC模块发出,状态转移为START                  
                else    STATE <= IDLE;                                      //否则继续保持空闲状态
            end
        else if(STATE == START)                                             //开始信号生成状态
            begin
                if(`SCL_HIG)    STATE <= ADD_DEV;                           //如果scl下降沿已生成,状态转移至ADD_DEV
                else STATE <= START;
            end
        else if(STATE ==  ADD_DEV)                                          //设备地址发送状态
            begin
                if(cnt == 4'd8&&`SCL_LOW) STATE <= ACK_DEV_ADD;             //8位地址(7位设备地址加一位读写标志)发送完毕,转入ACK_DEV_ADD状态
                else STATE <= ADD_DEV;                                      //未发送完毕,继续保持
            end
        else if(STATE ==  ACK_DEV_ADD)                                      //从机设备地址接收应当状态
            begin
                if(`SCL_NEG)                                                //在SCL下降沿改变状态
                begin
                    if(data_t[0])                                          //设备地址最低位位读写标志,1位读,0为写
                        STATE <= DATA_R;                                    //转移至DATA_R
                    else
                        STATE <= DATA_T;                                    //转移至DATA_T
                end
                else    
                    STATE <= ACK_DEV_ADD;
            end
        else if(STATE ==  DATA_T)                                           //主机数据发送状态
            begin
                if(cnt == 4'd8 && `SCL_LOW)                                 //8位数据发送完毕且处于SCL低电平
                    STATE <= ACK_DATA_T;                                    //状态转移至ACK_DATA_T
                else    STATE <= DATA_T;
            end
        else if(STATE ==  ACK_DATA_T)                                       //主机数据发送应答状态
            begin
                if(`SCL_NEG)                                                //在SCL下降沿时检测
                begin
                    if(cnt_frame == (FRAME_T-1))                                //当已发送指定帧数的数据
                        STATE <= STOP;                                      //状态转移至STOP
                     else    STATE <= DATA_T;                               //否则,状态转移至DATA_T,继续进行数据发送
                end
                else    STATE <= ACK_DATA_T;
            end
        else if(STATE ==  DATA_R)                                           //主机数据接收状态
            begin
                if(cnt == 4'd8 && `SCL_LOW)                                 //8位数据接收完毕且处于SCL低电平
                    STATE <= ACK_DATA_R;                                    //状态转移至ACK_DATA_R
                else    STATE <= DATA_R;                                    
            end
        else if(STATE ==  ACK_DATA_R)                                       //主机接收数据应答状态
            begin
                if(`SCL_NEG)                                                //SCL下降沿检测
                    begin
                        if(cnt_frame == (FRAME_R-1))                            //当已接收指定帧数数据
                            STATE <= STOP;                                  //状态转移至STOP
                        else    STATE <= DATA_R;                            //否则继续接收下一阵数据
                    end
                else    STATE <= ACK_DATA_R;
            end
        else if(STATE ==  STOP)                                             //数据发送/接收终止状态
            begin
                if(`SCL_HIG)                                                //当SCL抵达高电平时
                    STATE <= IDLE;                                          //状态转移至IDLE
                else STATE <= STOP;
            end
        else    STATE <= IDLE;                                              //默认状态为IDLE
    
    //状态机状态操作模块
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            begin
                cnt <= 4'd0;                                                //发送/接收一次的8位数据计数
                cnt_frame <= 4'd0;                                          //发送/接收数据帧数计数
                SDA <= 1'b1;                                                //写加载串行数据
                link <= 1'b0;                                               //sda传输方向控制信号,0写1读
                wr <= 1'b1;                                                 //发送数据缓冲区可写入数据
                rd <= 1'b0;                                                 //接收数据可读出脉冲
                data_t <= 8'd0;                                             //寄存需要向外部发送的数据
                data_r <= 8'd0;                                             //读取接收的数据
                data_rd <= 8'd0;                                            //接收的数据寄存
            end
        else
            case(STATE)
            IDLE :          begin                                          //空闲状态,SDA拉高,IIC等待内部数据到来
                                cnt <= 4'd0;
                                cnt_frame <= 4'd0;
                                SDA <= 1'b1;
                                link <= 1'b0;
                                rd <= 1'b0;
                                data_t <= 8'd0;
                                if(flag_data_in) wr <= 1'b0;
                            end
            START :         begin                                           //开始状态:向外发送起始信息
                                if(`SCL_HIG)                                //当计数器计数到scl为高时,sdr上的数据拉低,通讯开始
                                    begin
                                        link <= 1'b0;                       //sda传输向外写
                                        SDA <= 1'b0;                        //写数据为0
                                        data_t <= data_in;                  //发送数据寄存
                                        wr <= 1'b0;                         //不接受新的数据进入缓冲
                                                                            //在约定数据深度情况下,可以一步到位锁存,降低对FPGA内部其它模块的干扰
                                        rd <= 1'b0;                         //不接受数据被读取
                                    end
                            end
            ADD_DEV :       begin                                           //设备地址发送状态:IIC通讯必须以此开始确定通讯对象及读写操作
                                if(`SCL_LOW)                                //SCL低电平时改变数据
                                    begin
                                        if(cnt == 4'd8)                     //8bit发送完毕后
                                            begin
                                                cnt <= 4'd0;                //8位数据计数清零
                                                SDA <= 1'b1;                //SDR输出数据赋1,但此时为输入高阻状态
                                                link <= 1'b1;               //SDR输入状态
                                                if(data_t[0]) wr <= 1'b0;
                                                else wr <= 1'b1;            //发送缓冲区可写入数据
                                                rd <= 1'b0;                 //接收缓冲区不可被读
                                            end
                                        else
                                            begin
                                                cnt <= cnt + 4'd1;          //位计数器计数
                                                link <= 1'b0;               //SDR输出状态
                                                case(cnt)
                                                4'd0 : SDA <= data_t[7];
                                                4'd1 : SDA <= data_t[6];
                                                4'd2 : SDA <= data_t[5];
                                                4'd3 : SDA <= data_t[4];
                                                4'd4 : SDA <= data_t[3];
                                                4'd5 : SDA <= data_t[2];
                                                4'd6 : SDA <= data_t[1];
                                                4'd7 : SDA <= data_t[0];
                                                default : SDA <= 1'b1;
                                                endcase
                                            end
                                    end 
                            end
            ACK_DEV_ADD :   begin                                                       //设备地址接收应答状态 
                                cnt <= 4'd0;                                            //位发送接收计数器清零                                         
                                if(flag_data_in)                                        //有数据需要发送,则赋值到寄存器内
                                    begin
                                        data_t <= data_in;                              //由于下一个状态也可能是数据接收,因此本步不一定运行
                                        cnt_frame <= 4'd1;                              //接收到一帧发送数据,计数器赋1
                                        wr <= 1'b0;                                     //数据不可写入发送缓冲寄存器
                                        rd <= 1'b0;                                     //接收缓冲寄存器不可读
                                    end
                            end
            DATA_T :        begin                                           //数据发送状态
                                if(`SCL_LOW)                                //SCL低电平时改变数据
                                    begin
                                        if(cnt == 4'd8)                     //8bit发送完毕后
                                            begin
                                                cnt <= 4'd0;                //8位数据计数清零
                                                SDA <= 1'b1;                //SDR输出数据赋1,但此时为输入高阻状态
                                                link <= 1'b1;               //SDR输入状态
                                                wr <= 1'b1;                 //发送缓冲区可写入数据
                                                rd <= 1'b0;                 //接收缓冲区不可被读
                                            end
                                        else
                                            begin
                                                cnt <= cnt + 4'd1;          //位计数器计数
                                                link <= 1'b0;               //SDR输出状态
                                                case(cnt)
                                                4'd0 : SDA <= data_t[7];
                                                4'd1 : SDA <= data_t[6];
                                                4'd2 : SDA <= data_t[5];
                                                4'd3 : SDA <= data_t[4];
                                                4'd4 : SDA <= data_t[3];
                                                4'd5 : SDA <= data_t[2];
                                                4'd6 : SDA <= data_t[1];
                                                4'd7 : SDA <= data_t[0];
                                                default : SDA <= 1'b1;
                                                endcase
                                            end
                                    end 
                            end
            ACK_DATA_T :    begin                                                       //数据发送应答状态
                                cnt <= 4'd0;                                            //位发送接收计数器清零  
                                if(flag_data_in)                                        //有数据需要发送,则赋值到寄存器内
                                    begin
                                        data_t <= data_in;                              //由于下一个状态也可能是数据接收,因此本步不一定运行
                                        cnt_frame <= cnt_frame + 4'd1;                  //接收到一帧发送数据,计数器加1
                                        wr <= 1'b0;                                     //数据发送缓冲区不可写入
                                        rd <= 1'b0;                                     //数据接收缓冲
                                    end
                                else
                                    begin
                                        cnt_frame <= cnt_frame;                  //接收到一帧发送数据,计数器加1
                                        wr <= wr;                                     //数据发送缓冲区不可写入
                                        rd <= rd;
                                    end
                            end
            DATA_R :        begin                                                       //数据接收状态  
                                if(`SCL_HIG)                                            //SCL高电平读取数据 
                                begin  
                                    if(cnt == 4'd8)
                                        begin
                                           cnt <= 4'd0;                                     //8位数据计数清零
                                           SDA <= 1'b1;                                     //SDR输出数据赋1,但此时为输入高阻状态
                                           link <= 1'b1;                                    //SDR接收状态,下一步需要应答,此时不可在SCL高电平输出应答信号
                                           wr <= 1'b0;                                      //发送缓冲区可写入数据
                                           rd <= 1'b1;                                      //接收缓冲区不可被读
                                           data_rd <= data_r;                               //接受数据放入可读接口中 
                                        end
                                    else
                                        begin
                                            wr <= 1'b0;                                     //数据发送缓冲区不可写
                                            rd <= 1'b0;                                     //数据接收缓冲区不可读                                    
                                            cnt <= cnt + 1'b1;                              //位计数加1
                                            case(cnt)
                                            4'd0 : data_r[0] <= sda;
                                            4'd1 : data_r[1] <= sda;
                                            4'd2 : data_r[2] <= sda;
                                            4'd3 : data_r[3] <= sda;
                                            4'd4 : data_r[4] <= sda;
                                            4'd5 : data_r[5] <= sda;
                                            4'd6 : data_r[6] <= sda;
                                            4'd7 : data_r[7] <= sda;
                                            default : data_r <= 4'd0;
                                            endcase   
                                        end
                                end
                            end
            ACK_DATA_R :    begin                                           //数据接收应答状态
                                cnt <= 4'd0;                                //8位数据计数器清零
                                link <= 1'b0;                               //SDA向外写
                                SDA <=  1'b0;                               //SDA拉低
                                wr <= 1'b0;                                 //数据发送缓冲区不可写
                                rd <= 1'b1;                                 //数据接收缓冲区可读
                                if(`SCL_NEG)                                //SCL低电平
                                    begin
                                    if(cnt_frame == FRAME_R)                //SCL下降沿
                                        begin
                                            cnt_frame <= 4'd0;              //帧计数器清零
                                        end
                                    else
                                        begin
                                            cnt_frame <= cnt_frame + 4'd1;  //帧计数器加1
                                        end
                                end
                            end
            STOP :          begin                                           //停止发送/接收状态
                                wr <= 1'b0;                                 //不可写入数据
                                rd <= 1'b0;                                 //不可读出数据
                                if(`SCL_LOW)
                                    begin
                                        link <= 0;                          //向外写数据
                                        SDA <= 1'b0;                        //拉高SDA
                                    end
                                else if(`SCL_HIG)                                //SCL高电平状态
                                    begin
                                        link <= 0;                          //向外写数据
                                        SDA <= 1'b1;                        //拉高SDA
                                    end
                                    
                            end
            default : begin
                           cnt <= 4'd0;                                                //发送/接收一次的8位数据计数
                           cnt_frame <= 4'd0;                                          //发送/接收数据帧数计数
                           SDA <= 1'b0;                                                //写加载串行数据
                           link <= 1'b0;                                               //sda传输方向控制信号,0写1读
                           wr <= 1'b1;                                                 //发送数据缓冲区可写入数据
                           rd <= 1'b0;                                                 //接收数据可读出脉冲
                           data_t <= 8'd0;                                             //待发送数据缓冲区           
                        end
            endcase
            
            
    
    ///
    assign sda = link ? 1'bz : SDA;        //读时sda高阻,写时为SDA加载数据
    ///
    endmodule

    2.主机写仿真波形

    3.主机读仿真波形

     

    展开全文
  • (1)串口接收模块(UART_Byte_Rx.v):完成串口数据接收,将串行数据转换成并行数据输出。 (2)按键消抖模块(key_filter.v):进行按键消抖,可输出一个脉冲按键按下标志和按键按下时间标志。 (3)按键控制模块...
  • FPGA中uart接收

    2019-04-27 22:04:19
    电路功能说明:对输入的串行数据码流,用高速的时钟进行采样处理,将异步串行输入的数据流转换为同步的并行数据输出。
  • 每路串口收发均挂上FIFO,本设计采用了8路并行串口,实现了数据发送控制

    每路串口收发均挂上FIFO,本设计采用了8路并行串口,实现了数据发送控制

    展开全文
  • // 模拟串口信号线,串行接收数据 1101_1000 ,转换为并行数据, 并显示 D8 // 把并行数据 D8 传给串口输出模块,串行输出数据 1101 1000 时钟40MHz,波特率115200 1、源文件 uart_rx.v uart_tx.v 2、仿真文件 test...

    FPGA串口收发(三):接收数据,再转发出去

    功能:测试串口接收数据,再将数据从串口发出

    // 模拟串口信号线,串行接收数据 1101_1000 ,转换为并行数据, 并显示 D8

    // 把并行数据 D8 传给串口输出模块,串行输出数据 1101 1000

    时钟40MHz,波特率115200

    1、源文件

    uart_rx.v

    uart_tx.v

    2、仿真文件 testbench

    tb_uart_rx_tx.v

    `timescale 1ns / 1ps
    //
    // Company: Myminieye
    // Engineer: Nill
    //
    // Create Date:
    // Design Name:
    // Module Name:
    // Project Name:
    // Target Devices:
    // Tool Versions:
    // Description: 测试串口接收数据,再将数据从串口发出
    // 模拟串口信号线,串行接收数据 1101_1000 ,转换为并行数据, 并显示 D8
    // 把并行数据 D8 传给串口输出模块,串行输出数据 1101 1000
    // Dependencies:
    //
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    //
    //
    `define UD #1
    
    module tb_uart_rx_tx();
    
    //==========================================================================
    //wire and reg 定义:信号与参数
    //==========================================================================
    	reg       sim_clk;		//模拟时钟信号
    	//reg     tx_pulse;     // active posedge
    	reg 	  sim_rst_n;
    	
    	// input to rx module
    	reg       uart_rx;		//串口发送信号线
    	//output from rx module
    	wire [7:0] rx_data;		//送入串口发送模块,准备发送的数据
    	wire       rx_finish;   //串口接收数据有效,接收完成拉高1个BPS
    	
    	// input to tx module
    	reg       tx_pulse;     // active posedge
    	reg [7:0] tx_data;		//送入串口发送模块,准备发送的数据
    	//output from tx module
    	wire      uart_tx;		//串口发送信号线
    	wire      tx_busy;		//串口发送模块状态
    	
    	//时钟参数
    	parameter SYS_CLK_FRE = 40_000_000;     //系统频率40MHz  40_000_000
    	parameter SYS_CLK_PERIOD = 1_000_000_000/SYS_CLK_FRE;  //周期25ns
    	parameter RST_CYCLE = 5;                //复位持续时间,clk时钟周期数
    	parameter RST_TIME = RST_CYCLE * SYS_CLK_PERIOD;    //复位时间:5个时钟周期
    
    	//波特率参数
      	parameter BAUD_RATE = 115200; 	//串口波特率
    	parameter BAUD_RATE_PERIOD	= 1_000_000_000/BAUD_RATE;
    	//波特率周期,0.104ms = 104us,1/9600 s = 1^9 /9600 ns = 4167 sim_clk
    	//波特率周期, 1/115200 = 8680 ns  =  8.7us = 347 sim_clk
    
    	//波特率+时钟参数
        parameter [15:0]  BPS_NUM = SYS_CLK_FRE / BAUD_RATE; //时钟/波特率,用时钟周期构造波特率周期
    	//  BPS_NUM = 40_000_000/115200 = 347.22 = 16'd347
        //  1 bit位宽所需时钟周期的个数。最长的波特率计数,10417,二进制有14位,取16位
    	//  parameter BPS_4800: 40MHz set 8333 ; 50MHz set 10417
    	//  parameter BPS_9600: 40MHz set 4167 ; 50MHz set 5208
    	//  parameter BPS_115200: 40MHz set 347; 50MHz set 434
    
    //==========================================================================
    //模拟:信号的输入,显示输出结果
    //==========================================================================
    	//模拟系统时钟:40MHz,25ns
    	always #((SYS_CLK_PERIOD+1)/2-1) sim_clk = ~sim_clk; //延时,电平翻转
    	
    	initial	begin
    		//模拟复位信号:拉低一次
    		#0;
    			sim_clk = 1'b0;
    			sim_rst_n = 1'b0;      //复位拉低,有效,
    			
    			uart_rx  = 1'b1;		//串口线,默认高,起始拉低
    			//rx_data <= `UD 8'h00;		//rx_data 初始化,在 uart_rx中完成
    			tx_pulse = 1'b0;		//触发发送,高有效,先拉低
    			tx_data  = 8'h00;		//数据默认为0		
    			
    		//#RST_TIME;			   //延时:保持足够长时间(至少5个clk)
    		#BAUD_RATE_PERIOD;		   //5个clk时间轴太短,仿真改为1个BPS,更明显
    			sim_rst_n = 1'b1;      //解除复位
    		
    		//==========================================================================
    		//模拟串口接收:串行信号输入,转化成并行数据,并显示
    		//==========================================================================
    			uart_rx = 1'b1;	   //串口发送线,默认拉高
    		repeat( BPS_NUM*1 ) @(posedge sim_clk);	    //循环347个时钟周期,即一个波特率周期
    		//#BAUD_RATE_PERIOD; 						//直接延时,一个波特率周期
    		
    		$display("Initialization complete. BAUD_RATE is %d",BAUD_RATE); //命令行显示初始化完成,输出BAUD_RATE
    		
    		//串口:起始位
    			uart_rx = 1'b0;	
    		#BAUD_RATE_PERIOD;					
    
    		//串行数据,一位一位送入接收信号线:***从位0到位7***,依次发送
    		//测试数据为8'hD8=8'b1101_1000
    			uart_rx = 1'b0;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b0;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b0;		
    		#BAUD_RATE_PERIOD; 		
    			uart_rx = 1'b1;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b1;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b0;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b1;		
    		#BAUD_RATE_PERIOD; 
    			uart_rx = 1'b1;		
    		#BAUD_RATE_PERIOD; 		
    		$display("The uart_rx 8'hD8 = 8'b1101_1000 has been sent.");  //命令行显示:串口信号线数据已发送
    
    		//串口:结束位
    			uart_rx = 1'b1;	
    		#BAUD_RATE_PERIOD;
    			$display("The uart_rx has received. rx_data = 8'h%h",rx_data);  
    		//命令行显示:串口信号线接收已结束,显示接收到的数据
    		
    		//==========================================================================
    		//模拟串口发送:并行数据,串行输出
    		//==========================================================================		
    		//***串口接收模块收到的数据,传递给串口输出模块***
    		
    		repeat( BPS_NUM*1 ) @(posedge sim_clk);	  //循环347个时钟周期,即一个波特率周期
    		//#BAUD_RATE_PERIOD; 		//直接延时,一个波特率周期		
    		
    		//传递 第一组数据:8位并行数据,一次性送入串口发送模块
    		 tx_data = rx_data;	//串口接收数据,传给串口发送
    		#BAUD_RATE_PERIOD;
    		
    		//开启触发信号:串口发送
    		//tx_pulse = 1;				//用 rx_en 代替 模拟tx_pulse,直接触发
    		#BAUD_RATE_PERIOD;			//rx_en 串口接收数据有效,接收完成拉高1个BPS
    		
    		//结束触发:串口发送
    		//tx_pulse = 0;
    		#BAUD_RATE_PERIOD;
    		
    		repeat( BPS_NUM*9 ) @(posedge sim_clk);	//发送数据,等待8个波特率周期
    			$display("The first tx_data 8'h%h has been sent.",tx_data);  //命令行显示:第1组数据发送完
    		
    		#BAUD_RATE_PERIOD;	
    			$stop;		//结束仿真
    		
    	end
    
    //==========================================================================
    //调用top模块
    //==========================================================================
        //串口发送
        uart_rx #(
             //.CLK_SYS   (  SYS_CLK_FRE), //系统时钟
             .BPS_NUM (  BPS_NUM  )  // 时钟/波特率,1 bit位宽所需时钟周期的个数
         )
         u_uart_rx(
            .clk      (  sim_clk  ),// input       clk,
    		//.rst_n	  (  sim_rst_n ),// input 
    		.uart_rx  (  uart_rx  ),// input reg  串口接收信号线
    		
            .rx_data  (  rx_data  ),// output  接收到的数据
            .rx_finish(  rx_finish)// output  串口接收数据有效,接收完成拉高1个BPS
            //.rx_end   (  rx_end   ) // output  //接收到停止位,拉高1个clk,没啥用
        );
    	
    	//串口发送
        uart_tx #(
             //.CLK_SYS   (  SYS_CLK_FRE), //系统时钟
             .BPS_NUM (  BPS_NUM  )  // 时钟/波特率,用时钟周期构造波特率周期
         )
         u_uart_tx(
            .clk      (  sim_clk  ),// input       clk,
            .tx_data  (  tx_data  ),// input [7:0] tx_data,
            .tx_pulse (  rx_finish),// input       外部输入,开始产生数据->开启串口发送状态
    
            .uart_tx  (  uart_tx  ),// output reg  uart_tx,
            .tx_busy  (  tx_busy  ),  // output      输出,串口接收忙状态
    		.tx_finish(  tx_finish) // output      //串口发送数据结束标志,8位数据发完,拉高一个BPS
        );
    
    endmodule
    
    

    3、仿真结果

    ModelSim波形
    [外链图片转存中...(img-RJwbdc2Q-1592206711752)]

    命令行显示
    [外链图片转存中...(img-Phr8ckGc-1592206711759)]

    增加 tx_finish 和 rx_finish
    [外链图片转存中...(img-9MiYxW4g-1592206711764)]

    展开全文
  • 现代电磁信号环境越来越复杂密集,要求电子战接收机必须具有很宽的处理带宽、高灵敏度、大动态范围、多信号并行处理和大量信息实时处理的能力。而数字信道化接收机不仅可以较好地满足上述要求,还可实现监视信道内...
  • 以Xilinx公司V7系列FPGA为核心控制单元设计电路,在单通道传输速率为6 Gb/s的条件下完成数据收发测试,验证了传输过程中数据的同步性、准确性及整体方案的可行性。设计结果表明,这种串行传输方式不仅解决了并行传输...
  • 1 微处理器与FPGA微处理器普遍采用冯·诺依曼结构,即存储程序型计算机结构,主要包括存储器和运算器2个子系统。其从存储器读取数据和指令到运算器,运算结果储存到存储器,然后进行下一次读取-运算-储存的操作...

    1  微处理器与FPGA
    微处理器普遍采用冯·诺依曼结构,即存储程序型计算机结构,主要包括存储器和运算器2个子系统。其从存储器读取数据和指令到运算器,运算结果储存到存储器,然后进行下一次读取-运算-储存的操作过程。通过开发专门的数据和指令组合,即控制程序,微处理器就可以完成各种计算任务。冯·诺依曼型计算机成功地把信息处理系统分成了硬件设备和软件程序两部分,使得众多信息处理问题都可以在通用的硬件平台上处理,只需要开发具体的应用软件,从而极大地降低了开发信息处理系统的复杂性。然而,冯·诺依曼型计算机也有不足之处,由于数据和指令必须在存储器和运算器之间传输才能完成运算,使得计算速度受到存储器和运算器之间信息传输速度的限制,形成所谓的冯·诺依曼瓶颈[1];同时,由于运算任务被分解成一系列依次执行的读取-运算-储存过程,所以运算过程在本质上是串行的,使并行计算模式在冯·诺依曼型计算机上的应用受到限制。
    受到半导体物理过程的限制,微处理器运算速度的提高已经趋于缓慢,基于多核处理器或者集群计算机的并行计算技术已经逐渐成为提高计算机运算性能的主要手段。并行计算设备中包含多个微处理器,可以同时对多组数据进行处理,从而提高系统的数据处理能力。基于集群计算机的超级计算机已经成为解决大型科学和工程问题的有利工具。然而,由于并行计算设备中的微处理器同样受冯·诺依曼瓶颈的制约,所以在处理一些数据密集型,如图像分析等问题时,计算速度和性价比不理想。
    现场可编程门阵列(FPGA)是一种新型的数字电路。传统的数字电路芯片都具有固定的电路和功能,而FPGA可以直接下载用户现场设计的数字电路。FPGA技术颠覆了数字电路传统的设计-流片-封装的工艺过程,直接在成品PFGA芯片上开发新的数字电路,极大地扩大了专用数字电路的用户范围和应用领域。自从20世纪80年代出现以来,FPGA技术迅速发展,FPGA芯片的晶体管数量从最初的数万个迅速发展到现在的数十亿个晶体管[2],FPGA的应用范围也从简单的逻辑控制电路发展成为重要的高性能计算平台。
    FPGA芯片中的每个逻辑门在每个时钟周期都同时进行着某种逻辑运算,因此FPGA本质上是一个超大规模的并行计算设备,非常适合用于开发并行计算应用。目前,FPGA已被成功地应用到分子动力学、基因组测序、神经网路、人工大脑、图像处理、机器博弈等领域,取得了数十到数千倍的速度提高和优异的性价比[3-18]。
    2  FPGA并行算法的设计与开发
    FPGA通过逻辑电路实现计算功能,而微处理器则通过程序和存储器控制计算过程。FPGA和微处理器在基本架构上的根本区别,决定了它们算法的设计理念和方法也存在很大区别[4-5]。与微处理器相比,FPGA最主要的优势是可以同时对大量变量进行逻辑运算和赋值,实现并行运算;而FPGA最主要的劣势则是失去了微处理器所提供的许多基本计算工具,如浮点数计算、初等函数取值等。在设计FPGA算法时,应该充分发挥FPGA可以同时对大量变量进行逻辑运算和赋值的优势,而尽量避免使用浮点数运算、初等函数取值等数值计算功能,所以并不是任何并行计算问题都适于在FPGA上实现。一般来说,FPGA最适用于需要大量并行逻辑或者整数运算的计算任务。例如,图像处理应用中的线性除噪、形态学变换、边缘检测、模式匹配等应用,就非常适合在FPGA上实现[10-16]。
    FPGA算法中常用的电路结构包括流水线型和并行阵列型两种(见图1)。在流水线型结构中,计算任务被分解成多个子任务,由多个子电路依次完成,多组数据依次进入流水线电路,同时进行不同阶段的计算(见图1(a))。忽略首批数据进入流水线的延迟,流水线型电路处理数据的用时等于所有子任务中最长的用时。如果每个子任务都可以采用组合电路来完成而不需要时序电路,则所有子任务都可以在一个FGPA时钟内完成,从而实现极高的运算速度和性价比。

    图 1  流水线型电路和并行阵列型电路
     
    在并行阵列型电路中,多组并行排列的子电路同时接收整体数据的多个部分进行并行计算(见图1(b))。并行阵列型电路中的子电路本身可以是简单的组合电路,也可以是复杂的时序电路,如流水线型电路。如果受逻辑资源限制,无法同时处理全部数据,也可以依次处理部分数据,直到完成全部数据的处理。
    图2示出了FPGA系统的开发流程。一般采用硬件描述语言(HDL),如Verilog、VHDL等,实现FPGA并行算法。虽然有一些类似C语言的软件可以帮助没有数字电路设计经验的用户实现FPGA设计,但是由于包括C语言在内的微处理器编程语言蕴含了许多微处理器的计算模式和理念,所以会影响或干扰FPGA并行算法的实现。使用HDL可以更好地结合FPGA的计算模式,设计出更合理的并行算法。一般选用FPGA开发板作为FPGA开发和测试的平台。开发过程中所需要完成的功能仿真、设置管脚、编译综合等步骤需要依托FPGA供应商提供的编译和通信测试软件。
    下面以二值形态学腐蚀变换为例,设计一个简单的并行FPGA算法。如果把一幅二值图像中所有前景像素的集合称为A,把某个内核集合称为B;把该内核中心在x图像点时所包含的前景像素的集合称为B(x),则以B为结构元素的腐蚀变换定义为集合{X|B(x)A}。具体的计算任务是以包含右相邻和下相邻像素的内核为结构元素,对多幅6×6的二值图像进行连续两次腐蚀变换。采用Verilog语言,设计了一个三级流水线电路(见图3和图4)来实现这个计算任务。这个电路处理一幅图像的平均时间是一个FPGA时钟。在逻辑资源允许的情况下,相同的算法可以处理任何尺寸的图像,而且处理时间保持为一个FPGA时钟。

     
     
    腐蚀变换电路的Verilog源程序如下:

     
     3  FPGA在机器博弈方面的应用
    机器博弈是智能科学技术的一个重要研究领域。搜索博弈树是机器博弈的基本方法。由于博弈树的状态空间巨大,因此提高搜索速度一直是机器博弈领域的重要研究课题。采用专用数字电路可以显著提高搜索速度。曾经击败国际象棋世界冠军的机器博弈系统Deep Blue就是采用了专用数字电路来提高树搜索的速度[19]。FPGA提供了实现树搜索专用数字电路的更便捷的方法。目前已经有人尝试采用FPGA提高中国象棋和9路围棋的搜索速度[17-18]。下面介绍的基于FPGA的9路围棋博弈系统中吃子算法的设计,展示了FPGA算法设计的特点和方法。
    围棋规则规定,棋盘上的每个棋子本身或者与其相连的棋子必须至少与一个空位相邻(至少有一口气),不符合这个规则的棋子必须被拿掉(吃子)。实现这一规则的串行算法是依次在某个棋子本身的4连通点,或者与其相连同色棋子的4连通点搜索空位。有两个原因使这个串行算法不适于在FPGA上应用。首先,由于算法是串行的,因此无法在流水线型电路上实现,影响计算速度;其次,由于算法需要可寻址的储存空间,无法在FPGA上直接实现。
    设计以下并行算法来实现吃子规则(见图4),即不断地去除与空位直接相连的同色棋子(相当于腐蚀形态学变换),最后剩下的棋子就是需要拿掉的棋子。把这些棋子从棋盘中去掉就完成了吃子规则。这个算法可以通过一个19级流水线电路实现,处理一个棋盘的平均时间只要一个FPGA时钟。为了实现流水线型电路,图4中对棋盘的复制是必需的步骤。这个示例说明了在FPGA上实现并行计算不能简单翻译微处理器上的原有算法,而是要根据FPGA和计算问题的具体特点,从新设计相应的并行算法。
     
     
    图 4  吃子算法
     
    基于相似的并行算法,用一个167级的流水线实现了模拟黑白双方各走一步的功能,并在此基础上实现了对整盘围棋的蒙特卡罗模拟,比基于微处理器的蒙特卡罗模拟快了170倍[18]。
    4  结束语
    本文介绍FPGA并行计算技术的基本特点和方法。选择适当的计算问题,结合FPGA可以对大量变量同时进行逻辑运算和赋值的特点,设计高性能的并行算法,可以实现计算速度和性价比的大幅提高。近年来,FPGA的逻辑资源和运算速度飞速发展,使得FPGA并行计算的应用领域不断拓展,同时FPGA的设计测试工具也在不断完善,使得更多数字电路领域之外的科技工作者可以涉足FPGA并行计算,为FPGA高性能计算的爆发性发展提供了条件。作为一个新兴的交叉领域,我们期待FPGA并行计算在包括智能科学技术在内的更多领域产生突破性的结果。

    展开全文
  • 1.数据连续模式,此时sdata_valid指示有效数据开始,在并行数据接收完成前,后面数据均有效。输入时序如下图。 2.数据不连续模式,此时sdata_valid指示当前数据有效。输入时序如下图。 注意: 1.sdata与sdata_...
  • FPGA 串口发送和接收程序

    千次阅读 多人点赞 2019-11-16 14:46:14
    并行通信是指数据的各个位用多条数据线同时进行传输 串行通信是将数据分成一位一位的形式在一条传输线上逐个传输 同步通信:带时钟同步信号的数据传输;发送方和接收方在同一时钟的控制下,同步传输数据。 ...
  • FPGA视频拼接项目LVDS视频传输数据接口 ***本文为明德扬原创文章,转载请注明出处!*** LVDS(Low Voltage Differential Signaling)即低压差分信号传输,是一种满足当今高性能数据传输应用的新型技术。由于其可使...
  • 在串行数据传输中,数据接收端需要一些特定的信息来恢复出正确的字边界,以确定串行码流中哪些比特属于原始并行数据里的同一时钟节拍里的数据,这一处理过程称为字对齐(Word Aligner)。一些标准的协议会定义特殊的...
  • 现代电磁信号环境越来越复杂密集,要求电子战接收机必须具有很宽的处理带宽、高灵敏度、大动态范围、多信号并行处理和大量信息实时处理的能力。而数字信道化接收机不仅可以较好地满足上述要求,还可实现监视信道内...
  • 摘要:本文介绍了基于FPGA、以并行多相滤波结构为算法基础的超宽带数字下变频技术。设计过程包括高速AD信号降速预处理,应用SysGen开发环境完成的数字混频、多相滤波和数据抽取,并通过仿真验证了算法的可行性。  ...
  • 数据传输的顺序可以分为串并行通信(传输)和串行通信(传输); 按数据传输的同步方式可分为同步传输和异步传输,而同步和异步同属于串行通信,是串行通信的两种基本形式!!! 按数据传输的流向和时间关系可以...
  • 基于FPGA数据采集系统(一)

    千次阅读 多人点赞 2020-05-22 18:11:27
    本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行...
  • FPGA需要控制的仅为两条信号线:RXD和TXD,即数据接收线和数据发送线,因此我们只需要关注数据接收和发送时的时序图。传送的一帧数据里面包括起始位(0),8位数据位,奇偶校验位,停止位(1)。当空闲时,10位...
  • 本文提出了一种适用于高数据速率通信接收机的高效并行符号定时架构。 所展示的架构依赖于经典Gardner循环的修改版本,并具有“多通道流水线”内插器,该符号使符号率比FPGA的时钟率高出几倍,从而最大程度地提高了可...
  • // 串口接收数据:串行信号线 1101_1000 ,转为并行数据,取反截取低4位 传递给led,再传递给data_gen, // 发送数据: 0.1ms生成/发送一位数据,发送字符串"==HELLO WORLD=" (对应ASCII码),以及led值 1、源文件 ...
  • 多片FPGA级联通信 —— 数据流缓冲FIFO的深度如何选择 ...由于多片FPGA之间的通信接口均采用相同的传输速率1.25Gbps,故理论上在接收端采用缓冲FIFO即可将数据流完整的接收,并保存在本地SDRAM中。 ...
  • FPGA高速数据采集设计JESD204B接口

    千次阅读 2019-12-03 17:10:03
    三,JESD204B具体应用实例 1、相控阵雷达下行同步采集技术应用 多通道数据的同步采集是数字相控阵雷达下行数据接收和处理要解决的关键问题。提出了支持JESD204B协议的模数转换器和支持JESD204B协议的FPGA软核相结合...
  • 基于FPGA数据采集—信号产生篇

    千次阅读 2021-03-13 22:18:49
    Rx, //RS232数据输入 output reg [7:0] data_byte, //并行数据输出 output reg Rx_Done //一次数据接收完成标志 ); reg s0_Rs232_Rx,s1_Rs232_Rx;//同步寄存器 reg tmp0_Rs232_Rx,tmp1_Rs232_Rx;//...
  • 1、在FPGA设计过程,尤其是算法实现时hi,有时往往需要选取某个变量的动态范围地址,而verilog中常规的向量标识方法a[MSB:LSB]往往会发生错误,在此可借用a[BASE :- WIDTH]的方式实现。 a[BASE+:(/-:)WIDTH] ...
  • 时钟的设计与实现、高速数据的同步接收以及采样 数据的 校正算 法。实验及 仿真结 果表明, 同 步数据 采集的结 构设计 和预 处理算法, 能良好抑制并行 ADC 输出 信号因相位偏移、时钟抖动等造成的失配误差。 关键词:...
  • 接收机利用超高速A/D对数据进行高速采样,然后由高性能FPGA进行数据抽取、多相滤波、CORDIC算法等信道化实时处理。为了提高实时性,采用并行IFFT实现。该信道化接收机不仅能稳定输出载频及相位信息,还能处理三路...
  • 今天,对之前写好的UART程序...比如:之前的串口发送模块的写法与常规的写法有所不同,主要就是:一般地,可以将串口的应用分为两种,一者是接收模块和发送模块一起使用,构成回环的形式(比如FPGA的实验课中,板...
  • 此次实现uart协议通过回环来保证数据接收发送的正确。用状态机来理解(也不知道是不是状态机,觉得这样写比较好理解)。 两个接收,发送是对于开发板来说的。开发板的接收端连接的是pc机的发送端。反之。 波特率 ....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,195
精华内容 1,278
关键字:

fpga并行数据接收