FPGA实战 -- UART --- 封装UART IP核

这篇具有很好参考价值的文章主要介绍了FPGA实战 -- UART --- 封装UART IP核。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

FPGA基础 – 通信协议 — 了解UART以及电脑串口环境准备
FPGA实战 – UART — 实现串口回环(加FIFO)



前言

咱们已经学习了UART协议,并且编写了串口回环的代码。每次一些项目遇到串口的时候都要对 RX 或者 TX 代码进行修改,像本人这种非非非非常懒的人,肯定不愿意经常修改代码,所以制作一个IP核会非常方便,只需要调用一下,在top顶层连一连wire就能使用了。简直就是上天赐予的礼物。


一、代码

首先创建一个文件夹,记得找一个不会删的地址来创建。因为自己封装的IP核需要Quartus检测这个文档才能使用,所以放在一个不会动的文件夹里比较好。我建议直接放进Quartus的安装文件里。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
这个IP核的代码直接贴在这里,这个代码不用看不用理解不用试验,只需要知道日常IP核的底层代码是咱们看不到的,相当于自己做了一个非常简单的软核。目前市面上有一些专门做IP核的公司,这篇文章做的和这些公司出品的根本比不了,但日常自己使用就已经够了,一些简单的工程项目也可以调用。
直接创建一个文件夹,建立几个.v文件把代码复制进去就行。

(1)tx

//例化模板:
    // uart_tx #(
    //     .CHECK_BIT  ("None"     ),   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    //     .BPS        (115200     ),   //UART波特率
    //     .CLK        (50000000   )    //工作时钟
    // )uart_tx_inst(
    //     /* input                */.clk          (clk    ),
    //     /* input                */.rst_n        (rst_n  ),
    //     /* input       [7:0]    */.tx_data      (       ),
    //     /* input                */.tx_data_vld  (       ),
    //     /* output               */.ready        (       ),
    //     /* output  reg          */.tx           (       )
    // );

module uart_tx #(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   tx_data         ,
    input               tx_data_vld     ,
    output              ready           ,
    output  reg         tx              
);

    parameter       IDLE    =   0,
                    START   =   1,
                    DATA    =   2,
                    CHECK   =   3,
                    STOP    =   4;

    reg     [2:0]   state       ;

    wire            idle2start  ; 
    wire            start2data  ; 
    wire            data2check  ; 
    wire            data2stop   ;
    wire            check2stop  ;
    wire            stop2idle   ; 


    reg	    [12:0]  cnt_bps     ;
    wire		    add_bps_cnt ;
    wire            end_bps_cnt ;	
    parameter       BPS_MAX =   CLK/BPS;        //计算在设定的波特率下,一个bit占用的时钟周期数

    reg	    [3:0]   cnt_bit     ;
    wire		    add_bit_cnt ;
    wire            end_bit_cnt ;	
    reg     [3:0]   bit_max     ;

    reg     [7:0]   tx_data_r   ;
    wire            check_val   ;

/**************************************************************
                            状态机   
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
                IDLE    :	if(idle2start)
                                state <= START;
                START   :   if(start2data)
                                state <= DATA;
                DATA    :   if(data2stop)       //无校验位
                                state <= STOP;
                            else if(data2check) //有校验位
                                state <= CHECK;
                CHECK   :   if(check2stop)
                                state <= STOP;
                STOP    :   if(stop2idle)
                                state <= IDLE;
            default :	state <= IDLE;
        endcase
                
    assign idle2start   =   state == IDLE   &&  tx_data_vld;  
    assign start2data   =   state == START  &&  end_bit_cnt;  

    assign data2stop    =   state == DATA   &&  end_bit_cnt && CHECK_BIT == "None";
    assign data2check   =   state == DATA   &&  end_bit_cnt; 

    assign check2stop   =   state == CHECK  &&  end_bit_cnt;  
    assign stop2idle    =   state == STOP   &&  end_bit_cnt;  

/**************************************************************
                        波特率计数器
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bps <= 'd0;						
        else    if(add_bps_cnt) begin				
            if(end_bps_cnt)						
                cnt_bps <= 'd0;  				
            else									
                cnt_bps <= cnt_bps + 1'b1;		
        end											
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && cnt_bps == BPS_MAX - 1;

/**************************************************************
                        比特计数器
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_bps_cnt;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == bit_max - 1;

    always@(*)
        case(state)
            IDLE    :	bit_max = 1;
            START   :   bit_max = 1;
            DATA    :   bit_max = 8;
            CHECK   :   bit_max = 1;
            STOP    :   bit_max = 1;
            default :	bit_max = 1;
        endcase

/**************************************************************
                        输入数据寄存
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            tx_data_r <= 0;
        else if(tx_data_vld)
            tx_data_r <= tx_data;

/**************************************************************
                        计算校验位
**************************************************************/
    //奇校验,缩位同或运算
    //assign check_val =  ~^tx_data_r;

    //偶校验,缩位异或运算
    //assign check_val = ^tx_data_r;

    assign check_val = (CHECK_BIT == "Odd")? ~^tx_data_r : ^tx_data_r;

