ROM.v代碼
這個模塊設計的關鍵是在復位信號中執行初始化代碼,讀取指定位置的HEX文件中的數據初始化rom,然后在其他時鍾沿時刻輸出地址所指的數據。
//read hex file to initial ROM or RAM
module ROM(
input clk,
input rst_n,
input[15:0] addr,
output reg[7:0] q
);
parameter filename="F:/project/cpu/code/ModelSim/04_ROMInitTest/src/ROM.hex";
reg[ 7:0] char_1st;
reg[15:0] address; // Rom address
reg[ 7:0] len; // bytes of one line in the hex file
reg[ 7:0] dat;
reg[7:0] sum; // intel hex file verification
reg[640:1] errstr;
reg[7:0] rom[0:4095];
reg CanRead;
integer i,fp,code;
always@(posedge clk)
if(!rst_n)begin
char_1st = 0;
address =0;
len =0;
dat =0;
sum =0;
CanRead =1;
fp=$fopen(filename,"r");
if(fp==0)begin
$display($time,"ERROR: Hex File %s cann't be open!",filename);
$stop; // stop when no such file
end
else begin
$display($time,"Message: Hex File %s open succese!",filename);
end
if($ferror(fp,errstr))begin
$display("%s",errstr);
$display($time,"ERROR: Hex File %s error",filename);
$stop;
end
while(CanRead)begin
//first byte should be ":"
char_1st=$fgetc(fp);
if(char_1st=="\n")begin //if it is blank line read next char
char_1st=$fgetc(fp);
end
else if(char_1st!=":")begin // every line begin with ":"
//if one line is not start with ":" this file maybe not a hex file
$display($time,"Error: The 1st char isn't [:]Hex File convert end! ");
CanRead = 0;
end
else begin
//the second and third byte means how many data in this line
code=$fscanf(fp,"%2x",len);
if(len==0)begin
$display($time,"Message:Initial Rom Finish!");
CanRead = 0;
end
else begin
sum=len;
code=$fscanf(fp,"%4x",address);
sum=sum+address;
sum=sum+(address>>8); // unsigned sum
code=$fscanf(fp,"%2x",dat); // data type
sum=sum+dat;
for(i=0;i<len;i=i+1)begin
code=$fscanf(fp,"%2x",dat);
sum=sum+dat;
rom[address]=dat; // ram read data from file
address=address+1;
end
code=$fscanf(fp,"%2x\n",dat); // check data
sum=sum+dat;
if(sum!=0)begin
$display("Error:verification code is not zero!");
CanRead = 0;
end
end
end
end
end
else begin
q<=rom[addr];
end
endmodule
測試文件
ROM_t.v
module Rom_t;
reg clk;
reg rst_n;
reg[15:0] addr;
wire[7:0] q;
ROMU_rom(
clk,
rst_n,
addr,
q
);
always #1 clk=~clk;
initial begin
clk<=0;
rst_n<=1;
#5 rst_n<=0;
#5 rst_n<=1;
end
always@(posedge clk)
if(!rst_n)begin
addr<=0;
end
else begin
addr<=addr+1;
end
endmodule
測試所用HEX是Keil編譯一段C語言代碼后生成的十六進制文件。用STC-ISP打開如下圖(可以用任意一個HEX文件繼續后續實驗)

以上3個文件(ROM.v,Rom_t.v,ROM.hex)放在\04_ROMInitTest\src目錄下,代碼中會從這個目錄打開ROM.hex文件。
Modelsim中啟動仿真,結果如下圖

控制台輸出如下內容:

上面的時序圖中可見clk的上升沿中有2次都檢測到rst_n為0,所以上面的初始化代碼會執行2次。
附上網上一段關於HEX和mif文件相關的資料:
hex文件又叫intel hex文件
Intel HEX由任意數量的十六進制記錄組成。每個記錄包含5個域,它們按以下格式排列:
:llaaaatt[dd...]cc
每一組字母對應一個不同的域,每一個字母對應一個十六進制編碼的數字。每一個域由至少兩個十六進制編碼數字組成,
它們構成一個字節,就像以下描述的那樣:
: 每個Intel HEX記錄都由冒號開頭.
ll 是數據長度域,它代表記錄當中數據字節(dd)的數量.
aaaa 是地址域,它代表記錄當中數據的起始地址.
tt 是代表HEX記錄類型的域,它可能是以下數據當中的一個:
00 – 數據記錄
01 – 文件結束記錄
02 – 擴展段地址記錄
04 – 擴展線性地址記錄
dd 是數據域,它代表一個字節的數據.一個記錄可以有許多數據字節.記錄當中數據字節的數量必須和數據長度域(ll)中指
定的數字相符.
cc 是校驗和域,它表示這個記錄的校驗和.校驗和的計算是通過將記錄當中所有十六進制編碼數字對的值相加,以256為模
進行以下補足.
數據記錄
Intel HEX文件由任意數量以回車換行符結束的數據記錄組成.數據記錄外觀如下:
:10246200464C5549442050524F46494C4500464C33
其中:
10 是這個記錄當中數據字節的數量.
2462 是數據將被下載到存儲器當中的地址.
00 是記錄類型(數據記錄)
464C…464C是數據.
33 是這個記錄的校驗和.
mif文件比起hex文件就要"單純"許多 因為她沒有最后的坑爹的校驗和 所以生成起來比較容易
mif文件的格式范例如下
WIDTH=128;
DEPTH=4096;
ADDRESS_RADIX=HEX; //十進制為DEC
DATA_RADIX=HEX; //二進制為BIN
CONTENT BEGIN
0000 : 000000000000000000000000FFFFFFF0;
0001 : 000000000000000000000000FFFFFFF1;
0002 : 000000000000000000000000FFFFFFF2;
0003 : 000000000000000000000000FFFFFFF3;
.............................................................................
..............................................................................
END;
