RAM种类
Xilinx的RAM有三种类型:单口RAM、简单双口RAM、真双口RAM。
单口RAM
单口RAM的读和写均使用同一个端口,同一时间只能进行一种操作。
简单双口RAM
简单双口RAM具有两个通道,分别为写通道和读通道,并且每个通道只能完成一种操作。
真双口RAM
真双口RAM具有两个通道,并且这两个通道均可以实现读和写操作。
简单双口RAM实战
本实战例化4种不同位宽转换的RAM,通过仿真查看各种结果。
写操作
四个RAM的写操作使用同一种。
在ena和wea有效时,写入10个数据。
实现代码
always @(posedge i_clk ) begin
if (addra == 'd10 - 1)
ena <= 1'b0 ;
else if (pos_ram_wr_en)
ena <= 1'b1 ;
end
always @(posedge i_clk ) begin
if (ena)
addra <= addra + 1'b1 ;
else
addra <= 'd0 ;
end
always @(posedge i_clk ) begin
if (ena)
dina <= dina + 32'h04040404 ;
else
dina <= 32'h00010203 ;
end
仿真结果
由仿真可以看出,在ena有效时,地址从0开始增加,数据开始写入到RAM中。
虽然在ena无效后,递增和数据均增加了一个,但是由于ena的无效,多出来的地址和数据也不会写入到RAM中。
写入数据长度
由于在ena拉低后,地址还会增加一个,可以利用此特性来寄存写入的数据长度,用于判断读出的数据长度。
将ena打一拍,用于取其下降沿,利用此下降沿信号寄存数据长度。
需要注意:在使用此数据长度时,需要进行-1处理。
实现代码
always @(posedge i_clk ) begin
ena_1d <= ena ;
end
assign neg_ena = !ena & ena_1d ;
always @(posedge i_clk ) begin
if (neg_ena)
num_data <= addra ;
end
仿真结果
32i-8o读操作
此RAM的输入是32bit位宽,输出是8bit位宽,实现了位宽的转换。
在此需要注意,由于写入的位宽和读出的位宽不一致,相应的写入的地址和读出的地址也不同。在此RAM中,读出地址的长度是写入地址长度的4倍。
需要注意:读出的数据与写入的数据的大小端是相反的。
也就是说,如果想按照正确的顺序将数据读出来,需要提前将写入的数据进行大端转小端。
读出的数据在地址有效的下一个时钟周期输出。
实现代码
always @(posedge i_clk ) begin
if (ram1_addrb == (num_data<<2) - 1)
ram1_enb <= 1'b0 ;
else if (neg_ena)
ram1_enb <= 1'b1 ;
end
always @(posedge i_clk ) begin
if (ram1_addrb == (num_data<<2) - 1)
ram1_addrb <= 1'b0 ;
else if (ram1_enb)
ram1_addrb <= ram1_addrb + 1'b1 ;
end
仿真结果
32i-16o读操作
此RAM的输入是32bit位宽,输出是16bit位宽,实现了位宽的转换。
在此需要注意,由于写入的位宽和读出的位宽不一致,相应的写入的地址和读出的地址也不同。在此RAM中,读出地址的长度是写入地址长度的2倍。
需要注意:读出的数据与写入的数据的大小端是相反的。
也就是说,如果想按照正确的顺序将数据读出来,需要提前将写入的数据进行大端转小端。
读出的数据在地址有效的下一个时钟周期输出。
实现代码
always @(posedge i_clk ) begin
if (ram2_addrb == (num_data<<1) - 1)
ram2_enb <= 1'b0 ;
else if (neg_ena)
ram2_enb <= 1'b1 ;
end
always @(posedge i_clk ) begin
if (ram2_addrb == (num_data<<1) - 1)
ram2_addrb <= 1'b0 ;
else if (ram2_enb)
ram2_addrb <= ram2_addrb + 1'b1 ;
end
仿真结果
32i-32o读操作
此RAM的输入是32bit位宽,输出是32bit位宽,实现了数据缓存。
需要注意:读出的数据与写入的数据一致。
读出的数据在地址有效的下一个时钟周期输出。
实现代码
always @(posedge i_clk ) begin
if (ram3_addrb == (num_data<<0) - 1)
ram3_enb <= 1'b0 ;
else if (neg_ena)
ram3_enb <= 1'b1 ;
end
always @(posedge i_clk ) begin
if (ram3_addrb == (num_data<<0) - 1)
ram3_addrb <= 1'b0 ;
else if (ram3_enb)
ram3_addrb <= ram3_addrb + 1'b1 ;
end
仿真结果
32i-64o读操作
此RAM的输入是32bit位宽,输出是64bit位宽,实现了位宽的转换。
这种操作就有意思了,读出的数据都错乱了。
仿真结果
结论
-
RAM中若输入的数据位宽大于读出数据的位宽,那么需要对写入的数据先进行大小端转换;
-
RAM中实现位宽转换比较烦,最好是不用于位宽转换的场景;
-
如果RAM需要进行位宽转换,最好是再接一级FIFO用于位宽转换,而RAM仅用于数据缓存。