/**************************************************************
                        实现串口时序
**************************************************************/
    always@(*)
        case(state)
                IDLE    :	tx = 1;
                START   :   tx = 0;
                DATA    :   if(tx_data_r[cnt_bit])  //先发低位,再发高位
                                tx = 1;
                            else
                                tx = 0;
                CHECK   :   tx = check_val;
                STOP    :   tx = 1;
            default :	tx = 1;
        endcase

    assign ready = state == IDLE;

endmodule

(2)rx

//例化模板:
    // uart_rx #(
    //     .CHECK_BIT  ("None"     ),   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    //     .BPS        (115200     ),   //UART波特率
    //     .CLK        (50000000   )    //工作时钟
    // )uart_rx_inst(
    //     /* input                */.clk          (clk    ),
    //     /* input                */.rst_n        (rst_n  ),
    //     /* output      [7:0]    */.rx_data      (       ),
    //     /* output               */.rx_data_vld  (       ),
    //     /* output               */.ready        (       ),
    //     /* input                */.rx           (       )
    // );

module uart_rx #(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )(
    input               clk             ,
    input               rst_n           ,
    output      [7:0]   rx_data         ,
    output              rx_data_vld     ,
    output              ready           ,
    input               rx              
);

    parameter       IDLE    =   0,
                    START   =   1,
                    DATA    =   2,
                    CHECK   =   3;

    reg     [1:0]   state       ;

    wire            idle2start  ; 
    wire            start2data  ; 
    wire            data2idle   ; 
    wire            data2check  ;
    wire            check2idle  ;


    reg	    [12:0]  cnt_bps     ;
    wire		    add_bps_cnt ;
    wire            end_bps_cnt ;	
    parameter       BPS_MAX =   CLK/BPS;        //计算在设定的波特率下,一个bit占用的时钟周期数

    reg	    [3:0]   cnt_bit     ;
    wire		    add_bit_cnt ;
    wire            end_bit_cnt ;	
    reg     [3:0]   bit_max     ;

    reg     [7:0]   rx_temp     ; 

/**************************************************************
                            下降沿检测
**************************************************************/
    reg             rx_r1       ;
    reg             rx_r2       ;
    wire            rx_nege     ;

    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            rx_r1 <= 1;
            rx_r2 <= 1;
        end
        else begin
            rx_r1 <= rx;        //同步
            rx_r2 <= rx_r1;     //输入打拍,判断下降沿
        end

    assign rx_nege = !rx_r1 && rx_r2;

