PC使用串口助手给FPGA板发送9600 波特率的数据,FPGA板接收到数据后,回复同样的数据给PC
按模块可以划分为:
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:发送波特率时钟产生模块,功能同接收模块
`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
仿真波形
`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
发送单个数据没有问题,但是连发出现异常数据。
对比代码发现例程代码的发送数据,结束位并没有保持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
wire [9:0]w_data_10 = {1,i_rx_data,0}; //错误
wire [9:0]w_data_10 = {1'b1,i_rx_data,1'b0}; //正确