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;
来源:https://www.cnblogs.com/nf466568905/p/5049864.html