/**************************************************************
                            状态机   
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= IDLE;
        else case(state)
                IDLE    :	if(idle2start)
                                state <= START;
                START   :   if(start2data)
                                state <= DATA;
                DATA    :   if(data2idle)       //无校验位
                                state <= IDLE;
                            else if(data2check) //有校验位
                                state <= CHECK;
                CHECK   :   if(check2idle)
                                state <= IDLE;
            default :	state <= IDLE;
        endcase

    assign idle2start   =   state == IDLE   &&  rx_nege;        
    assign start2data   =   state == START  &&  end_bit_cnt;  
    
    assign data2idle    =   state == DATA   &&  end_bit_cnt && CHECK_BIT == "None";   
    assign data2check   =   state == DATA   &&  end_bit_cnt;

    assign check2idle   =   state == CHECK  &&  end_bit_cnt;

/**************************************************************
                        波特率计数器
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bps <= 'd0;						
        else    if(add_bps_cnt) begin				
            if(end_bps_cnt)						
                cnt_bps <= 'd0;  				
            else									
                cnt_bps <= cnt_bps + 1'b1;		
        end											
    assign add_bps_cnt = state != IDLE;
    assign end_bps_cnt = add_bps_cnt && cnt_bps == BPS_MAX - 1;

/**************************************************************
                        比特计数器
**************************************************************/
    always@(posedge clk or negedge rst_n)	
        if(!rst_n)								
            cnt_bit <= 'd0;						
        else    if(add_bit_cnt) begin				
            if(end_bit_cnt)						
                cnt_bit <= 'd0;  				
            else									
                cnt_bit <= cnt_bit + 1'b1;		
        end											
    assign add_bit_cnt = end_bps_cnt;
    assign end_bit_cnt = add_bit_cnt && cnt_bit == bit_max - 1;

    always@(*)
        case(state)
            IDLE    :	bit_max = 1;
            START   :   bit_max = 1;
            DATA    :   bit_max = 8;
            CHECK   :   bit_max = 1;
            default :	bit_max = 1;
        endcase

/**************************************************************
                        校验位处理
**************************************************************/
    reg         rev_check   ;
    wire        check_val   ;

//接收校验位
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            rev_check <= 0;
        else if(state == CHECK && cnt_bps == BPS_MAX >> 1)
            rev_check <= rx_r1;

//计算校验位
    assign check_val = (CHECK_BIT == "Odd")? ~^rx_temp : ^rx_temp;

/**************************************************************
                        实现接收数据
**************************************************************/
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            rx_temp <= 0;
        else if(state == DATA && cnt_bps == BPS_MAX >> 1)
            rx_temp[cnt_bit] <= rx_r1;

    assign rx_data = rx_temp;
    assign rx_data_vld  =   (CHECK_BIT == "None")? data2idle                //无校验方式输出数据有效信号
                        :   (check2idle && (check_val == rev_check))? 1     //有校验方式时,计算的校验值与接收的校验值一致,才能输出有效信号
                        :   0;

    assign ready = state == IDLE;

endmodule

(3)ctrl

module ctrl(
    input               uart_clk        ,   //50m
    input               rst_n           ,

    //TX通道
    input               user_tx_clk     ,   //比如80m
    input       [7:0]   user_tx_data    ,
    input               user_tx_data_vld,
    output              user_tx_ready   ,


    //RX通道
    input               user_rx_clk     ,
    output              user_rx_ready   ,   //表示串口已经收到数据了,可以读出
    input               user_rx_rd_req  ,
    output      [7:0]   user_rx_data    ,
    output              user_rx_data_vld,

    //与UART接口交互
    input       [7:0]   rx_data         ,
    input               rx_data_vld     ,

    output      [7:0]   tx_data         ,
    output              tx_data_vld     ,
    input               tx_ready        
);

/**************************************************************
                    实现TX通道的缓存、跨时钟域
**************************************************************/
    wire        tx_fifo_empty;
    wire        tx_fifo_full;

    uart_fifo	tx_fifo (
        .aclr       ( ~rst_n        ),

        .data       ( user_tx_data  ),
        .wrclk      ( user_tx_clk   ),
        .wrreq      ( user_tx_data_vld && !tx_fifo_full ),

        .rdclk      ( uart_clk      ),
        .rdreq      ( tx_ready && !tx_fifo_empty        ),
        .q          ( tx_data       ),

        .rdempty    ( tx_fifo_empty ),
        .wrfull     ( tx_fifo_full ) 
    );

    assign tx_data_vld = tx_ready && !tx_fifo_empty;
    assign user_tx_ready = !tx_fifo_full;               //只要fifo没有满,就可以继续接收需要发送的数据


