【FPGA/verilog -入门学习17】vivado 实现串口自发自收程序

1,需求

PC使用串口助手给FPGA板发送9600 波特率的数据,FPGA板接收到数据后,回复同样的数据给PC

2,需求分析

按模块可以划分为:

rx接收模块,将输入的8位并行rx 数据转换成[7:0]rx_data 信号,当数据接收完成后,同时生成一个rx_done 信号。

bsp_generate_clk_en:接收波特率时钟产生模块,当rx接收到数据时,给一个start信号给波特率时钟产生模块,由bsp时钟产生模块按9600波特率产生时钟使能信号bsp_generate_clk_en 用于同步rx 接收模块接收数据的时序。

tx发送模块:当接收到来子rx 接收模块发过来的rx_done 信号后,将[7:0]rx_data 的数据按波特率时钟产生模块的顺序依次发送出去。

bsp_generate_clk_en:发送波特率时钟产生模块,功能同接收模块

3,绘制框图和时序图

4,编写发送tx 模块,并编写行为测试模块代码测试其功能

`timescale 1ns/1ps

module testbench_top();
    

//参数定义

`define CLK_PERIORD        20        //时钟周期设置为20ns(50MHz)    


//接口申明
reg i_clk;
reg i_rest_n;
 
wire o_uart_tx;
wire o_uart_tx_done;


   

 vlg_design vlg_design_inst(
    .i_clk(i_clk),
    .i_rest_n(i_rest_n),
    .o_uart_tx(o_uart_tx),
    .o_uart_tx_done(o_uart_tx_done)
  
 );
 
initial begin
 
i_clk <= 0;
i_rest_n <= 0;
#20;
i_rest_n <= 1;


#2_000_000_000;
$stop;
 
end

always #(`CLK_PERIORD/2) i_clk = ~i_clk;
 
endmodule

//my_uart_tx
//put o_rx_data to i_uart_tx
  
`timescale 1ns/1ps
module vlg_1ms(
    input i_clk,  //25Mhz
    input i_rest_n,
    output [7:0]o_data, 
    output reg tx_en
    );

localparam CNT_1MS_MAX = 25*2000 - 1;

wire clk_25m;
wire clk_12r5m;
wire clk_100m;
wire locked;
 
clk_wiz_0 instance_name
(
    // Clock out ports
    .clk_out1(clk_25m),     // output clk_out1
    .clk_out2(clk_12r5m),     // output clk_out2
    .clk_out3(clk_100m),     // output clk_out3
    // Status and control signals
    .reset(!i_rest_n), // input reset
    .locked(locked),       // output locked
    // Clock in ports
    .clk_in1(i_clk) // input clk_in1
);      

// 每隔1ms 产生一个 tx_en 上升沿脉冲  
reg [15:0] r_cnt_1ms;    
always @(posedge i_clk) begin
    if(!i_rest_n) r_cnt_1ms <= 0;
    else if(r_cnt_1ms < CNT_1MS_MAX)  r_cnt_1ms <= r_cnt_1ms + 1;
    else r_cnt_1ms <= 0;
end    
    
always @(posedge i_clk) begin
    if(!i_rest_n) tx_en <= 0;
    else if(r_cnt_1ms == CNT_1MS_MAX)  tx_en <= 1;
    else tx_en <= 0;
end    
reg [7:0]r_data;
// 每隔1ms ,data = 0000_0001 自增 
always @(posedge i_clk) begin
    if(!i_rest_n)  r_data <= 0;
    else if(r_data == 100) r_data <= 0;
    else if(r_cnt_1ms == CNT_1MS_MAX) r_data <= r_data+1;
    else ;
    end

assign o_data = r_data;




endmodule

//my_uart_tx
//put o_rx_data to i_uart_tx
  
`timescale 1ns/1ps
module vlg_design(
    input i_clk,  //25Mhz
    input i_rest_n,
    output  o_uart_tx,    
    output  o_uart_tx_done 
    );


wire clk_25m;
wire clk_12r5m;
wire clk_100m;
wire locked;
 
wire tx_bps_start;
wire o_bps_clk_en;

wire [7:0]w_data;
wire w_tx_en;

  clk_wiz_0 instance_name
   (
    // Clock out ports
    .clk_out1(clk_25m),     // output clk_out1
    .clk_out2(clk_12r5m),     // output clk_out2
    .clk_out3(clk_100m),     // output clk_out3
    // Status and control signals
    .reset(!i_rest_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(i_clk));      // input clk_in1


    
//对被测试的设计进行例化
    
vlg_speed_generate        vlg_speed_generate_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_bps_start(tx_bps_start),
    .o_bps_clk_en(o_bps_clk_en) 
    );    
    
    
