目錄
RAM的例化
RAM的初始化
IP Catalog例化的RAM的初始化
自定義數組和XPM RAM的初始化
初始化資料的可讀性
本文所述的内容均以使用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上。