/**************************************************************
                    实现RX通道的缓存、跨时钟域
**************************************************************/
    wire        rx_fifo_empty;
    wire        rx_fifo_full;

    wire        rx_fifo_rd_req;

    uart_fifo	rx_fifo (
        .aclr       ( ~rst_n        ),

        .data       (rx_data        ),
        .wrclk      (uart_clk       ),
        .wrreq      (rx_data_vld && !rx_fifo_full),

        .rdclk      (user_rx_clk    ),
        .rdreq      (rx_fifo_rd_req ),
        .q          (user_rx_data   ),

        .rdempty    ( rx_fifo_empty ),
        .wrfull     ( rx_fifo_full  ) 
    );

    assign rx_fifo_rd_req = user_rx_rd_req && !rx_fifo_empty;
    assign user_rx_ready = !rx_fifo_empty;
    assign user_rx_data_vld = rx_fifo_rd_req;

endmodule

(4)fifo.v

这个是这个IP核内部需要用到的FIFO,直接找.v文件,一起加进Quartus中编译就行。

// megafunction wizard: %FIFO%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: dcfifo 

// ============================================================
// File Name: uart_fifo.v
// Megafunction Name(s):
// 			dcfifo
//
// Simulation Library Files(s):
// 			altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 18.1.0 Build 625 09/12/2018 SJ Standard Edition
// ************************************************************


//Copyright (C) 2018  Intel Corporation. All rights reserved.
//Your use of Intel Corporation's design tools, logic functions 
//and other software and tools, and its AMPP partner logic 
//functions, and any output files from any of the foregoing 
//(including device programming or simulation files), and any 
//associated documentation or information are expressly subject 
//to the terms and conditions of the Intel Program License 
//Subscription Agreement, the Intel Quartus Prime License Agreement,
//the Intel FPGA IP License Agreement, or other applicable license
//agreement, including, without limitation, that your use is for
//the sole purpose of programming logic devices manufactured by
//Intel and sold by Intel or its authorized distributors.  Please
//refer to the applicable agreement for further details.


// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module uart_fifo (
	aclr,
	data,
	rdclk,
	rdreq,
	wrclk,
	wrreq,
	q,
	rdempty,
	wrfull);

	input	  aclr;
	input	[7:0]  data;
	input	  rdclk;
	input	  rdreq;
	input	  wrclk;
	input	  wrreq;
	output	[7:0]  q;
	output	  rdempty;
	output	  wrfull;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
	tri0	  aclr;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif

	wire [7:0] sub_wire0;
	wire  sub_wire1;
	wire  sub_wire2;
	wire [7:0] q = sub_wire0[7:0];
	wire  rdempty = sub_wire1;
	wire  wrfull = sub_wire2;

	dcfifo	dcfifo_component (
				.aclr (aclr),
				.data (data),
				.rdclk (rdclk),
				.rdreq (rdreq),
				.wrclk (wrclk),
				.wrreq (wrreq),
				.q (sub_wire0),
				.rdempty (sub_wire1),
				.wrfull (sub_wire2),
				.eccstatus (),
				.rdfull (),
				.rdusedw (),
				.wrempty (),
				.wrusedw ());
	defparam
		dcfifo_component.intended_device_family = "Cyclone IV E",
		dcfifo_component.lpm_numwords = 512,
		dcfifo_component.lpm_showahead = "ON",
		dcfifo_component.lpm_type = "dcfifo",
		dcfifo_component.lpm_width = 8,
		dcfifo_component.lpm_widthu = 9,
		dcfifo_component.overflow_checking = "ON",
		dcfifo_component.rdsync_delaypipe = 4,
		dcfifo_component.read_aclr_synch = "OFF",
		dcfifo_component.underflow_checking = "ON",
		dcfifo_component.use_eab = "ON",
		dcfifo_component.write_aclr_synch = "OFF",
		dcfifo_component.wrsync_delaypipe = 4;


endmodule

// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: AlmostEmpty NUMERIC "0"
// Retrieval info: PRIVATE: AlmostEmptyThr NUMERIC "-1"
// Retrieval info: PRIVATE: AlmostFull NUMERIC "0"
// Retrieval info: PRIVATE: AlmostFullThr NUMERIC "-1"
// Retrieval info: PRIVATE: CLOCKS_ARE_SYNCHRONIZED NUMERIC "0"
// Retrieval info: PRIVATE: Clock NUMERIC "4"
// Retrieval info: PRIVATE: Depth NUMERIC "512"
// Retrieval info: PRIVATE: Empty NUMERIC "1"
// Retrieval info: PRIVATE: Full NUMERIC "1"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
// Retrieval info: PRIVATE: LE_BasedFIFO NUMERIC "0"
// Retrieval info: PRIVATE: LegacyRREQ NUMERIC "0"
// Retrieval info: PRIVATE: MAX_DEPTH_BY_9 NUMERIC "0"
// Retrieval info: PRIVATE: OVERFLOW_CHECKING NUMERIC "0"
// Retrieval info: PRIVATE: Optimize NUMERIC "0"
// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: UNDERFLOW_CHECKING NUMERIC "0"
// Retrieval info: PRIVATE: UsedW NUMERIC "1"
// Retrieval info: PRIVATE: Width NUMERIC "8"
// Retrieval info: PRIVATE: dc_aclr NUMERIC "1"
// Retrieval info: PRIVATE: diff_widths NUMERIC "0"
// Retrieval info: PRIVATE: msb_usedw NUMERIC "0"
// Retrieval info: PRIVATE: output_width NUMERIC "8"
// Retrieval info: PRIVATE: rsEmpty NUMERIC "1"
// Retrieval info: PRIVATE: rsFull NUMERIC "0"
// Retrieval info: PRIVATE: rsUsedW NUMERIC "0"
// Retrieval info: PRIVATE: sc_aclr NUMERIC "0"
// Retrieval info: PRIVATE: sc_sclr NUMERIC "0"
// Retrieval info: PRIVATE: wsEmpty NUMERIC "0"
// Retrieval info: PRIVATE: wsFull NUMERIC "1"
// Retrieval info: PRIVATE: wsUsedW NUMERIC "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
// Retrieval info: CONSTANT: LPM_NUMWORDS NUMERIC "512"
// Retrieval info: CONSTANT: LPM_SHOWAHEAD STRING "ON"
// Retrieval info: CONSTANT: LPM_TYPE STRING "dcfifo"
// Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "8"
// Retrieval info: CONSTANT: LPM_WIDTHU NUMERIC "9"
// Retrieval info: CONSTANT: OVERFLOW_CHECKING STRING "ON"
// Retrieval info: CONSTANT: RDSYNC_DELAYPIPE NUMERIC "4"
// Retrieval info: CONSTANT: READ_ACLR_SYNCH STRING "OFF"
// Retrieval info: CONSTANT: UNDERFLOW_CHECKING STRING "ON"
// Retrieval info: CONSTANT: USE_EAB STRING "ON"
// Retrieval info: CONSTANT: WRITE_ACLR_SYNCH STRING "OFF"
// Retrieval info: CONSTANT: WRSYNC_DELAYPIPE NUMERIC "4"
// Retrieval info: USED_PORT: aclr 0 0 0 0 INPUT GND "aclr"
// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]"
// Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]"
// Retrieval info: USED_PORT: rdclk 0 0 0 0 INPUT NODEFVAL "rdclk"
// Retrieval info: USED_PORT: rdempty 0 0 0 0 OUTPUT NODEFVAL "rdempty"
// Retrieval info: USED_PORT: rdreq 0 0 0 0 INPUT NODEFVAL "rdreq"
// Retrieval info: USED_PORT: wrclk 0 0 0 0 INPUT NODEFVAL "wrclk"
// Retrieval info: USED_PORT: wrfull 0 0 0 0 OUTPUT NODEFVAL "wrfull"
// Retrieval info: USED_PORT: wrreq 0 0 0 0 INPUT NODEFVAL "wrreq"
// Retrieval info: CONNECT: @aclr 0 0 0 0 aclr 0 0 0 0
// Retrieval info: CONNECT: @data 0 0 8 0 data 0 0 8 0
// Retrieval info: CONNECT: @rdclk 0 0 0 0 rdclk 0 0 0 0
// Retrieval info: CONNECT: @rdreq 0 0 0 0 rdreq 0 0 0 0
// Retrieval info: CONNECT: @wrclk 0 0 0 0 wrclk 0 0 0 0
// Retrieval info: CONNECT: @wrreq 0 0 0 0 wrreq 0 0 0 0
// Retrieval info: CONNECT: q 0 0 8 0 @q 0 0 8 0
// Retrieval info: CONNECT: rdempty 0 0 0 0 @rdempty 0 0 0 0
// Retrieval info: CONNECT: wrfull 0 0 0 0 @wrfull 0 0 0 0
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo_inst.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL uart_fifo_bb.v TRUE
// Retrieval info: LIB_FILE: altera_mf

