滨州建设厅网站石家庄网站编辑

当前位置: 首页 > news >正文

滨州建设厅网站,石家庄网站编辑,体育馆做网站公司,ps做的网站文章目录 前言一、UART通信协议1.1 通信格式2.2 MSB或LSB2.3 奇偶校验位2.4 UART传输速率 二、UART通信回环2.1 系统架构设计2.2 fsm_key2.3 baud2.4 sel_seg2.5 fifo2.6 uart_rx2.7 uart_tx2.8 top_uart2.9 发送模块时序分析2.10 接收模块的时序分析2.11 FIFO控制模块时序分析… 文章目录 前言一、UART通信协议1.1 通信格式2.2 MSB或LSB2.3 奇偶校验位2.4 UART传输速率 二、UART通信回环2.1 系统架构设计2.2 fsm_key2.3 baud2.4 sel_seg2.5 fifo2.6 uart_rx2.7 uart_tx2.8 top_uart2.9 发送模块时序分析2.10 接收模块的时序分析2.11 FIFO控制模块时序分析 三、仿真3.1 testbench3.2 接收模块仿真分析3.3 发送模块仿真分析3.4 FIFO控制模块仿真分析 四、上板验证五、总结 前言 本篇博客的实验内容是实现uart串口通信发送数据和接收数据并加入按键模块调整波特率数码管模块显示波特率实现不同波特率下的PC端与FPGA板子的数据回环并加入FIFO IP核对数据进行缓存当数据大于等于8个的时候一次性发送8个数据出去。 实验环境quartus 18.1 modelsim Cyclone IV开发板 一、UART通信协议 UART 即通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)是一种串行、异步、全双工的通信协议。特点是通信线路简单适用于远距离通信但传输速度慢。它在发送数据时将并行数据转换成串行数据来传输在接收数据时将接收到的串行数据转换成并行数据。 1.1 通信格式 它的一帧数据由4部分组成 起始位1bit 数据位(6\7\8 bit) 奇偶校验位(1bit) 停止位(1bit\1.5bit\2bit) 注意它在空闲状态下为高电平起始位为低电平停止位为高电平。它的数据位甚至可以为5位或者4位但是不能为9位及以上。 为什么起始位是低电平 因为它的空闲位为高电平必须有一个由高到低的下降沿变化才能知道后面要传输数据了不然如果起始位为高电平那它怎么知道你何时传数据呢 2.2 MSB或LSB 它的数据位传输顺序可以采用MSB也可以采用LSB由通信双方决定。 MSB数据高位先发送 LSB数据低位先发送 在本次实验的工程里面采用的是LSB的格式传输。 2.3 奇偶校验位 奇校验当实际数据中“1”的个数为奇数的时候这个校验位就是“0”否则这个校验位就是“1” 偶校验当实际数据中“1”的个数为偶数的时候这个校验位就是“0”否则这个校验位就是“1”。 注意奇偶校验只能检验奇数个数据的错误对于偶数个数据的错误无法检测。 本次实验没有用到奇偶校验因此在这里单独进行说明 在接收模块中接收串行数据的校验方法 奇校验实现方式校验位默认为高电平每检测到1则状态翻转 偶校验实现方式校验位默认为低电平每检测到1则状态翻转 核心代码 在接收模块接收并行数据进行校验 偶校验将输入数据按位异或 奇校验将输入数据按位异或再取反与偶校验相反 核心代码
2.4 UART传输速率 UART的数据传输速率用波特率baud表示常见的波特率有9600、19200、38400、57600、115200最常用的是9600和115200。 通信双方必须保持一致的波特率不然数据就会出错这个在最后我们的上板验证会体现。 以9600为例它代表我们传输1bit数据所需要的时间是1/9600104160ns我们的时钟周期为20ns所以传输1bit数据需要104160/20 5208个时钟周期。 注意波特率和比特率的区别 比特率是指每秒传送的比特(bit)数。单位为 bps(Bit Per Second)比特率越高每秒传送数据就越多。 波特率在电子通信领域波特Baud即调制速率指的是有效数据信号调制载波的速率即单位时间内载波调制状态变化的次数。它是对符号传输速率的一种度量1波特即指每秒传输1个符号而通过不同的调制方式可以在一个码元符号上负载多个bit位信息。 在信息传输通道中携带数据信息的信号单元叫码元每秒钟通过信道传输的码元数称为码元传输速率简称波特率。波特率是传输通道频宽的指标。 比特率波特率x单个调制状态对应的二进制位数。 二、UART通信回环 2.1 系统架构设计 该系统分为了6个模块分别是按键消抖模块(fsm_key)、波特率设置模块(baud)、数码管显示模块sel_seg、接收模块(uart_rx)、FIFO数据缓存模块(fifo)以及发送模块(uart_tx)。 首先是通过按键调整波特率输入的按键信号进入按键消抖模块然后将消抖后的按键信号传给波特率设置模块通过按键信号可以设置不同的波特率有常用的波特率选择9600、19200、38400、57600、115200。默认情况下是9600。然后把设置好的波特率传给数码管显示模块进行波特率的显示。同时将设置好的波特率传给接收和发送模块两个模块的波特率应该保持一致。同时接收模块接收了外界传入的串行数据后将串行数据转成并行数据传给FIFO模块进行数据缓存当FIFO里面缓存的数据大于等于8个数据时FIFO模块将开启数据发送使能信号然后把读取到的一个数据传给发送模块然后发送模块再把并行数据转成串行数据一个一个的发送出去。每发送完一个数据后都拉高一个数据发送完成的使能信号传给FIFO模块FIFO在根据这个使能信号再读一个数据传给发送模块进行数据发送重复该步骤直到发送出8个数据为止。 2.2 fsm_key //三段式状态机实现按键消抖 module fsm_key #(parameter CNT_20MS 26d1_000_000,KEY_NUM 3d1)(//20MS计数器和按键的数量input clk ,//时钟信号input rst_n ,//复位信号input [KEY_NUM-1:0] key_in ,//按键输入信号output reg [KEY_NUM-1:0] key_out //按键输出信号 );parameter IDLE 4b0001, //空闲状态DOWN 4b0010, //按键按下抖动状态HOLD 4b0100, //消抖后保持低电平有效状态UP 4b1000; //释放抖动状态reg [3:0] state_c;//现态reg [3:0] state_n;//次态reg [25:0] cnt_20ms;//20ms计数寄存器wire add_cnt_20ms;//开始计数wire end_cnt_20ms;//结束计数reg [KEY_NUM-1:0] key_r0;//同步reg [KEY_NUM-1:0] key_r1;//打拍wire [KEY_NUM-1:0] nedge;//下降沿wire [KEY_NUM-1:0] podge;//上升沿//第一段状态机时序逻辑描述状态空间的切换always (posedge clk or negedge rst_n) beginif(!rst_n)beginstate_c IDLE;endelse beginstate_c state_n;endend//第二段状态机组合逻辑描述状态转移条件always () begincase (state_c)IDLE:beginif(nedge)begin //检测到下降沿进入抖动状态state_n DOWN;endelse beginstate_n state_c;endendDOWN:beginif(podge)begin //检测到上升沿证明是一个抖动回到空闲状态state_n IDLE;endelse if(end_cnt_20ms podge 1b0)begin//计时结束并且没有出现上升沿就证明是一个有效按下进入下一个状态state_n HOLD;endelse beginstate_n state_c;endendHOLD:beginif(podge)begin//出现上升沿进入释放抖动状态state_n UP;endelse beginstate_n state_c;endendUP :beginif(end_cnt_20ms)begin//延时结束进入空闲状态state_n IDLE;endelse beginstate_n state_c;endend default: state_n state_c;endcaseend//第三段状态机组合时序都可以描述输出always (posedge clk or negedge rst_n) beginif(!rst_n)beginkey_out {KEY_NUM{1b0}};endelse if(state_c DOWN end_cnt_20ms)beginkey_out ~key_r1;endelse beginkey_out {KEY_NUM{1b0}};endend//打拍寄存always (posedge clk or negedge rst_n) beginif(!rst_n) beginkey_r0 {KEY_NUM{1b1}}; //{3{1b1}} - 3b111 {}拼接符号key_r1 {KEY_NUM{1b1}};endelse beginkey_r0 key_in;key_r1 key_r0; endend// always (posedge Clk or negedge Rst_n)begin // if(!Rst_n)begin// key_r 10b11;// end // else begin// key_r {key_r[0],key_in}; //打拍寄存// end// end assign nedge ~key_r0 key_r1;//要用按位与因为不止一个按键信号assign podge ~key_r1 key_r0;//20MS计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_20ms 26d0;endelse if(add_cnt_20ms)beginif(end_cnt_20ms)begincnt_20ms CNT_20MS;endelse begincnt_20ms cnt_20ms 1d1;endendelse begincnt_20ms 26d0;endendassign add_cnt_20ms state_c DOWN || state_c UP;assign end_cnt_20ms add_cnt_20ms (cnt_20ms CNT_20MS - 1d1);endmodule2.3 baud module baud(input clk ,input rst_n ,input key ,//按键输入信号output reg [16:0] baud //波特率 );reg [2:0] cnt;//设置波特率的计数器0为9600,1为19200,2为38400,3为57600,4为115200always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt 3d0;endelse if(key)beginif(cnt 3d4)begincnt 3d0;endelse begincnt cnt 1d1;endendelse begincnt cnt;endendalways (posedge clk or negedge rst_n) beginif(!rst_n)beginbaud 17d9600;endelse begincase (cnt)0: baud 17d9600 ;1: baud 17d19200 ;2: baud 17d38400 ;3: baud 17d57600 ;4: baud 17d115200 ; default: baud 17d9600 ;endcaseendendendmodule2.4 sel_seg module sel_seg(input clk ,//系统时钟input rst_n ,//复位信号input [16:0] baud ,//波特率output reg [5:0] sel ,//位选信号output reg [7:0] seg //段选信号 );parameter ZERO 8b1100_0000 ,ONE 8b1111_1001 ,TWO 8b1010_0100 ,THREE 8b1011_0000 ,FOUR 8b1001_1001 ,FIVE 8b1001_0010 ,SIX 8b1000_0010 ,SEVEN 8b1111_1000 ,EIGHT 8b1000_0000 ,NINE 8b1001_0000 ,A 8b1000_1000 ,B 8b1000_0011 ,C 8b1100_0110 ,D 8b1010_0001 ,E 8b1000_0110 ,F 8b1000_1110 ;parameter CNT_20US 10d999 ;//20us需要1000个时钟周期reg [9:0] cnt_20us ;//20us计数寄存器wire add_cnt_20us ;//开始计数标志符wire end_cnt_20us ;//结束计数标志符reg [4:0] number ;//数码管要显示的数字//20us计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_20us 10d0;endelse if(add_cnt_20us)beginif(end_cnt_20us)begincnt_20us 10d0;endelse begincnt_20us cnt_20us 1d1;endendelse begincnt_20us cnt_20us;endendassign add_cnt_20us 1b1;assign end_cnt_20us add_cnt_20us cnt_20us CNT_20US;//位选信号控制每20us刷新一次always (posedge clk or negedge rst_n) beginif(!rst_n)beginsel 6b111_110;endelse if(end_cnt_20us)beginsel {sel[4:0],sel[5]};endelse beginsel sel;endend//每个数码管需要显示的数字always () begincase (sel)6b111_110: number baud / 100000 ;//最左侧数码管 6b111_101: number baud % 100000 / 10000 ;6b111_011: number baud % 10000 / 1000 ;6b110_111: number baud % 1000 / 100 ;6b101_111: number baud % 100 / 10 ; 6b011_111: number baud % 10 ;//最右侧数码管default: number 4d0;endcaseend//段选信号控制显示always (*) begincase (number)4d0: seg ZERO ;4d1: seg ONE ;4d2: seg TWO ; 4d3: seg THREE ;4d4: seg FOUR ;4d5: seg FIVE ;4d6: seg SIX ;4d7: seg SEVEN ;4d8: seg EIGHT ;4d9: seg NINE ;default:seg ZERO ; endcaseendendmodule2.5 fifo 这里的FIFO是调用的IP核使用的前显模式 module fifo(input clk ,input rst_n ,input [7:0] rx_dout ,//接收并行数据input dout_sign ,//接收完成input dout_sign_tx,output tx_req ,//发送请求拉高时开始发送output [7:0] tx_din ,//输入并行数据output empty ,//空标志output full ,//满标志output [6:0] usedw //已经用了多少空间 );reg tx_rd ;//读数据信号寄存reg flag ;//状态信号为0时缓存数据为1时发送数据8时为1;reg [3:0] cnt_tx ;//一次性发送8个数据出去wire add_cnt_tx ;//发送开始信号wire end_cnt_tx ;//发送结束信号 reg rdreq ;//读取数据请求 fifo_128x8 fifo_128x8_inst (.clock ( clk ),.data ( rx_dout ),.rdreq ( rdreq ),.wrreq ( dout_sign ),.empty ( empty ),.full ( full ),.q ( tx_din ),.usedw ( usedw ));//读取数据信号使能always (posedge clk or negedge rst_n) beginif(!rst_n)beginrdreq 1b0;endelse if((tx_rd || dout_sign_tx) !end_cnt_tx)beginrdreq 1b1;endelse beginrdreq 1b0;endend//当可度量达到标志时第一次读取数据使能信号always (posedge clk or negedge rst_n) beginif(!rst_n)begintx_rd 1b0;endelse if(!flag usedw 7d8)begintx_rd 1b1;endelse begintx_rd 1b0;endend//状态信号0为空闲状态1为读取数据状态 always (posedge clk or negedge rst_n) beginif(!rst_n)beginflag 1b0;endelse if(end_cnt_tx)beginflag 1b0;endelse if(!flag usedw 7d8)begin//空闲状态时检测剩余可读量达到标志flag 1b1;endelse beginflag flag;endend//8个数据计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_tx 3d0;endelse if(add_cnt_tx)beginif(end_cnt_tx )begincnt_tx 3d0;endelse begincnt_tx cnt_tx 1d1;endendelse begincnt_tx cnt_tx;endendassign add_cnt_tx flag dout_sign_tx;assign end_cnt_tx add_cnt_tx cnt_tx 3d7;assign tx_req rdreq;endmodule2.6 uart_rx module uart_rx(input clk ,input rst_n ,input rx_din ,//输入串行数据input [16:0] baud ,//波特率output [7:0] rx_dout ,//接收并行数据output reg dout_sign//接收完成 );parameter CNT_1S 26d50_000_000;// parameter BAUD 14d9600;reg [12:0] cnt_baud ;//波特率计数器wire add_baud ;//波特率开始计数wire end_baud ;//波特率结束计数reg [3:0] cnt_bit ;//比特计数器wire add_bit ;//比特开始计数wire end_bit ;//比特结束计数reg [9:0] rx_data ;//数据寄存器reg rx_din_r0 ;//同步reg rx_din_r1 ;//打拍wire nedge ;//起始位的下降沿reg rx_flag ;//开始接收数据标志//波特率计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_baud 13d0;endelse if(rx_flag 1b0)begincnt_baud 13d0;endelse if(add_baud)beginif(end_baud)begincnt_baud 13d0;endelse begincnt_baud cnt_baud 1d1;endendelse begincnt_baud cnt_baud;endendassign add_baud rx_flag;assign end_baud add_baud cnt_baud (CNT_1S/baud) - 1d1;//根据波特率求得传输1bit所需要的时钟周期//比特计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit 4d0;endelse if(rx_flag 1b0)begincnt_bit 13d0;endelse if(add_bit)beginif(end_bit)begincnt_bit 4d0;endelse begincnt_bit cnt_bit 1d1;endendelse begincnt_bit cnt_bit;endendassign add_bit end_baud;assign end_bit add_bit cnt_bit 4d9;//一个数据有1bit起始位8bit数据位1bit结束位//同步打拍检测下降沿always (posedge clk or negedge rst_n) beginif(!rst_n)beginrx_din_r0 1b1;rx_din_r1 1b1;endelse beginrx_din_r0 rx_din;rx_din_r1 rx_din_r0;endendassign nedge ~rx_din_r0 rx_din_r1;//同步打拍检测到下降沿开始接收数据//同步完成检测下降沿将开始的接收的标志置为1always (posedge clk or negedge rst_n) beginif(!rst_n)beginrx_flag 1b0;endelse if(nedge)beginrx_flag 1b1;endelse if(rx_data[0] 1b1 || end_bit)begin//开始位为1意外关闭rx_flag 1b0;endelse beginrx_flag rx_flag;endend//缓存接收到的数据always (posedge clk or negedge rst_n) beginif(!rst_n)beginrx_data 10b0;endelse if(rx_flag cnt_baud (CNT_1S/baud-1)/2)beginrx_data[cnt_bit] rx_din_r1;endelse beginrx_data rx_data;endend//结束位为1时才接收数据否则让数据为0视为无效数据assign rx_dout[7:0] rx_data[8:1] ;//接收到一个完整的有效数据后接收完成信号拉高always (posedge clk or negedge rst_n) beginif(!rst_n)begindout_sign 1b0;endelse if(end_bit rx_data[9] 1b1)begindout_sign 1b1;endelse begindout_sign 1b0;endendendmodule2.7 uart_tx module uart_tx (input clk ,input rst_n ,input tx_req ,//发送请求拉高时开始发送input [7:0] tx_din ,//输入并行数据input [16:0] baud ,//波特率output reg tx_dout,//发送串行数据,低位在前高位在后output dout_sign_tx //发送结束标志 );parameter CNT_1S 26d50_000_000;// parameter BAUD 14d9600;reg [12:0] cnt_baud ;//波特率计数器wire add_baud ;//波特率开始计数wire end_baud ;//波特率结束计数reg [9:0] tx_data ;//10bit数据reg tx_flag ;//发送标志reg [3:0] cnt_bit ;//比特计数器wire add_bit ;//比特开始计数wire end_bit ;//比特结束计数//波特率计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_baud 13d0;endelse if(add_baud)beginif(end_baud)begincnt_baud 13d0;endelse begincnt_baud cnt_baud 1d1;endendelse begincnt_baud cnt_baud;endendassign add_baud tx_flag;assign end_baud add_baud cnt_baud (CNT_1S/baud - 1d1);//根据波特率求得传输1bit所需要的时钟周期//比特计数器always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit 4d0;endelse if(add_bit)beginif(end_bit)begincnt_bit 4d0;endelse begincnt_bit cnt_bit 1d1;endendelse begincnt_bit cnt_bit;endendassign add_bit end_baud;assign end_bit add_bit cnt_bit 4d9;//一个数据有1bit起始位8bit数据位1bit结束位//发送请求拉高时寄存数据always (posedge clk or negedge rst_n) beginif(!rst_n)begintx_data 10b0;endelse if(tx_req)begintx_data {1b1,tx_din,1b0};endelse begintx_data tx_data;endend//发送请求拉高时保持发送信号拉高直到发送完毕always (posedge clk or negedge rst_n) beginif(!rst_n)begintx_flag 1b0;endelse if (tx_req)begintx_flag 1b1;endelse if(end_bit)begintx_flag 1b0;endelse begintx_flag tx_flag;endend//串行数据输出always (posedge clk or negedge rst_n) beginif(!rst_n)begintx_dout 1b1;endelse if(tx_flag cnt_baud)begintx_dout tx_data[cnt_bit];endelse begintx_dout tx_dout;endendassign dout_sign_tx end_bit;//直到没有再发送就结束了 endmodule2.8 top_uart module top_uart(input clk ,input rst_n ,input key ,//按键输入信号input rx ,//输入串行数据output tx ,//输出串行数据output [5:0] sel ,//位选信号output [7:0] seg //段选信号
);wire [7:0] rx_dout;//串转并数据wire rx_dout_sign;wire tx_dout_sign;wire tx_req;wire [7:0] tx_din;wire key_r;wire [16:0] baud_r;fsm_key fsm_key_inst(.clk (clk),//时钟信号.rst_n (rst_n),//复位信号.key_in (key),//按键输入信号.key_out (key_r) //按键输出信号);baud baud_inst(.clk (clk),.rst_n (rst_n),.key (key_r),//按键输入信号.baud (baud_r) //波特率);sel_seg sel_seg_inst(.clk (clk),//系统时钟.rst_n (rst_n),//复位信号.baud (baud_r),//波特率.sel (sel),//位选信号.seg (seg) //段选信号);uart_rx uart_rx_inst(.clk (clk ),.rst_n (rst_n ),.rx_din (rx ),.baud (baud_r ),.rx_dout (rx_dout ),.dout_sign (rx_dout_sign));fifo fifo_inst(.clk (clk),.rst_n (rst_n),.rx_dout (rx_dout),//接收并行数据.dout_sign (rx_dout_sign),//接收完成.dout_sign_tx (tx_dout_sign),.tx_req (tx_req),//发送请求拉高时开始发送.tx_din (tx_din) //输入并行数据// .empty (),//空标志// .full (),//满标志// .usedw () //已经用了多少空间);uart_tx uart_tx_inst(.clk (clk ),.rst_n (rst_n ),.tx_req (tx_req ),.tx_din (tx_din ),.baud (baud_r ),.tx_dout (tx ),.dout_sign_tx (tx_dout_sign));endmodule2.9 发送模块时序分析 从时序图看这里想要发送的并行数据是8’b11010011。首先是发送请求信号拉高此时发送标志信号拉高直到数据发送完成。采用一个10bit的寄存器对并行数据进行寄存同时由于是低位在前高位在后开始位为0停止位为1因此寄存的数据为10’b1110100110。同时在发送标志信号拉高时cnt_baud计数器开始计数该计数器是计的发送1bit数据所需要的系统时钟周期。计满时cnt_bit计数器加1然后发送的串行数据就是根据cnt_bit计数器对寄存的数据进行一个一个的发送。在出现发送停止位后就表明这个数据发送完成了因此发送结束标志信号dout_sign就拉高。 2.10 接收模块的时序分析 从这张时序图看收到了一串的串行数据首先需要对这一串数据进行同步打拍通过同步打拍判断出下降沿出现下降沿时开始接收数据的标志信号rx_flag拉高直到接收数据完成。然后依旧是两个计数器与发送模块的计数器功能一样cnt_baud和cnt_bit。然后采用一个10bit的数据寄存器rx_data。为了接收到的数据稳定因此采用当baud计数器过了一半时根据传入的串行数据打拍后的波形数据写入寄存器同时判断了开始位为1和停止位为0的话都属于无效数据不会接收无效数据。当数据有效时最终接收到的并行数据rx_dout就为rx_data寄存器的[8:1]位。同时接收完毕后将接收完成的标志信号拉高。 2.11 FIFO控制模块时序分析 当FIFO中缓存的数据大于等于8个数据时开启读取数据的状态开启读取第一个数据的使能信号将读取到的数据发给数据发送模块当数据发送模块将一个数据发送完成后将数据发送完成的标志信号dout_sign_tx传给FIFO控制模块cnt_tx计数器加1同时重新读取一个新的数据传给数据发送模块进行发送直到发送完8个数据才重新进入空闲状态。并且由于FIFO是使用的前显模式因此当读取数据有效时读取的数据是前面显示的那个数据。 三、仿真 3.1 testbench timescale 1ns/1ns module top_uart_tb();reg clk ;reg rst_n ;reg rx ;reg key_r ;wire tx ;defparam uart_rx_inst.CNT_1S 26d50;defparam uart_tx_inst.CNT_1S 26d50;parameter CYCLE 20;wire [7:0] rx_dout;//串转并数据wire rx_dout_sign;wire tx_dout_sign;wire tx_req;wire [7:0] tx_din;wire [6:0] usedw ;wire [16:0] baud_r;reg [7:0] test ;baud baud_inst(.clk (clk),.rst_n (rst_n),.key (key_r),//按键输入信号.baud (baud_r) //波特率);uart_rx uart_rx_inst(.clk (clk ),.rst_n (rst_n ),.rx_din (rx ),.baud (baud_r ),.rx_dout (rx_dout ),.dout_sign (rx_dout_sign));fifo fifo_inst(.clk (clk),.rst_n (rst_n),.rx_dout (rx_dout),//接收并行数据.dout_sign (rx_dout_sign),//接收完成.dout_sign_tx (tx_dout_sign),.tx_req (tx_req),//发送请求拉高时开始发送.tx_din (tx_din), //输入并行数据// .empty (),//空标志// .full (),//满标志.usedw (usedw) //已经用了多少空间);uart_tx uart_tx_inst(.clk (clk ),.rst_n (rst_n ),.tx_req (tx_req ),.tx_din (tx_din ),.baud (baud_r ),.tx_dout (tx ),.dout_sign_tx (tx_dout_sign));always #(CYCLE/2) clk ~clk;initial beginclk 1b0;rst_n 1b0;test 8b0;#(CYCLE/2 3);rst_n 1b1;repeat(8)begintest test 1b1;rx 1b0;#(5*CYCLE);rx test[0];#(5*CYCLE);rx test[1];#(5*CYCLE);rx test[2];#(5*CYCLE);rx test[3];#(5*CYCLE);rx test[4];#(5*CYCLE);rx test[5];#(5*CYCLE);rx test[6];#(5*CYCLE);rx test[7];#(5*CYCLE);rx 1b1;#(30*CYCLE);end#(2000*CYCLE);$stop;endendmodule3.2 接收模块仿真分析 从这张仿真波形图可以看到发送的标志信号拉高后两个计数器正常工作然后10bit的数据寄存器rx_data。在cnt_baud计数器过了一半时才会根据传入的串行数据打拍后的波形数据写入寄存器同时判断了开始位为1和停止位为0的话都属于无效数据不会接收无效数据。当数据有效时最终接收到的并行数据rx_dout就为rx_data寄存器的[8:1]位。同时接收完毕后将接收完成的标志信号拉高。 3.3 发送模块仿真分析 从这张仿真波形图可以看到当发送请求信号tx_req拉高后发送标志信号tx_flag拉高直到发送完毕。同时10bit的数据寄存器将tx_din的并行数据存入并加上开始位0停止位1。然后两个计数器开始正常计数。然后发送的串行数据tx_dout就是根据cnt_bit计数器对寄存的数据进行一个一个的发送。在出现发送停止位后就表明这个数据发送完成了因此发送结束标志信号dout_sign就拉高。 3.4 FIFO控制模块仿真分析 从这张仿真波形图可以看出当FIFO中缓存的数据大于等于8个数据时开启读取数据的状态开启读取第一个数据的使能信号将读取到的数据发给数据发送模块当数据发送模块将一个数据发送完成后将数据发送完成的标志信号dout_sign_tx传给FIFO控制模块cnt_tx计数器加1同时重新读取一个新的数据传给数据发送模块进行发送直到发送完8个数据才重新进入空闲状态。并且由于FIFO是使用的前显模式因此当读取数据有效时读取的数据是前面显示的那个数据。 四、上板验证 从图片中可以看出当发送数据小于8个时它将数据接收并存入了FIFO缓冲器。当再次输入几个数据时它会一次性输出8个数据并且先输出之前存入的数据。当我们切换了波特率PC端设置的波特率与FPGA板子上设置的波特率不同时我们发送的数据就会解析混乱变成一些乱码存入缓存器然后达到8个数据时输出。当我们重新调整波特率使得双方波特率一致后又能够收发同步了。 五、总结 因为该工程是分模块化的设计因此如果只需要串口的发送和接收功能就只需要将这两个模块直接拿去用就可以了大大方便了之后其他工程的设计。另外这只是我的一点学习笔记如果有错误还请提出。