///

 vlg_my_uart_tx vlg_my_uart_tx_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_uart_tx_bps_en(o_bps_clk_en),
    .i_rx_data(w_data),
    .tx_en(w_tx_en),
    .tx_bps_start(tx_bps_start),
    .o_uart_tx(o_uart_tx),
    .o_uart_tx_done(o_uart_tx_done)
  
 );
 ///

 vlg_1ms vlg_1ms_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .o_data(w_data),
    .tx_en(w_tx_en)
 );
 endmodule

//my_uart_tx
//put o_rx_data to i_uart_tx
  
`timescale 1ns/1ps
module vlg_my_uart_tx(
    input i_clk,  //25Mhz
    input i_rest_n,
    input i_uart_tx_bps_en, 
    input [7:0]i_rx_data, 
    input tx_en,
    output reg tx_bps_start,
    output reg o_uart_tx,    
    output reg o_uart_tx_done
    );
    
reg [3:0]txdata_cnt;    
    

//检测tx_en 的高电平脉冲
//检测到高脉冲 tx_bps_start 置1
//当数据发送完成,即start,D0~D7,Stop .txdata_cnt = 10 时结束tx_bps_start 置0


reg [1:0]r_tx_en_plus;
wire w_tx_en_pos;


always @(posedge i_clk) begin
    if(!i_rest_n) r_tx_en_plus <= 2'b00;
    else r_tx_en_plus <= {r_tx_en_plus[0],tx_en};
end

assign w_tx_en_pos = r_tx_en_plus[0]& ~r_tx_en_plus[1];


//产生 tx_bps_start
always @(posedge i_clk) begin
if(!i_rest_n) tx_bps_start <= 0;
else if(txdata_cnt == 11) tx_bps_start <= 0;
else if(w_tx_en_pos) tx_bps_start <= 1;
end


//txdata_cnt 计数 0~10 
always @(posedge i_clk) begin
if(!i_rest_n) txdata_cnt <= 0;
else if(txdata_cnt == 11)  txdata_cnt <= 0;
else if(i_uart_tx_bps_en) txdata_cnt <= txdata_cnt + 1;
end

//发出o_uart_tx
wire [9:0]w_data_10 = {1'b1,i_rx_data,1'b0}; // 1,d7,d6,d5,d4,d3,d2,d1,d0,0
always @(posedge i_clk) begin
    if(!i_rest_n) o_uart_tx <= 1;
    else if(i_uart_tx_bps_en)  begin 
            case (txdata_cnt)
                0: o_uart_tx <= w_data_10[0];
                1: o_uart_tx <= w_data_10[1];
                2: o_uart_tx <= w_data_10[2];
                3: o_uart_tx <= w_data_10[3];
                4: o_uart_tx <= w_data_10[4];
                5: o_uart_tx <= w_data_10[5];
                6: o_uart_tx <= w_data_10[6];
                7: o_uart_tx <= w_data_10[7];
                8: o_uart_tx <= w_data_10[8];
                9: o_uart_tx <= w_data_10[9];
                10: o_uart_tx <= 1;
                default : o_uart_tx <= 1;
            endcase 
        end
end

//o_uart_tx_done
always @(posedge i_clk) begin
if(!i_rest_n) o_uart_tx_done <= 0;
else if(txdata_cnt == 11)  o_uart_tx_done <= 1;
else  o_uart_tx_done <= 0;
end



endmodule

 
`timescale 1ns/1ps
module vlg_speed_generate(
    input i_clk,  //input 25Mhz
    input i_rest_n,
    input i_bps_start,
    output reg o_bps_clk_en
    );

localparam bpsrate = 115200;
localparam BPS_COUNT_MAX = 1*25*1000_000/bpsrate - 1;
localparam BPS_COUNT_MAX_DIV_2 = 1*25*1000_000/bpsrate/2-1;


reg [11:0]bsp_cnt;
 
always @(posedge i_clk) begin
if(!i_rest_n) bsp_cnt <= 0;
else if(!i_bps_start) bsp_cnt <= 0;
else if(bsp_cnt < BPS_COUNT_MAX)  bsp_cnt <= bsp_cnt+1;
    else bsp_cnt <= 0;