(5)top

顶层代码

module uart_top#(
    parameter           CHECK_BIT   =   "None"      ,   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
    parameter           BPS         =   115200      ,   //UART波特率
    parameter           CLK         =   50000000        //工作时钟
    )(
    input               uart_clk        ,   //50m
    input               rst_n           ,

    //TX通道
    input               user_tx_clk     ,   //比如80m
    input       [7:0]   user_tx_data    ,
    input               user_tx_data_vld,
    output              user_tx_ready   ,

    //RX通道
    input               user_rx_clk     ,
    output              user_rx_ready   ,   //表示串口已经收到数据了,可以读出
    input               user_rx_rd_req  ,
    output      [7:0]   user_rx_data    ,
    output              user_rx_data_vld,

    input               uart_rxd        ,
    output              uart_txd    
);

    wire    [7:0]   rx_data         ;
    wire            rx_data_vld     ;
    wire    [7:0]   tx_data         ;
    wire            tx_data_vld     ;
    wire            tx_ready        ;

ctrl ctrl(
    /* input                */.uart_clk         (uart_clk           ),   //50m
    /* input                */.rst_n            (rst_n              ),

    /* input                */.user_tx_clk      (user_tx_clk        ),   //比如80m
    /* input       [7:0]    */.user_tx_data     (user_tx_data       ),
    /* input                */.user_tx_data_vld (user_tx_data_vld   ),
    /* output               */.user_tx_ready    (user_tx_ready      ),

    /* input                */.user_rx_clk      (user_rx_clk        ),
    /* output               */.user_rx_ready    (user_rx_ready      ),   //表示串口已经收到数据了,可以读出
    /* input                */.user_rx_rd_req   (user_rx_rd_req     ),
    /* output      [7:0]    */.user_rx_data     (user_rx_data       ),
    /* output               */.user_rx_data_vld (user_rx_data_vld   ),

    /* input       [7:0]    */.rx_data          (rx_data            ),
    /* input                */.rx_data_vld      (rx_data_vld        ),
    /* output      [7:0]    */.tx_data          (tx_data            ),
    /* output               */.tx_data_vld      (tx_data_vld        ),
    /* input                */.tx_ready         (tx_ready           )
);

    uart_tx #(
        .CHECK_BIT  (CHECK_BIT  ),   //"None" 偶校验,"Odd" 奇校验,"Even" 偶校验
        .BPS        (BPS        ),   //UART波特率
        .CLK        (CLK        )    //工作时钟
    )uart_tx_inst(
        /* input                */.clk          (uart_clk       ),
        /* input                */.rst_n        (rst_n          ),
        /* input       [7:0]    */.tx_data      (tx_data        ),
        /* input                */.tx_data_vld  (tx_data_vld    ),
        /* output               */.ready        (tx_ready       ),
        /* output  reg          */.tx           (uart_txd       )
    );

    uart_rx #(
        .CHECK_BIT  (CHECK_BIT  ),   //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
        .BPS        (BPS        ),   //UART波特率
        .CLK        (CLK        )    //工作时钟
    )uart_rx_inst(
        /* input                */.clk          (uart_clk       ),
        /* input                */.rst_n        (rst_n          ),
        /* output      [7:0]    */.rx_data      (rx_data        ),
        /* output               */.rx_data_vld  (rx_data_vld    ),
        /* output               */.ready        (               ),
        /* input                */.rx           (uart_rxd       )
    );

