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

系列文章目录

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



前言

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


一、代码

首先创建一个文件夹,记得找一个不会删的地址来创建。因为自己封装的IP核需要Quartus检测这个文档才能使用,所以放在一个不会动的文件夹里比较好。我建议直接放进Quartus的安装文件里。
在这里插入图片描述
这个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)创建工程

首先创建一个工程,命名随意,不过尽量言简意赅。
在这里插入图片描述

(2)打开 Platform Designer

空工程创建完毕,接下来打开 Platform Designer。图中圈起来的那个图标。
在这里插入图片描述

(3)双击New Component

双击New Component ,进入创建新IP的界面。
在这里插入图片描述

(4)修改IP名字

可以自己设定IP核的名字。还是那句话,言简意赅。然后点击上边的files。
在这里插入图片描述

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

点击Add Files ,将上边的代码文件加进去。
在这里插入图片描述

将文件加入以后,点击top文件的“Attributes”一列,将其设置为顶层。
在这里插入图片描述

点击编译一下这些代码文件。等待编译完成。
在这里插入图片描述

编译完成后,不用管下面的错误,点击close,进入下一步。
在这里插入图片描述

(6)引脚分组

点击上边框的最后一个,进入对引脚的配置。
在这里插入图片描述

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

在这里插入图片描述

2、改变接口类型

添加接口的同时更改名字和类型
在这里插入图片描述

完成后一共有四个接口:
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”,连接时钟接口和复位接口。

在这里插入图片描述

然后将管道信号中的每个信号后边的字符改变一下。直接将name复制进去就行。记住,只用改管道接口里的信号。
在这里插入图片描述

最后就是像下图这样,接口就算配置完成了。
在这里插入图片描述
在改动接口的过程中就能注意到,下面的报错在减少。等配置完报错就没了。就说明已经配置完成了,仔细检查一遍没有错误了就点击Finish完成,返回quartus的主页面。
在这里插入图片描述

(7)quartus检测IP核

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

在这里插入图片描述

点击右边的三个点,然后选择存放此工程的文件夹,且点击add。
在这里插入图片描述
在这里插入图片描述
这一步是为了让quartus检测到我们创建的IP核,不然就只能在这个工程里用。如下图,随便打开一个工程就能看到自己封装的IP核。

在这里插入图片描述

三、调用方法

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

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

在这里插入图片描述


总结

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

代码放进了baidu网盘里,去找就行。
在这里插入图片描述
链接:https://pan.baidu.com/s/107QVaN3du91LhQbFuL3IRQ?pwd=yang
提取码:yang

如果有错误的话就加qq问我,号码在上一篇写了。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值