end
 
 
always @(posedge i_clk) begin
    if(!i_rest_n) o_bps_clk_en <= 0;
    else if (bsp_cnt == BPS_COUNT_MAX_DIV_2)  o_bps_clk_en <= 1;
    else o_bps_clk_en <= 0;
end

endmodule

仿真波形

5,编写RX接收模块,并编写行为测试模块代码测试其功能

`timescale 1ns/1ps

module testbench_top();
    

//参数定义

`define CLK_PERIORD        20        //时钟周期设置为20ns(50MHz)    


//接口申明
reg [7:0]data;
reg i_clk;
reg i_rest_n;
reg i_rx;

 
wire o_uart_tx;
wire o_uart_tx_done;

wire i_uart_rx_bps_en;
wire [7:0]o_rx_data;
wire o_rx_start;
wire o_uart_rx_done;

wire locked;

  clk_wiz_0 instance_name
   (
    // Clock out ports
    .clk_out1(clk_25m),     // output clk_out1
    .clk_out2(clk_12r5m),     // output clk_out2
    .clk_out3(clk_100m),     // output clk_out3
    // Status and control signals
    .reset(!i_rest_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(i_clk));      // input clk_in1
    
 
 
vlg_my_uart_rx vlg_my_uart_rx_inst(

    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_rx(i_rx),
    .i_uart_rx_bps_en(i_uart_rx_bps_en),
    .o_rx_data(o_rx_data),
    .o_rx_start(o_rx_start),
    .o_uart_rx_done(o_uart_rx_done)

); 
 


 
 vlg_speed_generate        vlg_speed_generate_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_bps_start(o_rx_start),
    .o_bps_clk_en(i_uart_rx_bps_en) 
    );    
    
integer i;


 
initial begin
 
i_rx <= 1;
i_clk <= 0;
i_rest_n <= 0;
#20;
i_rest_n <= 1;
data <= 8'b0101_0101;  

end

always #(`CLK_PERIORD/2) i_clk = ~i_clk;


initial begin

 
@(posedge i_clk);
@(posedge i_rest_n);
#2000_000;
i_rx <= 0;
#10_4166;  //等待1个时钟 1_000_000_000 / 9600

for(i = 0;i<8;i=i+1) begin 
    i_rx <= data[i];
    #10_4166;
end
i_rx <= 1;
#10_4166;
#2000_000;


data <= 8'b0000_0101;  
i_rx <= 0;
#10_4166;  //等待1个时钟 1_000_000_000 / 9600

for(i = 0;i<8;i=i+1) begin 
    i_rx <= data[i];
    #10_4166;
end
i_rx <= 1;
#10_4166;
#2000_000;


data <= 8'b1111_0000;  
i_rx <= 0;
#10_4166;  //等待1个时钟 1_000_000_000 / 9600

for(i = 0;i<8;i=i+1) begin 
    i_rx <= data[i];
    #10_4166;
end
i_rx <= 1;
#10_4166;
#2000_000;


data <= 8'b0000_1111;  
i_rx <= 0;
#10_4166;  //等待1个时钟 1_000_000_000 / 9600

for(i = 0;i<8;i=i+1) begin 
    i_rx <= data[i];
    #10_4166;
end
i_rx <= 1;
#10_4166;
#2000_000;

$stop;

end


endmodule

//my_uart_tx
//put rx_data to i_uart_tx