endmodule


二、IP封装流程

(1)创建工程

首先创建一个工程,命名随意,不过尽量言简意赅。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(2)打开 Platform Designer

空工程创建完毕,接下来打开 Platform Designer。图中圈起来的那个图标。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(3)双击New Component

双击New Component ,进入创建新IP的界面。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(4)修改IP名字

可以自己设定IP核的名字。还是那句话,言简意赅。然后点击上边的files。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(5)添加.v以及头文件

点击Add Files ,将上边的代码文件加进去。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

将文件加入以后,点击top文件的“Attributes”一列,将其设置为顶层。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

点击编译一下这些代码文件。等待编译完成。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

编译完成后,不用管下面的错误,点击close,进入下一步。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(6)引脚分组

点击上边框的最后一个,进入对引脚的配置。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

1、点击“add interface”添加接口

FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

2、改变接口类型

添加接口的同时更改名字和类型
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

完成后一共有四个接口:
1、时钟类型的接口,名字改成clk ,添加时选择类型为clk input,并且将“uart_clk”拖动到里边。
2、复位接口,名字改成rst_n,添加时选择类型为reset input,并且将“rst_n”拖到里边。
3、管道类型接口有两个,(1)名字改为uart,添加时选择类型为conduit,并且将“uart_txd”“uart_rxd”拖进里边(2)名字改为conduitend(接口名字随意,只要类型和包含的信号对就行),类型也是conduit,剩余的信号全都在里边。

还有一个重要的就是将管道接口的时钟和复位改一下,就在图中画星星的旁边,不要是“none”,连接时钟接口和复位接口。

FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

然后将管道信号中的每个信号后边的字符改变一下。直接将name复制进去就行。记住,只用改管道接口里的信号。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

最后就是像下图这样,接口就算配置完成了。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
在改动接口的过程中就能注意到,下面的报错在减少。等配置完报错就没了。就说明已经配置完成了,仔细检查一遍没有错误了就点击Finish完成,返回quartus的主页面。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

(7)quartus检测IP核

回到主页面后点击“Tool”–>“Options”–>“IP setting”–>“IP catalog”。

FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

点击右边的三个点,然后选择存放此工程的文件夹,且点击add。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
这一步是为了让quartus检测到我们创建的IP核,不然就只能在这个工程里用。如下图,随便打开一个工程就能看到自己封装的IP核。

FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议

三、调用方法

完成了IP核的封装,接下来就是调用了。

随便打开一个工程,然后点击“Platfrom Designer”,或者点击右边的IP核。
就会弹出下图一样的画面。修改IP的名字以及ip存放的地址。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
然后就是可以修改我们需要的校验方式,波特率和时钟频率。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
然后按照图中的顺序点击按钮。如果第三步没有报错就说明之前配置IP核的步骤没问题。点击完后等待一会。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
这里同样,只要没有报错就没有问题,关闭就行,最后点击Finish。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
然后就可以在代码里找到例化的模版。这里例化进顶层进行连线就行,之后我会发一个文章仔细讲解,现在先弄好IP核就行。

FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议


总结

IP核的封装完成了,接下来就是调用,等几天我发出来具体的方法。

代码放进了baidu网盘里,去找就行。
FPGA实战 -- UART --- 封装UART IP核,FPGA实战,fpga开发,网络协议
链接:https://pan.baidu.com/s/107QVaN3du91LhQbFuL3IRQ?pwd=yang
提取码:yang

如果有错误的话就加qq问我,号码在上一篇写了。文章来源地址https://www.toymoban.com/news/detail-834451.html

