Vivado ROM IP的生成和调用
1 需求
1、如何将MATLAB生成的数据导入到FPGA中;
2、vivado如何对数据进行分析;
3、如何验证vivado数据的可靠性;
4、如何将vivado的数据导出到MATLAB进行分析
2 具体实现
2.1 MATLAB生成的数据导入到FPGA
2.1.1 生成coe文件
.coe 是 FPGA 设计中常用的存储文件,用于 ROM、RAM 等存储器数据的加,所以我们将MATLAB的数据导出为.coe。如何导出呢?MATLAB程序如下:
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fc=3e6; % 信号频率
fs=65e6; % 采样频率
L=1024; % 采样点数
ADC_bit=16; % 采样位数
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 产生信号
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
t=0:1/fs:(L-1)/fs;
noise=randn(1,length(t));
noise=0;
st=sin(2*pi*fc*t)+noise;
y=st/max(abs(st)); % 归一化
yt=round(y*(2^(ADC_bit-1)-1)); % 12bit量化
figure(1);
plot(st);hold on;
figure(2);
plot(y);hold on;
figure(3);
plot(yt);hold on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MATLAB生成coe文件
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 在.coe文件中
% 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
% 从第 3 行开始到第最后一行,是这个 L(数据长度为1024)* ADC_bit(16bit) 大小 ROM 的初始化数据。
% 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
fid=fopen('data0.coe','w');
fprintf(fid,'Memory_Initialization_Radix = 2;\r\n'); % 二进制
fprintf(fid,'Memory_Initialization_Vector = \r\n');
for p=1:L
B_s=dec2bin(yt(p)+(yt(p)<0)*2^ADC_bit,ADC_bit);
for q=1:ADC_bit % 16位,依次判断这16位的数值
if B_s(q)=='1'
data=1;
else
data=0;
end
fprintf(fid,'%d',data);
end
% 下面if语句的目的
% 每行数字后面用逗号(,),最后一行数字结束用分号(;)
if (p<L)
fprintf(fid,',\r\n');
else
fprintf(fid,';\r\n'); % 分号(;) 结束标志位
end
end
fclose(fid);
导出的coe文件为
在.coe文件中, 第一行为定义数据格式, 2代表 ROM 的数据格式为二进制。
从第 3 行开始到第最后一行,是这个L(数据长度为1024)* ADC_bit(16bit)大小 ROM 的初始化数据。 第一行到倒数第二行的数字后面用逗号,最后一行数字结束用分号。
2.1.2 将coe文件导入到FPGA,并分析
1、打开vivado软件,新建一个工程;
2、添加 ROM IP核
(1)打开IP catalog,搜索ROM。找到 Block Memory Generator,双击打开。
(2)ROM IP核配置
a、 将 Component Name 改为 rom_ip,在 Basic 栏目下,将 Memory Type 改为 Single Prot ROM。
b、切换到 Port A Options 栏目下。
将 ROM 位宽 Port A Width 改为 16;
将 ROM 深度 Port A Depth 改为1024;
使能管脚 Enable Port Type 改为 Always Enabled;
取消 勾选Primitives Output Register。
depth :表示有多少个数据。
width :表示一个数据有多少位。对于AD9238而言,ADC数据有12位
c、切换到 Other Options 栏目下,勾选 Load Init File,点击 Browse,选中之前制作好的.coe 文件。
d、点击 ok,点击 Generate 生成 ip 核。
3、添加 ILA IP核
目的在于对coe数据进行观察
4、添加源文件、编写源文件
`timescale 1ns / 1ps
module v_rom(
input sys_clk, //50MHz时钟
input rst_n //复位,低电平有效
);
wire [15:0] rom_data; //ROM读出数据 每个数据有16bit
reg [9:0] rom_addr; //ROM输入地址 1024个数据,需要2^10个地址
//产生ROM地址读取数据
always @ (posedge sys_clk or negedge rst_n)
begin
if(!rst_n)
rom_addr <= 10'd0;
else
rom_addr <= rom_addr+1'b1;
end
//实例化ROM
rom_ip rom_ip_inst
(
.clka (sys_clk ), //inoput clka
.addra (rom_addr ), //input [9:0] addra 1024个数据,需要2^10个地址
.douta (rom_data ) //output [15:0] douta
);
//实例化逻辑分析仪
ila_0 ila_m0
(
.clk (sys_clk),
.probe0 (rom_addr), // input wire [9:0] probe0
.probe1 (rom_data) // input wire [15:0] probe1
);
endmodule
5、添加约束文件
############## clock and reset define##################
create_clock -period 20 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports {sys_clk}]
set_property PACKAGE_PIN U18 [get_ports {sys_clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {rst_n}]
set_property PACKAGE_PIN N15 [get_ports {rst_n}]
6、保存并生成比特流文件
7、ila波形分析
a、添加触发信号
b、点击第二个三角形进行触发,设置waveform style为analog;设置radix为signed Decimal。其结果如下
从上图可以看出,rom_data的数据为一个正弦波,和MATLAB产生的信号是一样的。rom_addr从0增加到511,再从-512变到-1。为什么不是0~2^10-1呢?因为我们radix设置的是有符号十进制,而不是无符号十进制。
2.2 将ila的数据导入到MATLAB分析
1、设置radix为二进制。
2、导出ILa数据
3、用excel打开iladata.csv
只保留数据栏,第一行删除----------全选中数据,右键点击设置单元格格式-----自定义-----选中0----点击确定----保存退出。
4、MATLAB分析数据
主函数:
close all;
clear all;
clc;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% bin2dec函数:将二进制转换为十进制。
% uint16(A) 把A转换为16位非负整型
% Y = typecast(X, DATATYPE) 函数的作用是在不更改基础数据的情况下转换数据类型。
% 大矩阵计算,如果用single类型的数据能节省一半的内存空间
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
filenameCSV = 'iladata.csv';
data = importfile(filenameCSV);
SIZEdata = size( data );
L = SIZEdata(1);
lie = SIZEdata(2);
y = zeros( L,lie );
for i = 1 : L
for j = 1 : lie
x1 = data( i, j );
x2 = typecast( uint16(bin2dec( x1 )),'int16');
x3 = single( x2 );
y( i,j ) = x3;
end
end
Y=fftshift(fft(y'));
P=abs(Y)/L;
fs = 65e6; % 采样频率 AD9238的采样频率为65M
f = (0:L-1)*fs/L-fs/2;
t=0:1/fs:(L-1)/fs ;
figure(1)
plot(t,real(y));grid on;
xlabel('时间(s)');ylabel('幅度');
figure(2)
plot(f/1000,P);grid on;
xlabel('频率/KHz');ylabel('幅度');
子函数:
function data = importfile(filename, startRow, endRow)
% IMPORTFILE1 将文本文件中的数值数据作为列矢量导入。
% 初始化变量。
delimiter = ',';
if nargin<=2
startRow = 1;
endRow = inf;
end
% 每个文本行的格式字符串:
% 列1: 文本 (%q)
% 有关详细信息,请参阅 TEXTSCAN 文档。
formatSpec = '%q%[^\n\r]';
% 打开文本文件。
fileID = fopen(filename,'r');
% 根据格式字符串读取数据列。
% 该调用基于生成此代码所用的文件的结构。如果其他文件出现错误,请尝试通过导入工具重新生成代码。
dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, 'Delimiter', delimiter, ...
'EmptyValue' ,NaN,'HeaderLines', startRow(1)-1, 'ReturnOnError', false);
for block=2:length(startRow)
frewind(fileID);
dataArrayBlock = textscan(fileID, formatSpec, endRow(block)-startRow(block)+1, 'Delimiter', delimiter, ...
'EmptyValue' ,NaN,'HeaderLines', startRow(block)-1, 'ReturnOnError', false);
dataArray{1} = [dataArray{1};dataArrayBlock{1}];
end
% 关闭文本文件。
fclose(fileID);
% 将导入的数组分配给列变量名称
data = dataArray{:, 1};
仿真结果:
时域波形为正弦波,信号频率为2983KHz,和最初所设的3MHz接近。说明
MATLAB和FPGA联合仿真成功。
3 注意事项
当我们rom的数据发生变化时,重新在ROM导入数据后,如下图所示
可能会出现如下问题
解决方法:
右键?红色文件------点击replace file----找到对应文件即可
效果图: