🎉欢迎来到FPGA专栏~bin文件ram存取回环测试
🎉 目录-bin文件ram存取回环测试
一、效果演示
使用matlab
生成100
个1-100
的32
位随机整型数据,并将数据保存为bin
文件;通过带有bin
文件收发功能的串口调试助手发送给FPGA
,再接收FPGA
的回环数据。
🥝演示视频
演示视频-【FPGA】bin文件ram存取回环测试
🥝结果分析
matlab生成的随机数:
串口助手接收结果:
分析:
将串口助手接收框内的数据转换成10
进制,并与matlab
生成的数据进行对比,数值一致;串口助手接收框内的有效数据个数为100
个,与matlab
生成的数据个数一致;接收框内最后出现了FF
,这个数据并不是bin
文件中的数据,而是串口助手最后发送给FPGA
的发送指令,该指令也被存入到了ram
中(为了避免该现象的出现,可以在FPGA中对数据进行相关的判断处理,或者使用vio进行控制,即可解决)。
二、说明
需要该项目中的串口助手软件或者项目工程文件的小伙伴,直接加入QQ群,在群文件中搜索下载即可,QQ群:862135231
。
matlab版本:R2020b
vivado版本:2020.2
开发板型号:AXKU040
接口连接如下所示:
三、bin文件的生成
🔸bin文件介绍
bin文件是二进制文件的缩写,通常用于存储计算机程序或数据。它们可以包含各种类型的信息,例如操作系统、应用程序和媒体文件。bin文件是计算机系统中最基本的文件类型之一,通常需要特定的软件才能打开和读取。
不同类型的bin文件可能需要不同的软件来打开,例如,图像bin文件可能需要图像查看器来打开,而文档bin文件可能需要文本编辑器来打开。
重点在这:
bin文件可以储存大量数据的二进制形式!
🔸为什么使用bin文件作为数据载体发送给FPGA
ChatGPT的回答:
bin文件作为一种固定的二进制数据格式,具有以下优点,使其成为向FPGA发送数据的常用载体:
- 紧凑性:bin文件通常比其他格式更紧凑,减少了数据传输的带宽和存储空间需求。
- 高效性:FPGA可以直接读取和处理二进制数据,而不需要进行格式转换,提高了数据处理的效率。
- 准确性:bin文件中的数据以精确的二进制形式表示,确保了数据的准确性和完整性。
- 通用性:bin文件是一种广泛使用的格式,可以在不同的系统和硬件平台之间进行传输和使用。
🔸matlab生成bin文件
运行如下代码,会在matlab脚本的根目录下生成bin文件:
%% bin文件生成代码
%----------------------------------------------------
% 首先生成1-100的double类型随机整数;
% 将double类型随机整数转换为32位数据;
% 将转换后的数据保存为bin文件。
%----------------------------------------------------
clear;
clc;
close all;
% 生成1-100的double类型随机整数
random_numbers = randi([1 100], 1, 100);
% 将double类型数据转换为uint32类型
random_numbers_uint32 = uint32(random_numbers);
% 将数据保存为bin文件
fid = fopen('random_numbers.bin', 'wb');
fwrite(fid, random_numbers_uint32, 'uint32');
fclose(fid);
disp('随机数已生成并保存为 random_numbers.bin');
四、串口收发模块
关于串口收发模块的详细讲解,请点击如下两篇文章进行学习:
串口发送模块:【FPGA零基础学习之旅#13】串口发送模块设计与验证;
串口接收模块:【FPGA零基础学习之旅#15】串口接收模块设计与验证(工业环境)。
在此附上模块代码:
串口发送模块:
//
//模块名称:串口发送模块
//
module uart_byte_tx(
input Clk,
input Rst_n,
input [7:0] data_byte,
input send_en,
input [2:0] baud_set,
output reg uart_tx,
output reg Tx_Done,
output reg uart_state
);
reg bps_clk;//波特率时钟
reg [15:0]div_cnt;//分频计数器
reg [15:0]bps_DR;//分频计数最大值
reg [3:0]bps_cnt;//波特率计数时钟
//定义数据的起始位和停止位
localparam START_BIT = 1'b0;
localparam STOP_BIT = 1'b1;
reg [7:0]r_data_byte;//数据寄存器
//--------<uart状态模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(send_en)
uart_state <= 1'b1;
else if(bps_cnt == 4'd11)//bps_cnt计数达到11次,即发送结束
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
//--------<使能分频计数模块>-------
// assign en_cnt = uart_state;
//--------<寄存待发送的数据,使数据保持稳定>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
r_data_byte <= 8'd0;
else if(send_en)
r_data_byte <= data_byte;
else
r_data_byte <= r_data_byte;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd5207;
else begin
case(baud_set)
0:bps_DR <= 16'd5207;
1:bps_DR <= 16'd2603;
2:bps_DR <= 16'd1301;
3:bps_DR <= 16'd867;
4:bps_DR <= 16'd433;
default:bps_DR <= 16'd5207;
endcase
end
end
//--------<Div_Cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin // assign en_cnt = uart_state;
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_cnt计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 4'd0;
else if(bps_cnt == 4'd11)//clr信号
bps_cnt <= 4'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Tx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Tx_Done <= 1'b0;
else if(bps_cnt == 4'd11)
Tx_Done <= 1'b1;
else
Tx_Done <= 1'b0;
end
//--------<数据位输出模块-10选1多路器>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_tx <= 1'b1;
else begin
case(bps_cnt)
0:uart_tx <= 1'b1;
1:uart_tx <= START_BIT;
2:uart_tx <= r_data_byte[0];
3:uart_tx <= r_data_byte[1];
4:uart_tx <= r_data_byte[2];
5:uart_tx <= r_data_byte[3];
6:uart_tx <= r_data_byte[4];
7:uart_tx <= r_data_byte[5];
8:uart_tx <= r_data_byte[6];
9:uart_tx <= r_data_byte[7];
10:uart_tx <= STOP_BIT;
default:uart_tx <= 1'b1;
endcase
end
end
endmodule
串口接收模块:
//
//模块名称:串口接收模块(工业环境)
//
module uart_byte_rx(
input Clk,//50M
input Rst_n,
input [2:0] baud_set,
input data_rx,
output reg [7:0] data_byte,
output reg Rx_Done
);
reg s0_Rx,s1_Rx;//同步寄存器
reg tmp0_Rx,tmp1_Rx;//数据寄存器
reg [15:0]bps_DR;//分频计数器计数最大值
reg [15:0]div_cnt;//分频计数器
reg bps_clk;//波特率时钟
reg [7:0]bps_cnt;
reg uart_state;
reg [2:0] r_data_byte [7:0];
reg [2:0]START_BIT;
reg [2:0]STOP_BIT;
wire nedge;
//--------<同步寄存器处理>--------
//用于消除亚稳态
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
s0_Rx <= 1'b0;
s1_Rx <= 1'b0;
end
else begin
s0_Rx <= data_rx;
s1_Rx <= s0_Rx;
end
end
//--------<数据寄存器处理>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
tmp0_Rx <= 1'b0;
tmp1_Rx <= 1'b0;
end
else begin
tmp0_Rx <= s1_Rx;
tmp1_Rx <= tmp0_Rx;
end
end
//--------<下降沿检测>--------
assign nedge = !tmp0_Rx & tmp1_Rx;
//--------<div_cnt模块>--------
//得到不同计数周期的计数器
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
div_cnt <= 16'd0;
else if(uart_state)begin
if(div_cnt == bps_DR)
div_cnt <= 16'd0;
else
div_cnt <= div_cnt + 1'b1;
end
else
div_cnt <= 16'd0;
end
//--------<bps_clk信号的产生>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_clk <= 1'b0;
else if(div_cnt == 16'd1)
bps_clk <= 1'b1;
else
bps_clk <= 1'b0;
end
//--------<bps_clk计数模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_cnt <= 8'd0;
else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))
bps_cnt <= 8'd0;
else if(bps_clk)
bps_cnt <= bps_cnt + 1'b1;
else
bps_cnt <= bps_cnt;
end
//--------<Rx_Done模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Rx_Done <= 1'b0;
else if(bps_cnt == 8'd159)
Rx_Done <= 1'b1;
else
Rx_Done <= 1'b0;
end
//--------<波特率查找表>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
bps_DR <= 16'd324;
else begin
case(baud_set)
0:bps_DR <= 16'd324;
1:bps_DR <= 16'd162;
2:bps_DR <= 16'd80;
3:bps_DR <= 16'd53;
4:bps_DR <= 16'd26;
default:bps_DR <= 16'd324;
endcase
end
end
//--------<采样数据接收模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
else if(bps_clk)begin
case(bps_cnt)
0:begin
START_BIT <= 3'd0;
r_data_byte[0] <= 3'd0;
r_data_byte[1] <= 3'd0;
r_data_byte[2] <= 3'd0;
r_data_byte[3] <= 3'd0;
r_data_byte[4] <= 3'd0;
r_data_byte[5] <= 3'd0;
r_data_byte[6] <= 3'd0;
r_data_byte[7] <= 3'd0;
STOP_BIT <= 3'd0;
end
6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rx;
22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rx;
38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rx;
54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rx;
70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rx;
86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rx;
102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rx;
118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rx;
134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rx;
150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rx;
default:begin
START_BIT <= START_BIT;
r_data_byte[0] <= r_data_byte[0];
r_data_byte[1] <= r_data_byte[1];
r_data_byte[2] <= r_data_byte[2];
r_data_byte[3] <= r_data_byte[3];
r_data_byte[4] <= r_data_byte[4];
r_data_byte[5] <= r_data_byte[5];
r_data_byte[6] <= r_data_byte[6];
r_data_byte[7] <= r_data_byte[7];
STOP_BIT <= STOP_BIT;
end
endcase
end
end
//--------<数据状态判定模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
data_byte <= 8'd0;
else if(bps_cnt == 8'd159)begin
data_byte[0] <= r_data_byte[0][2];
data_byte[1] <= r_data_byte[1][2];
data_byte[2] <= r_data_byte[2][2];
data_byte[3] <= r_data_byte[3][2];
data_byte[4] <= r_data_byte[4][2];
data_byte[5] <= r_data_byte[5][2];
data_byte[6] <= r_data_byte[6][2];
data_byte[7] <= r_data_byte[7][2];
end
else
;
end
//--------<uart_state模块>--------
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
uart_state <= 1'b0;
else if(nedge)
uart_state <= 1'b1;
else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
uart_state <= 1'b0;
else
uart_state <= uart_state;
end
endmodule
五、回环系统搭建
🍍系统功能描述
系统串口波特率设置为:115200
;
功能描述: 为了避免使用按键等作为触发或者控制信号,本项目使用串口指令控制整个系统的运行,因此设置了十六进制的fe
和ff
指令。通过串口助手发送十六进制的fe
指令使能ram
的储存,再通过发送十六进制的ff
指令让FPGA将ram
中的数据通过串口发送出来。
🍍ram ip核设置
选择普通双端口类型:
由于串口接收到的数据位宽都是8
位,将ram
的输入端口设置为8
位便于存储和控制,深度选择为2048
,一直使能:
ram
中的数据也是直接通过串口输出,因此输出端口的数据位宽也设置为8
位便于控制和读取,选择使用使能引脚控制ram
读取数据:
在Summary
中可以看到每个端口访问数据的地址位宽:
🍍RTL视图
🍍系统完整代码
uart_top.v:
`timescale 1ns / 1ps
module uart_top(
input sys_clk_p,
input sys_clk_n,
input rst_n,
input uart_rx,
output uart_tx
);
localparam baud_set = 3'b100;
wire locked;
wire clk_r;
wire clk;
wire [7:0] data_byte;
reg En;
reg send_en;
reg [11-1:0] data_in_cnt;
wire Rx_Done;
wire write_en;
reg [10:0] addrb;
wire read_en;
wire [7:0] data_byte_tx;
clk50MHz instance_name
(
// Clock out ports
.clk_out1(clk_r), // output clk_out1
// Status and control signals
.reset(!rst_n), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_in1_p(sys_clk_p), // input clk_in1_p
.clk_in1_n(sys_clk_n)
); // input clk_in1_n
assign clk = (locked)?clk_r:1'b0;
uart_byte_rx inst_uart_byte_rx
(
.Clk (clk),
.Rst_n (rst_n),
.baud_set (baud_set),
.data_rx (uart_rx),
.data_byte (data_byte),
.Rx_Done (Rx_Done)
);
//--------------------<system_ctrl>--------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
En <= 1'b0;
else if(data_byte == 8'hfe)
En <= 1'b1;
else
En <= En;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
send_en <= 1'b0;
else if(data_byte == 8'hff && addrb < 11'd2000)
send_en <= 1'b1;
else if(addrb == 11'd2000)
send_en <= 1'b0;
else
send_en <= send_en;
end
//-----------------------------------------------------------------
//--------------------<data_in_cnt>--------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_in_cnt <= 11'd0;
else if((En) && (data_in_cnt < 11'd2047) && Rx_Done)
data_in_cnt <= data_in_cnt + 1'b1;
else if(data_in_cnt == 11'd2047)
data_in_cnt <= 11'd0;
else
data_in_cnt <= data_in_cnt;
end
//-----------------------------------------------------------------
assign write_en = (En && !(send_en))?1'b1:1'b0;
ram_ip ram_8_2048_8
(
.clka (clk), // input wire clka
.wea (write_en), // input wire [0 : 0] wea
.addra (data_in_cnt), // input wire [10 : 0] addra
.dina (data_byte), // input wire [7 : 0] dina
.clkb (clk), // input wire clkb
.enb (read_en), // input wire enb
.addrb (addrb), // input wire [10 : 0] addrb
.doutb (data_byte_tx) // output wire [7 : 0] doutb
);
assign read_en = (send_en && Tx_Done)?1'b1:1'b0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
addrb <= 11'd0;
else if(Tx_Done)
addrb <= addrb + 1'b1;
else
addrb <= addrb;
end
uart_byte_tx inst_uart_byte_tx
(
.Clk (clk),
.Rst_n (rst_n),
.data_byte (data_byte_tx),
.send_en (send_en),
.baud_set (baud_set),
.uart_tx (uart_tx),
.Tx_Done (Tx_Done),
.uart_state ()
);
endmodule
🧸结尾