到了这里,关于FPGA实战 -- UART --- 封装UART IP核的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA(Verilog)实现uart传输协议传输数据(含仿真)

    目录 实现功能: 1.接收uart串行数据,输出并行数据(1byte)。 2.输入并行数据(1byte),输出uart串行数据。 3.完成uart传输的1次环回。 uart协议的1帧数据传输 模块封装-port设置 Verilog代码实现 1.uart接收模块:接收串行数据,输出并行数据和其有效标志。 仿真结果: 2.uart发送模块:接收

    2024年04月16日
    浏览(34)
  • FPGA:三大协议(UART、IIC、SPI)之SPI

    摘要:1、本文介绍SPI物理层面连接(通过哪几条线通信),2、本文介绍SPI时序(通过哪种方式进行器件之间交流)。3、提供主机和从机verilog代码。4、仅供自己参考 一、SPI物理层连接 (1)有四根线连接:CS_N(片选信号--主机发出)、miso(从机发出,主机接收)、mosi(主机发

    2024年02月14日
    浏览(29)
  • FPGA:三大协议(IIC、UART、SPI)之IIC

    摘要:1、本文讲述IIC的物理层面的结构(使用iic工作的物理层面的连接);2、本文讲解协议层面的通信交流格式(IIC时序);3、提供一个主机和从机的一个verilog代码;4、本文的主从机指的是:板子一号作为主机,发送数据给作为从机的板子二号;注意:在实际应用中,一

    2024年02月06日
    浏览(34)
  • FPGA实战 -- UART --- 实现串口回环(加FIFO)

    FPGA基础 – 通信协议 — 了解UART以及电脑串口环境准备 咱们上一文章学过了UART的基础,已经初步了解了协议的基础知识和时序要求,接下来就是将学到的写出东西来,本文通过上位机的串口助手发送数据,FIFO将数据在RX和TX之间进行传递,并且传递回上位机的串口助手,修改

    2024年02月20日
    浏览(34)
  • FPGA协议篇:UART通信及Verilog最易懂实现方式/通用于任何工程/带握手信号 ----UART_TX

            UART(Universal Asynchronous Receiver/Transmitter)是一种通用的 异步收发传输协议 ,用于在计算机系统和外部设备之间进行串行数据传输。UART 协议定义了数据的传输格式和通信规则,使得不同设备之间能够进行可靠的数据交换。 首先先把设计代码放到这里: UART_TX完整代

    2024年03月27日
    浏览(40)
  • FPGA开发基础篇之一(接口篇)UART串口

    写在前面 从本文开始,将连载fpga开发基础知识,将这几年浅显的fpga开发经验整理出来,一是梳理一下这几年给别人做fpga的经历,同时也是分享给大家,也希望大牛批评指正。 一、UART串口通信基本概念 串口通信是非常基本且应用十分广泛的低速通信接口,无论是在dsp、单片

    2024年02月02日
    浏览(50)
  • FPGA-结合协议时序实现UART收发器(五):串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive

    串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive,功能实现。 对照代码,串口发送模块UART_TOP实现功能包括: PLL锁相环,实现稳定系统输入时钟功能 UART_FIFO,数据先进先出,实现数据缓存功能,防止出现数据错乱 w_clk_rst = ~w_system_pll_locked;保证复位电平是先高位再地位 r_use

    2024年02月08日
    浏览(52)
  • 【FPGA开发】HDMI通信协议解析及FPGA实现

      笔者在这里使用的开发板是正点原子的达芬奇开发板,FPGA型号为XC7A35TFGG484-2。参考的课程是正点原子的课程手把手教你学达芬奇达芬奇Pro之FPGA开发篇。   HDMI,全称为High Definition Multimedia Interface,即高清多媒体接口。它不仅可以传输视频信号,还可以传输音频信号。上

    2024年02月21日
    浏览(31)
  • FPGA入门 —— FPGA UART 串口通信

    UART 通用异步收发传输器( Universal Asynchronous Receiver/Transmitter) ,通常称作 UART。 UART 是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。 它包括了ch340、 RS232、

    2024年02月09日
    浏览(36)
  • 网络编程1—— IP地址 + 端口号 +TCP/IP协议 + 协议分层的封装与应用

    本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下! 从本篇文章开始就要分享网络编程的内容了,越发的感受到了编程的魅力,鸡汤来喽!! 1️⃣单机阶段:计算机跟计算

    2024年02月12日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包