版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yinyeyy/article/details/104546018
本文所述的内容均以使用Xilinx器件为前提,不需要进行修改,或者做出少量修改就可以在Altera器件上应用。
RAM的例化
我常使用的例化方法主要有三种。
使用IP Catalog例化
通过代码让编译器推断出RAM
调用原语
上述方法各有优缺点。
IP Catalog的方法容易上手,但是修改起来很是繁琐,需要重新customize,重新OOC综合......,而且不能实现参数化;通过特定代码风格使编译器推断出RAM的方式更为灵活,也可以实现参数化,而且只要设计者对Xilinx的一些约束足够熟悉(比如RAM_STYLE、ROM_STYLE)也可以达到其他方式一样的效果,同时只有这种实现方法可以实现代码在不同厂商间兼容,只是该方法对设计者要求高,尤其是一些复杂应用,如非对称RAM;调用原语的好处是使用灵活,修改方便,可参数化。
个人认为,如果不是为了厂商间兼容,使用原语(即XPM)是更好的实现方式。
通过代码让编译器推断出RAM时,可以参考ug901的RAM HDL Coding Techniques章节。如果使用XPM则可以参考ug974。
RAM的初始化
IP Catalog例化的RAM的初始化
这种情况下都是通过COE文件进行初始化。在IP生成向导中有步骤让使用者指定所需要的COE文件,当然文件内容需要使用者编辑。COE文件具体格式通过直接搜索“COE File Syntax”就可以找到,不同内容的COE文件的格式略有区别,大致都是下面的样子。
****************************************************************** ******** Example of Single Port Block Memory .COE file ********* ****************************************************************** ; Sample memory initialization file for Single Port Block Memory, ; v3.0 or later. ; ; This .COE file specifies initialization values for a block ; memory of depth=16, and width=8. In this case, values are ; specified in hexadecimal format. memory_initialization_radix=16; memory_initialization_vector= ff, ab, f0, 11, 11, 00, 01, aa, bb, cc, dd, ef, ee, ff, 00, ff;
COE文件对格式的要求相对严格。
自定义数组和XPM RAM的初始化
自定义数组可以使用.mem文件初始化,XPM RAM只能通过该文件初始化。初始化的语法通常像下面这样。
module tb(); reg [7:0] test_memory [0:15]; initial begin $readmemh("rom_init.mem", test_memory); end endmodule
使用readmemh和readmemb进行初始化,重点是,这两个命令是可以综合的!!!他们的语法如下。
$readmemh("hex_init.mem", memory_array, [start_address], [end_address]); $readmemb("bin_init.mem", memory_array, [start_address], [end_address]);
其中memory_array是用户定义的数组,start_address和end_address定义了memory_array的哪部分将被初始化,hex_init.mem和bin_init.mem是用户编辑的初始化数据。.mem文件中数据通过空格、tab或者换行符分隔,比如下面这样的格式。
reg [7:0] t_memory [0:3]; $readmemh("init1.mem", t_memory); // Init memory file content beed beef 5a5a a5a5
初始化数据的可读性
由于格式的限制,当存储器的位宽很宽时,上述初始化文件的可读性比较差。比如一个存储单元item[255:0]有多个涵义不同的段组成,每个段的宽度也不同。我在初始化的时候希望数据表述很清晰,每段位宽一目了然。但是上述文件格式做不到。所以在之前的项目中,我使用下面的verilog代码的方式赋值。
always @ (posedge clk) begin if( en ) begin case( addr ) `PAT_ADDR_WID'h00: data <= {`PAT_RESV1'h0, `ADDR'h000000000, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0010}; `PAT_ADDR_WID'h01: data <= {`PAT_RESV1'h0, `ADDR'h000000001, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0001}; `PAT_ADDR_WID'h02: data <= {`PAT_RESV1'h0, `ADDR'h000000002, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0001, `VEC'h0000_0000_0000_0000_0000_0000_0000_0010}; `PAT_ADDR_WID'h03: data <= {`PAT_RESV1'h0, `ADDR'h000000003, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0002, `VEC'h0000_0000_0000_0000_0000_0000_0000_0001}; `PAT_ADDR_WID'h04: data <= {`PAT_RESV1'h0, `ADDR'h000000004, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0003, `VEC'h0000_0000_0000_0000_0000_0000_0000_0010}; `PAT_ADDR_WID'h05: data <= {`PAT_RESV1'h0, `ADDR'h000000005, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000004, `TIMING'h0003, `VEC'h0000_0000_0000_0000_0000_0000_0000_0001}; `PAT_ADDR_WID'h06: data <= {`PAT_RESV1'h0, `ADDR'h000000006, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000008, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0077}; `PAT_ADDR_WID'h07: data <= {`PAT_RESV1'h0, `ADDR'h000000007, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0077}; `PAT_ADDR_WID'h08: data <= {`PAT_RESV1'h0, `ADDR'h000000008, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0077}; `PAT_ADDR_WID'h09: data <= {`PAT_RESV1'h0, `ADDR'h000000009, `CMD'h09, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0000, `VEC'h0000_0000_0000_0000_0000_0000_0000_0010}; endcase end end
可以看到上述方式能够清晰的看到256bits数据由哪些段组成,每段的涵义,每段的值都非常清楚。下面把这种方式和.mem格式初始化数据的内容放在一起对比,就有一个更直观的认识。
`PAT_ADDR_WID'h04: data <= {`PAT_RESV1'h0, `ADDR'h000000004, `CMD'h00, `PAT_RESV2'h0, `OPERA'h00000000, `TIMING'h0003, `VEC'h0000_0000_0000_0000_0000_0000_0000_0010}; 0000000000000004000000000000000300000000000000000000000000000010
第一行表示的数据和最后一行表示的数据是一致的,哪一个更清楚就不用多说了!当然在这里我使用的方式也有局限性,更适合用在位宽大、深度相对小的RAM或ROM上。