系列文章目录
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文章来源:https://www.toymoban.com/news/detail-834451.html
如果有错误的话就加qq问我,号码在上一篇写了。文章来源地址https://www.toymoban.com/news/detail-834451.html
到了这里,关于FPGA实战 -- UART --- 封装UART IP核的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!