参考UG573和UG974。
一、xpm_memory_tdpram原语介绍
xpm_memory_tdpram #(
.ADDR_WIDTH_A(16), // DECIMAL,A口地址[15:0]
.ADDR_WIDTH_B(16), // DECIMAL,B口地址[15:0]
.AUTO_SLEEP_TIME(0), // DECIMAL
.BYTE_WRITE_WIDTH_A(72), // DECIMAL A口写宽度为72,4个像素点
.BYTE_WRITE_WIDTH_B(72), // DECIMAL B口写宽度为72,4个像素点
.CASCADE_HEIGHT(0), // DECIMAL
.CLOCKING_MODE("common_clock"), // String
.ECC_MODE("no_ecc"), // String
// .MEMORY_INIT_FILE("none"), // String
.MEMORY_INIT_FILE("triangle_192_256.mem"), // String
.MEMORY_INIT_PARAM("0"), // String
.MEMORY_OPTIMIZATION("true"), // String
.MEMORY_PRIMITIVE("ultra"), // String
.MEMORY_SIZE(3538944), // DECIMAL 内存bits数,192*1024*18=192*256*72=3538944,生成192行1024列的图像,18为图像颜色的深度
.MESSAGE_CONTROL(0), // DECIMAL 禁能消息报告
.READ_DATA_WIDTH_A(72), // DECIMAL A口读数据位宽[71:0],4个像素点
.READ_DATA_WIDTH_B(72), // DECIMAL B口读数据位宽[71:0],4个像素点
.READ_LATENCY_A(1), // DECIMAL 读延迟1个时钟 内存大于2MB时,延迟必须大于8
.READ_LATENCY_B(1), // DECIMAL 读延迟1个时钟 内存大于2MB时,延迟必须大于8
.READ_RESET_VALUE_A("0"), // String
.READ_RESET_VALUE_B("0"), // String
.RST_MODE_A("SYNC"), // String
.RST_MODE_B("SYNC"), // String
.SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
.USE_EMBEDDED_CONSTRAINT(0), // DECIMAL
.USE_MEM_INIT(0), // DECIMAL
.USE_MEM_INIT_MMI(0), // DECIMAL
.WAKEUP_TIME("disable_sleep"), // String
.WRITE_DATA_WIDTH_A(72), // DECIMAL A口读数据位宽[71:0],4个像素点
.WRITE_DATA_WIDTH_B(72), // DECIMAL B口读数据位宽[71:0],4个像素点
.WRITE_MODE_A("no_change"), // String
.WRITE_MODE_B("no_change"), // String
.WRITE_PROTECT(1) // DECIMAL,使用写使能信号
)
ram_inst (
.dbiterra(),// 1-bit output: 指示A口数据输出有双bits错误发生
.dbiterrb(),// 1-bit output: 指示B口数据输出有双bits错误发生
.sbiterra(),// 1-bit output: 指示A口数据输出有单bits错误发生
.sbiterrb(), // 1-bit output: 指示B口数据输出有单bits错误发生
.ena(1'b1), //RAM A口使能信号,高电平有效
.enb(1'b1), //RAM B口使能信号,高电平有效
.injectdbiterra(1'b0),//控制双bits错误注入
.injectdbiterrb(1'b0),//控制双bits错误注入
.injectsbiterra(1'b0),//控制单bits错误注入
.injectsbiterrb(1'b0),//控制单bits错误注入
.regcea(1'b1), //输入端口,输出数据路径的上一个寄存器阶段,时钟使能
.regceb(1'b1), //输入端口,输出数据路径的上一个寄存器阶段,时钟使能
.rsta(1'b0), //复位信号,高电平有效
.rstb(1'b0),//复位信号,高电平有效
.sleep(1'b0), //高电平使能动态降低功耗功能,此处禁能
.douta(douta), //READ_DATA_WIDTH_A bits 输出,[71:0],B口读数据
.doutb(doutb), //READ_DATA_WIDTH_B bits 输出,[71:0],B口读数据
.addra(addra),//ADDR_WIDTH_A bits输入 [15:0]A口写地址
.addrb(addrb),//ADDR_WIDTH_B bits输入 [15:0]B口写地址
.clka(clk_300M), //A口输入时钟
.clkb(clk_300M), //B口输入时钟
.dina(dina), // WRITE_DATA_WIDTH_A bits A口数据输入[71:0]
.dinb(dinb), // WRITE_DATA_WIDTH_B bits B口数据输入[71:0]
.wea(wea), //A口写使能信号,宽度为 WRITE_DATA_WIDTH_A / BYTE_WRITE_WIDTH_A =72/72=1
.web(web) //B口写使能信号,宽度为 WRITE_DATA_WIDTH_B / BYTE_WRITE_WIDTH_B =72/72=1
);
当需要指定RAM内初始化数值时,可以使用原语中的参数MEMORY_INIT_FILE,指向一个尾缀为.mem的文件。该文件内的内容要求必须时十六进制的,与$readmemh函数读取的文件内容格式一致,用空格或者回车将数据隔开即可。
另外需要注意的是,本着节约使用ultra RAM的想法,一个ultra RAM的数据位宽为72bits,因此72bits拆分为18bits,即ultra RAM的一个地址上,保存着4个18bits的像素数据。
二、如何生成.mem文件
借助于matlab,与生成.coe文件类似,matlabl示例程序如下:
M = 192;
N = 1024 ;
y = zeros(M , N) ;%生成192行1024列的矩阵
for i = 1:1:M
for j = 1:1:N
if(j <= N/2) %锯齿波,从0到512时,下降斜率直线
if( i == round(191/512*j))
y(i,j) = 255 ;%出现在斜率线附近的点赋值255
else
y(i,j) = 0 ;%不在斜率线附近的点赋值0
end
end
if(j > N/2)%%锯齿波,从512到1024时,上升斜率直线
if( i == round(382-191/511*j) )
y(i,j) = 255 ;%出现在斜率线附近的点赋值255
else
y(i,j) = 0 ;%不在斜率线附近的点赋值0
end
end
end
end
imshow(y)
% plot(y(i,j));%绘图预览
%y矩阵4个点拼成1个点
fpga_y_dec = zeros(M , N/4) ;
fpga_y_dec_49152=zeros(49152,1);%将矩阵变为1列
fpga_y_hex = zeros(M*N/4,8) ;
for i = 1:1:M
for j = 1:1:N/4
fpga_y_dec(i,j)=y(i,4*j-3)*256*256*256+y(i,4*j-2)*256*256+y(i,4*j-1)*256+y(i,4*j);
fpga_y_dec_49152(i*256-256+j,1)=fpga_y_dec(i,j);
end
end
fpga_y_hex= dec2hex(fpga_y_dec_49152);
fid1 = fopen('triangle_192_256.mem','wt');
%COE文件格式
% fprintf( fid1, 'MEMORY_INITIALIZATION_RADIX = 16;\n');
% fprintf( fid1, 'MEMORY_INITIALIZATION_VECTOR =\n');
%输出十进制数据,保存至文件
for i = 1:1:49152
if(i == 49152)
fprintf(fid1,'%s%c%c%c%c%c%c%c%c',"0000000000",fpga_y_hex(i,1),fpga_y_hex(i,2),fpga_y_hex(i,3),fpga_y_hex(i,4),fpga_y_hex(i,5),fpga_y_hex(i,6),fpga_y_hex(i,7),fpga_y_hex(i,8)); %最后一个点时,标点为分号,其余时候为逗号
else
fprintf(fid1,'%s%c%c%c%c%c%c%c%c\n',"0000000000",fpga_y_hex(i,1),fpga_y_hex(i,2),fpga_y_hex(i,3),fpga_y_hex(i,4),fpga_y_hex(i,5),fpga_y_hex(i,6),fpga_y_hex(i,7),fpga_y_hex(i,8));
end
end
fclose(fid1);%关闭文件
生成 如下图所示的三角波图像:
生成的文件的部分内容如下:
三、仿真xpm_memory_tdpram原语
仿真xpm_memory_tdpram原语原语,将初始化文件内容读出,观察是否与文件内容一致。
`timescale 1ns/1ps
module top();
reg clk_100M = 1'b0;
reg rst = 1'b1;
reg rd_enable =1'b0;
//生成100M时钟
always
begin
#5
clk_100M <= ~clk_100M;
end
//延迟100ns,复位拉高
initial
begin
#100 rst <= 1'b0;
#200 rd_enable <= 1'b1;
end
wire [71:0] douta;
wire [71:0] doutb;
reg [15:0] addra =0;
reg [15:0] addrb =0;
wire [71:0] dina;
wire [71:0] dinb;
wire wea;
wire web;
//不写入新的数据,仅读
assign dina = 72'b0;
assign dinb = 72'b0;
assign wea = 1'b0;
assign web = 1'b0;
always@(posedge clk_100M)
begin
if(rd_enable) begin
addra <= addra + 1'b1;
addrb <= addrb + 1'b1;
end
end
xpm_memory_tdpram #(
.ADDR_WIDTH_A(16), // DECIMAL,A口地址[15:0]
.ADDR_WIDTH_B(16), // DECIMAL,B口地址[15:0]
.AUTO_SLEEP_TIME(0), // DECIMAL
.BYTE_WRITE_WIDTH_A(72), // DECIMAL A口写宽度为72,4个像素点
.BYTE_WRITE_WIDTH_B(72), // DECIMAL B口写宽度为72,4个像素点
.CASCADE_HEIGHT(0), // DECIMAL
.CLOCKING_MODE("common_clock"), // String
.ECC_MODE("no_ecc"), // String
// .MEMORY_INIT_FILE("none"), // String
.MEMORY_INIT_FILE("triangle_192_256.mem"), // String
.MEMORY_INIT_PARAM("0"), // String
.MEMORY_OPTIMIZATION("true"), // String
.MEMORY_PRIMITIVE("ultra"), // String
.MEMORY_SIZE(3538944), // DECIMAL 内存bits数,192*1024*18=192*256*72=3538944,生成192行1024列的图像,18为图像颜色的深度
.MESSAGE_CONTROL(0), // DECIMAL 禁能消息报告
.READ_DATA_WIDTH_A(72), // DECIMAL A口读数据位宽[71:0],4个像素点
.READ_DATA_WIDTH_B(72), // DECIMAL B口读数据位宽[71:0],4个像素点
.READ_LATENCY_A(0), // DECIMAL
.READ_LATENCY_B(0), // DECIMAL
.READ_RESET_VALUE_A("0"), // String
.READ_RESET_VALUE_B("0"), // String
.RST_MODE_A("SYNC"), // String
.RST_MODE_B("SYNC"), // String
.SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
.USE_EMBEDDED_CONSTRAINT(0), // DECIMAL
.USE_MEM_INIT(0), // DECIMAL
.USE_MEM_INIT_MMI(0), // DECIMAL
.WAKEUP_TIME("disable_sleep"), // String
.WRITE_DATA_WIDTH_A(72), // DECIMAL A口读数据位宽[71:0],4个像素点
.WRITE_DATA_WIDTH_B(72), // DECIMAL B口读数据位宽[71:0],4个像素点
.WRITE_MODE_A("no_change"), // String
.WRITE_MODE_B("no_change"), // String
.WRITE_PROTECT(1) // DECIMAL,使用写使能信号
)
ram_inst (
.dbiterra(),// 1-bit output: 指示A口数据输出有双bits错误发生
.dbiterrb(),// 1-bit output: 指示B口数据输出有双bits错误发生
.sbiterra(),// 1-bit output: 指示A口数据输出有单bits错误发生
.sbiterrb(), // 1-bit output: 指示B口数据输出有单bits错误发生
.ena(1'b1), //RAM A口使能信号,高电平有效
.enb(1'b1), //RAM B口使能信号,高电平有效
.injectdbiterra(1'b0),//控制双bits错误注入
.injectdbiterrb(1'b0),//控制双bits错误注入
.injectsbiterra(1'b0),//控制单bits错误注入
.injectsbiterrb(1'b0),//控制单bits错误注入
.regcea(1'b1), //输入端口,输出数据路径的上一个寄存器阶段,时钟使能
.regceb(1'b1), //输入端口,输出数据路径的上一个寄存器阶段,时钟使能
.rsta(rst), //复位信号,高电平有效
.rstb(rst),//复位信号,高电平有效
.sleep(1'b0), //高电平使能动态降低功耗功能,此处禁能
.douta(douta), //READ_DATA_WIDTH_A bits 输出,[71:0],B口读数据
.doutb(doutb), //READ_DATA_WIDTH_B bits 输出,[71:0],B口读数据
.addra(addra),//ADDR_WIDTH_A bits输入 [15:0]A口写地址
.addrb(addrb),//ADDR_WIDTH_B bits输入 [15:0]B口写地址
.clka(clk_100M), //A口输入时钟
.clkb(clk_100M), //B口输入时钟
.dina(dina), // WRITE_DATA_WIDTH_A bits A口数据输入[71:0]
.dinb(dinb), // WRITE_DATA_WIDTH_B bits B口数据输入[71:0]
.wea(wea), //A口写使能信号,宽度为 WRITE_DATA_WIDTH_A / BYTE_WRITE_WIDTH_A =72/72=1
.web(web) //B口写使能信号,宽度为 WRITE_DATA_WIDTH_B / BYTE_WRITE_WIDTH_B =72/72=1
);
endmodule
另外,wea的信号宽度为WRITE_DATA_WIDTH_A / BYTE_WRITE_WIDTH_A。 当WRITE_DATA_WIDTH_A=72,BYTE_WRITE_WIDTH_A=9时,wea的信号宽度为72/9=8。
可以通过wea的某一位或者某几位有效,控制72bits中对应的9bits数据写入到UltraRAM中。比如wea=8'b0000_0011,那么dina[71:0]中的低18bits写入到ultraRAM中,其余bits位不变。
web同理。
根据仿真结果,发现,设置READ_LATENCY_A和READ_LATENCY_B为0时,在地址有效后一个时钟后,ultraRAM数据输出。
四、其他需要注意的事项
使用ultraRAM时,对xpm_memory_tdpram赋值初始化数据,仅可用于仿真,无法用于综合布局布线。(综合时会自动使用BRAM或者报错)