ROM
ROM是只读存储器(only read memory)的简称,是一种只能读出事先所存储数据的固态半导体存储器。FPGA中是没有非易性存储器的,ROM ip核其实是使用到FPGA中的RAM资源。在 FPGA 运行时通过数据文件给 ROM 模块初始化,模拟成非易失存储器。
Altera 推出的 ROM IP 核分为两种类型:单端口 ROM 和双端口 ROM。对于单端口ROM 提供一个读地址端口和一个读数据端口,只能进行读操作;双端口 ROM 与单端口ROM 类似,区别是其提供两个读地址端口和两个读数据端口。
一、单端口ROM
1.
本次采用matlab生成一个FPGA所需要的正弦波MIF文件,sin_wave_8x256.mif会生成在你的资源管理器中,把他添加到你的FPGA工程文件下
clc; %清除命令行命令
clear all; %清除工作区变量,释放内存空间
F1=1; %信号频率
Fs=2^8; %采样频率
P1=0; %信号初始相位
N=2^8; %采样点数
t=[0:1/Fs:(N-1)/Fs]; %采样时刻
ADC=2^7-1; %直流分量
A=2^7; %信号幅度
%生成正弦信号
s=A*sin(2*pi*F1*t + pi*P1/180) + ADC;
plot(s); %绘制图形
%创建mif文件
fild = fopen('sin_wave_8x256.mif','wt');
%写入mif文件头
fprintf(fild, '%s\n','WIDTH=8;'); %位宽
fprintf(fild, '%s\n\n','DEPTH=256;'); %深度
fprintf(fild, '%s\n','ADDRESS_RADIX=UNS;'); %地址格式
fprintf(fild, '%s\n\n','DATA_RADIX=UNS;'); %数据格式
fprintf(fild, '%s\t','CONTENT'); %地址
fprintf(fild, '%s\n','BEGIN'); %开始
for i = 1:N
s0(i) = round(s(i)); %对小数四舍五入以取整
if s0(i) <0 %负1强制置零
s0(i) = 0
end
fprintf(fild, '\t%g\t',i-1); %地址编码
fprintf(fild, '%s\t',':'); %冒号
fprintf(fild, '%d',s0(i)); %数据写入
fprintf(fild, '%s\n',';'); %分号,换行
end
fprintf(fild, '%s\n','END;'); %结束
fclose(fild);
2.
创建ROM IP,数据位宽为8,ROM深度为256
3.
添加初始化MIF文件
4.
生成例化文档
RTL代码
顶层模块rom
module rom
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire [7:0] rom_data
);
wire [7:0] rom_addr;
rom_ctrl rom_ctrl_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rom_addr (rom_addr)
);
rom_sin rom_sin_inst (
.address ( rom_addr ),
.clock ( sys_clk ),
.q ( rom_data )
);
endmodule
rom_ctrl模块
module rom_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [7:0] rom_addr
);
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_addr <= 8'd0;
else
rom_addr <= rom_addr + 1'b1;
endmodule
testbench
`timescale 1ns/1ns
module tb_rom();
reg sys_clk;
reg sys_rst_n;
wire rom_data;
initial
begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
rom rom_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rom_data (rom_data)
);
endmodule
modelsim仿真结果
由仿真波形数据得正弦波的周期是5120ns,而ROM存储的一个完整正弦波周期也是256*20=5120ns。仿真结果正确
二、双端口ROM
在原来的基础,采用双端口ROM输出两路信号,一路正弦波一路方波。步骤与上诉类似
1.matlab产生MIF文件
clc; %清除命令行命令
clear all; %清除工作区变量,释放内存空间
F1=1; %信号频率
Fs=2^8; %采样频率
P1=0; %信号初始相位
N=2^8; %采样点数
t=[0:1/Fs:(N-1)/Fs]; %采样时刻
ADC=2^7 - 1; %直流分量
A=2^7; %信号幅度
s1=A*sin(2*pi*F1*t + pi*P1/180) + ADC; %正弦波信号
s2=A*square(2*pi*F1*t + pi*P1/180) + ADC; %方波信号
%创建mif文件
fild = fopen('wave_512x8.mif','wt');
%写入mif文件头
fprintf(fild, '%s\n','WIDTH=8;'); %位宽
fprintf(fild, '%s\n\n','DEPTH=512;'); %深度
fprintf(fild, '%s\n','ADDRESS_RADIX=UNS;'); %地址格式
fprintf(fild, '%s\n\n','DATA_RADIX=UNS;'); %数据格式
fprintf(fild, '%s\t','CONTENT'); %地址
fprintf(fild, '%s\n','BEGIN'); %开始
for j = 1:2
for i = 1:N
if j == 1 %打印正弦信号数据
s0(i) = round(s1(i)); %对小数四舍五入以取整
fprintf(fild, '\t%g\t',i-1); %地址编码
end
if j == 2 %打印方波信号数据
s0(i) = round(s2(i)); %对小数四舍五入以取整
fprintf(fild, '\t%g\t',i-1+N); %地址编码
end
if s0(i) <0 %负1强制置零
s0(i) = 0
end
fprintf(fild, '%s\t',':'); %冒号
fprintf(fild, '%d',s0(i)); %数据写入
fprintf(fild, '%s\n',';'); %分号,换行
end
end
fprintf(fild, '%s\n','END;'); %结束
fclose(fild);
RTL代码
顶层模块rom
module rom
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire [7:0] rom_data ,
output wire [7:0] sin_d ,
output wire [7:0] squ_d
);
wire [7:0] rom_addr;
wire [8:0] rom_sin_d;
wire [8:0] rom_squ_d;
rom_ctrl rom_ctrl_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rom_addr (rom_addr),
.rom_sin_d (rom_sin_d),
.rom_squ_d (rom_squ_d)
);
rom_double rom_double_inst ( //双端口ROM
.address_a ( rom_sin_d ),
.address_b ( rom_squ_d ),
.clock ( sys_clk ),
.q_a ( sin_d ),
.q_b ( squ_d )
);
rom_sin rom_sin_inst (
.address ( rom_addr ),
.clock ( sys_clk ),
.q ( rom_data )
);
endmodule
rom_ctrl模块
module rom_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [7:0] rom_addr ,
output reg [8:0] rom_sin_d ,
output reg [8:0] rom_squ_d
);
localparam SQU_Z = 9'd256;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_addr <= 8'd0;
else
rom_addr <= rom_addr + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_sin_d <= 9'd0;
else if(rom_sin_d == 9'd255)
rom_sin_d <= 9'd0;
else
rom_sin_d <= rom_addr + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rom_squ_d <= SQU_Z;
else
rom_squ_d <= rom_addr + SQU_Z;
endmodule
testbench
`timescale 1ns/1ns
module tb_rom();
reg sys_clk;
reg sys_rst_n;
wire rom_data;
wire rom_sin_d;
wire rom_squ_d;
initial
begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
rom rom_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.rom_data (rom_data),
.sin_d (rom_sin_d),
.squ_d (rom_squ_d)
);
endmodule
modelsim仿真结果