`timescale 1ns/1ps
module vlg_design(
    input i_clk,  //25Mhz
    input i_rest_n,
    input i_rx,
    output o_tx
);

wire o_uart_tx;
wire o_uart_tx_done;
wire i_uart_rx_bps_en;
(*mark_debug = "true"*)wire [7:0]o_rx_data;
wire o_rx_start;
(*mark_debug = "true"*)wire o_uart_rx_done;
wire locked;


wire clk_25m;
wire clk_12r5m;
wire clk_100m;
wire locked;
 
wire tx_bps_start;
wire i_uart_tx_bps_en;


//灏唕x_data 杞垚 tx 
 ///

 vlg_my_uart_tx vlg_my_uart_tx_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_uart_tx_bps_en(i_uart_tx_bps_en),
    .i_rx_data(o_rx_data),
    .tx_en(o_uart_rx_done),
    .tx_bps_start(tx_bps_start),
    .o_uart_tx(o_tx),
    .o_uart_tx_done(o_uart_tx_done)
  
 );

    
vlg_speed_generate        vlg_speed_generate_tx_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_bps_start(tx_bps_start),
    .o_bps_clk_en(i_uart_tx_bps_en) 
    );    
    

//浜х敓rx 杞琩ata鏁版嵁
vlg_my_uart_rx vlg_my_uart_rx_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_rx(i_rx),
    .i_uart_rx_bps_en(i_uart_rx_bps_en),
    .o_rx_data(o_rx_data),
    .o_rx_start(o_rx_start),
    .o_uart_rx_done(o_uart_rx_done)
); 
  
 vlg_speed_generate        vlg_speed_generate_rx_inst(
    .i_clk(clk_25m),
    .i_rest_n(i_rest_n),
    .i_bps_start(o_rx_start),
    .o_bps_clk_en(i_uart_rx_bps_en) 
    );    


// 浜х敓鏃堕挓淇″彿
  clk_wiz_0 instance_name
   (
    // Clock out ports
    .clk_out1(clk_25m),     // output clk_out1
    .clk_out2(clk_12r5m),     // output clk_out2
    .clk_out3(clk_100m),     // output clk_out3
    // Status and control signals
    .reset(!i_rest_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(i_clk));      // input clk_in1


endmodule

//my_uart_tx
//put o_rx_data to i_uart_tx
  
`timescale 1ns/1ps
module vlg_my_uart_rx(
    input i_clk,  //25Mhz
    input i_rest_n,
    input(*mark_debug = "true"*) i_rx,  //杈撳叆 
    input i_uart_rx_bps_en, 
    
    output  [7:0]o_rx_data, 
    output reg o_rx_start,
    output reg o_uart_rx_done
    );
    
reg [3:0]rxdata_cnt;    
wire w_rx_pos;
reg  r_rx_start_pos;
reg [1:0]rx_plus;

always @(posedge i_clk) begin
    if(!i_rest_n)  rx_plus <= 2'b00;
    else rx_plus <= {rx_plus[0],i_rx};
end    

assign w_rx_pos = ~rx_plus[0] & rx_plus[1];// 鎵?鏈塺x 涓嬮檷娌?

///
//浜х敓 r_rx_start_pos 鑴夊啿锛屽綋rx 绗竴娆? = 0鏃讹紝杈撳嚭楂樼數骞筹紝鍏朵粬鏃跺埢鎷変綆
always @(posedge i_clk) begin
    if(!i_rest_n) r_rx_start_pos <= 0;
    else if(rxdata_cnt == 0) r_rx_start_pos <= w_rx_pos;
    else r_rx_start_pos <= 0;

end    



//妫?娴嬪埌 rx 1涓剦鍐茬殑涓嬮檷娌匡紝琛ㄧず淇″彿寮?濮嬶紝鎶?鏈粨鏉燂紝瀹屾垚
//o_rx_start
always @(posedge i_clk) begin
    if(!i_rest_n) o_rx_start <= 0;
    else if(rxdata_cnt == 11)o_rx_start <= 0;
    else if(r_rx_start_pos) o_rx_start <= 1;
    else  ;
end

//浜х敓rxdata_cnt璁℃暟
always @(posedge i_clk) begin
    if(!i_rest_n) rxdata_cnt <= 0;
    else if (rxdata_cnt == 11) rxdata_cnt <= 0;
    else if(i_uart_rx_bps_en)rxdata_cnt <= rxdata_cnt + 1;
    else ;
end

reg [7:0]r_rx_data;    
//鏀堕泦鏁版嵁
always @(posedge i_clk) begin
    if(!i_rest_n) r_rx_data <= 8'b0000_0000;  
   
     else if(i_uart_rx_bps_en)   
        case (rxdata_cnt)
            4'd0:    ;//start
            4'd1:    r_rx_data[0] <= i_rx;
            4'd2:    r_rx_data[1] <= i_rx;
            4'd3:    r_rx_data[2] <= i_rx;
            4'd4:    r_rx_data[3] <= i_rx;
            4'd5:    r_rx_data[4] <= i_rx;
            4'd6:    r_rx_data[5] <= i_rx;
            4'd7:    r_rx_data[6] <= i_rx;
            4'd8:    r_rx_data[7] <= i_rx;
            4'd9:;//stop
            4'd10:;//none
            default:;//none
        endcase 
 end    

assign o_rx_data= (rxdata_cnt == 11)?r_rx_data:o_rx_data;

//浜х敓o_uart_rx_done
always @(posedge i_clk) begin
    if(!i_rest_n)  o_uart_rx_done <= 0;
    else if(rxdata_cnt == 11) o_uart_rx_done <= 1;
    else o_uart_rx_done <= 0;  
end
endmodule

6,配置IO端口,生成bit文件烧录到板子测试

7,问题1:2024年1月10日

发送单个数据没有问题,但是连发出现异常数据。

对比代码发现例程代码的发送数据,结束位并没有保持1个bsp的完整高电平,而是只保持了一个脉冲的电平

修改代码后,测试ok

//my_uart_tx
//put o_rx_data to i_uart_tx
  
`timescale 1ns/1ps
module vlg_my_uart_tx(
    input i_clk,  //25Mhz
    input i_rest_n,
    input i_uart_tx_bps_en, 
    input [7:0]i_rx_data, 
    input tx_en,
    output reg tx_bps_start,
    output reg o_uart_tx,    
    output reg o_uart_tx_done
    );
    
reg [3:0]txdata_cnt;    
    

//检测tx_en 的高电平脉冲
//检测到高脉冲 tx_bps_start 置1
//当数据发送完成,即start,D0~D7,Stop .txdata_cnt = 10 时结束tx_bps_start 置0


reg [1:0]r_tx_en_plus;
wire w_tx_en_pos;


always @(posedge i_clk) begin
    if(!i_rest_n) r_tx_en_plus <= 2'b00;
    else r_tx_en_plus <= {r_tx_en_plus[0],tx_en};
end

assign w_tx_en_pos = r_tx_en_plus[0]& ~r_tx_en_plus[1];


//产生 tx_bps_start
always @(posedge i_clk) begin
if(!i_rest_n) tx_bps_start <= 0;
else if(txdata_cnt == 10) tx_bps_start <= 0;
else if(w_tx_en_pos) tx_bps_start <= 1;
end


//txdata_cnt 计数 0~10 
always @(posedge i_clk) begin
if(!i_rest_n) txdata_cnt <= 0;
else if(txdata_cnt == 10)  txdata_cnt <= 0;
else if(i_uart_tx_bps_en) txdata_cnt <= txdata_cnt + 1;
end

//发出o_uart_tx
wire [9:0]w_data_10 = {1'b1,i_rx_data,1'b0}; // 1,d7,d6,d5,d4,d3,d2,d1,d0,0
always @(posedge i_clk) begin
    if(!i_rest_n) o_uart_tx <= 1;
    else if(i_uart_tx_bps_en)  begin 
            case (txdata_cnt)
                0: o_uart_tx <= w_data_10[0];
                1: o_uart_tx <= w_data_10[1];
                2: o_uart_tx <= w_data_10[2];
                3: o_uart_tx <= w_data_10[3];
                4: o_uart_tx <= w_data_10[4];
                5: o_uart_tx <= w_data_10[5];
                6: o_uart_tx <= w_data_10[6];
                7: o_uart_tx <= w_data_10[7];
                8: o_uart_tx <= w_data_10[8];
                9: o_uart_tx <= w_data_10[9];
                default : o_uart_tx <= 1;
            endcase 
        end
end

//o_uart_tx_done
always @(posedge i_clk) begin
    if(!i_rest_n) o_uart_tx_done <= 0;
    else if(txdata_cnt == 10)  o_uart_tx_done <= 1;
    else  o_uart_tx_done <= 0;
end
endmodule

8,问题2:{} 数据合并要加上前缀

wire [9:0]w_data_10 = {1,i_rx_data,0}; //错误

wire [9:0]w_data_10 = {1'b1,i_rx_data,1'b0}; //正确

相关推荐
DS小龙哥4 小时前
基于Zynq FPGA的雷龙SD NAND存储芯片性能测试
fpga开发·sd nand·雷龙·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
上理考研周导师13 小时前
第二章 虚拟仪器及其构成原理
fpga开发
FPGA技术实战15 小时前
《探索Zynq MPSoC》学习笔记(二)
fpga开发·mpsoc
bigbig猩猩1 天前
FPGA(现场可编程门阵列)的时序分析
fpga开发
Terasic友晶科技1 天前
第2篇 使用Intel FPGA Monitor Program创建基于ARM处理器的汇编或C语言工程<二>
fpga开发·汇编语言和c语言
码农阿豪1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发·sd nand·spi nand·spi nand flash·工业级tf卡·嵌入式tf卡
江山如画,佳人北望1 天前
EDA技术简介
fpga开发
淘晶驰AK1 天前
电子设计竞赛准备经历分享
嵌入式硬件·fpga开发
最好有梦想~1 天前
FPGA时序分析和约束学习笔记(4、IO传输模型)
笔记·学习·fpga开发