声明:本文仅供参考学习交流,内容为网上整理获得,禁止商用。
特别感谢我的师兄朱博在Memory Compiler使用过程中给予的指导与帮助。
1. 为什么需要Memory Compiler
SRAM、ROM 等存储器是数字IC设计中重要的组成部分,广泛应用于片上数据存储、缓存以及查找表等场景。本文将介绍如何使用Memory Compiler生成所需的存储器IP,并结合RTL调用与VCS功能仿真,完整展示从存储器生成到仿真验证的基本流程。
在数字IC设计中,经常需要存储大量的中间数据。如果存储规模很小,我们可以直接使用寄存器实现;但随着存储容量不断增加,继续使用寄存器就会带来非常高的硬件开销。以最常见的SRAM为例,一个经典的6T SRAM单元只需要6个MOS管即可存储1bit数据。而在标准单元设计中,一个能够存储1 bit数据的D触发器通常需要20个甚至更多晶体管才能实现。虽然两者在功能和使用方式上并不完全等价,但从存储密度的角度来看,SRAM的优势十分明显。
Memory Compiler是用于生成SRAM、ROM等存储器IP的工具。设计人员可以根据实际需求配置存储深度、数据位宽、端口类型等参数,并生成对应的存储器宏单元及其仿真、综合、时序分析和后端实现所需的相关文件,从而方便地将存储器集成到ASIC设计中。
2. TSMC 28nm工艺库——Memory Compiler (MC2)介绍
本文使用的Memory Compiler基于台积电28nm工艺平台配套的存储器编译环境,完整的TSMC 28nm工艺库所包含的子目录如下,工艺库可在网上获取。
各个目录下文件功能描述如下:
EFUSE:存放电熔丝存储器宏,用于永久保存芯片配置、修复信息、校准值或密钥。General_AppNotes_PR_Constraint_File:存放IO、ESD、eFuse及特殊版图设计的说明和约束文档。IO:存放芯片输入输出、焊盘和ESD保护单元,用于连接芯片内部电路与外部引脚。logic:存放数字标准单元库,用于逻辑综合、时序分析和布局布线。Memory:存放SRAM、ROM、Register File等存储器的生成工具、配置文件和相关模型。PDK:存放工艺器件、仿真模型及DRC、LVS、PEX规则,是电路设计和版图验证的基础。TF:存放不同金属层结构和EDA工具所需的技术文件,用于布局布线、寄生参数提取和最终签核。
其中,Memory目录主要存放存储器IP的生成与集成资源,包括Memory Compiler工具、SRAM、ROM和Register File等存储器的配置文件,以及相关的时序、仿真和物理实现模型。对于存储器IP的生成而言,Memory目录下的内容已经完全够用了,具体内容如下。
先看懂文件夹公共部分的命名规则:
tsn:TSMC存储器IP的产品命名前缀。28:28nm工艺。hpcp:High Performance Compact Plus,即N28HPC+工艺。
目录1下包含了三个子目录:
tsmc_n28hpcpmc_20120200_110a:Memory Compiler公共执行环境,其中mc2-eu是核心执行程序,由各存储器的.pl脚本调用,用于加载对应的.mco编译器对象,并根据config.txt中的深度、位宽等参数生成指定规格的存储器IP。目录中还包含Linux等版本程序、安装脚本、许可证工具及使用文档。temn28hpcphssrammacros_170a:N28HPC+高速固定SRAM宏库。“固定”表示这些存储器规格已由厂商预先生成,用户只能从现有宏中选择,不能通过Memory Compiler自由配置容量和位宽。该目录包含Databook、9984个.ds数据表,以及GDS、NLDM、LEF、SPICE、Verilog和MDT等设计视图。tsn28hpcpl2spsrammacros_180a:面向L2 Cache的固定单端口SRAM宏库,同样由厂商预先生成一组确定规格的存储器,设计时可直接选用,无需运行Memory Compiler。该目录包含256个.ds数据表及相应的物理、时序和仿真视图。
其他目录分别存放不同类型存储器的MC2工具包,用于配置并生成相应规格的Register File、SRAM或ROM存储器IP。内部主要包含了存储器的.pl脚本、.mco编译器对象、config.txt参数配置等文件。文件夹的命名含义如下:
tsn28hpcp1prf_20120200_130a:1prf表示One-Port Register File,即单端口寄存器文件编译器。tsn28hpcp2prf_20120200_130a:2prf表示Two-Port Register File,即双端口寄存器文件编译器。tsn28hpcpd127spsram_20120200_180a:d127spsram表示D127系列Single-Port SRAM Compiler,即单端口SRAM编译器。tsn28hpcpdpsram_20120200_130a:dpsram表示Dual-Port SRAM Compiler,即双端口SRAM编译器。tsn28hpcprom_20120200_130a:rom表示ROM Compiler,即ROM编译器。tsn28hpcpuhddpsram_20120200_170a:uhd表示Ultra High Density,dpsram表示Dual-Port SRAM,因此这是超高密度双端口SRAM Compiler。tsn28hpcpuhdspsram_20120200_170a:uhd表示Ultra High Density,spsram表示Single-Port SRAM,因此这是超高密度单端口SRAM Compiler。
整个存储IP的生成流程可以概括为:
先选对应的MC2工具包,并在config.txt中填写存储器的深度、位宽等功能选项;随后运行对应的.pl脚本,该脚本会读取并检查配置参数,加载该存储器类型对应的.mco编译器对象,然后调用1/tsmc_n28hpcpmc_20120200_110a/.../MC2_2012.02.00.d/bin/Linux-64/mc2-eu核心程序完成编译。最终,MC2会生成指定规格的存储器IP,并输出用于芯片设计的Databook、GDS、LEF、SPICE、NLDM和Verilog等文件。
注意:执行mc2-eu来生成存储器IP需要许可证文件license.dat。解决方法:₥㏄ 2012 02 00 d - EDA资源使用讨论 - EETOP 创芯网论坛 (原名:电子顶级开发网) - Powered by Discuz! 替换/bin/Linux-64/mc2-eu和/aux/flexlm/amd64_re3/interrad两个文件,并添加可执行权限。经过本人测试,该方法有效。
3. 存储IP生成及功能仿真
规范化工程目录,让整个工程的结构更加清晰。在目录mem下来生成所需的存储单元,假设项目中需要一个双口RAM和一个ROM,所以在mem下再创建两个目录dpsram512_16bit和rom512_8bit。
3.1 DPSRAM案例
3.1.1 SRAM IP的生成
先确定存储器类型为双口SRAM,可以选择目录tsn28hpcpdpsram_20120200_130a下的工具包。将目录tsn28hpcpdpsram_20120200_130a/下的4个文件复制到目录/mem/dpsram512_16bit/下。
在目录/mem/dpsram512_16bit/下给一些文件添加权限,方便后续操作。
chmod+w config.txtchmod+x tsn28hpcpdpsram_130a.pl先看一下原始的config.txt文件中的内容为:
4096x36m8m 4096 x 36 m 8 m │ │ │ │ │ └── 分段模式:Medium │ │ │ │ └───── MUX数值:8 │ │ │ └──────── MUX字段的标识符 │ │ └─────────── 数据位宽:36 bit │ └─────────────── 深度与位宽的分隔符 └──────────────────── 存储深度:4096个地址表示生成一个深度为4096、位宽为36bit、采用MUX 8和Medium分段模式的双端口SRAM。但是这些配置信息并不能随意填写,而是要遵照对应工具包的Databook中的要求。Databook的目录为tsn28hpcpdpsram_20120200_130a/。下表为Databook中规定的双口SRAM配置相关的约束条件。
- 分段模式只能选择
F或M,其中F偏向速度,M更侧重面积与容量。 - MUX只能选择4、8或16,它决定阵列的列复用方式和长宽比例。
- Word Depth不能在范围内随意填写。MUX为4、8、16时,深度必须分别从32、64、128开始,并按照16、32、64的步长递增,最大深度还会随分段模式变化。
- Word Width则必须为整数。MUX为4、8、16时分别支持4~72、4~36和4~18bit。
- 计算
WL = WordDepth / MUX,避免取到Databook注明的特殊禁用值。对于F分段,WL不能为68,132和196;而对于M分段,WL则不能为132,260和388。
如果所需存储空间超过单个存储器IP的容量,则可使用多个合法规格的存储器IP进行扩展组合,并行连接扩展位宽,分Bank扩展深度。这里我们需要生成512x16bit的双口SRAM,可在config.txt文件中写入如下内容:
512x16m4f在目录/mem/dpsram512_16bit/下打开终端命令行,输入如下指令:
exportPATH=/data/SunYuhao_TSMC28/TSMC28/Memory/1/tsmc_n28hpcpmc_20120200_110a/AN61001_20180125/TSMCHOME/sram/Compiler/tsmc_n28hpcpmc_20120200_110a/MC2_2012.02.00.d/bin/Linux-64:$PATHwhichmc2-eu该命令将MC2的bin目录加入当前Shell的命令搜索路径,使用户可以直接执行mc2-eu,末尾的$PATH用于保留系统原有的命令搜索路径。其中export不仅完成PATH变量的设置,还会将更新后的路径传递给当前Shell启动的.pl脚本及其他子进程,保证它们也能找到MC2程序。值得注意的是,该设置只在当前终端会话中有效,关闭终端后会失效,失效后需要重新设置。命令which mc2-eu用来检测上一条指令是否配置成功,配置成功后会显示mc2-eu的绝对路径。
输入如下指令,运行tsn28hpcpdpsram_130a.pl脚本,生成双口SRAM IP相关的文件:
./tsn28hpcpdpsram_130a.pl运行.pl脚本后,首先会生成tsdn28hpcpa512x16m4fwbaso.cfg,其中记录SRAM的深度、位宽、MUX、分段模式及BIST等功能选项;随后MC2根据该文件生成lastrun.cfg,补充实际输出路径和各PVT条件,是本次编译的最终配置记录。tsdn28hpcpa512x16m4fwbaso_130a目录中是生成的双口SRAM IP的相关文件。
DATASHEET:包含16种PVT条件下的数据表,用于查看SRAM的面积、时序、功耗及工作条件。DFT:包含ATPG和MBIST相关测试模型,用于生成测试向量和集成存储器自测试功能。GDSII:包含SRAM完整的物理版图数据,用于DRC、LVS等物理验证以及最终流片。LEF:包含SRAM的尺寸、引脚位置和布线阻挡信息,供布局布线工具进行宏单元集成。LOG:记录MC2的配置参数、文件生成过程以及错误和警告信息,可用于检查运行结果。NLDM:包含16种PVT条件下的.lib时序模型,用于逻辑综合、静态时序分析和功耗评估。SPICE:包含SRAM的晶体管级网表和层次信息,用于电路仿真以及版图与原理图一致性验证。VERILOG:包含普通行为模型和电源相关模型,用于SRAM的功能仿真、时序检查及低功耗验证。
其中,VERILOG目录下的文件可以用来进行功能仿真,一共包含两类模型,每类又按照不同的PVT工艺角划分:
- 普通Verilog模型:文件名以
.v结尾,接口中不包含VDD和VSS电源引脚,适合常规RTL功能仿真和时序仿真。 - 带电源引脚的Verilog模型:文件名以
_pwr.v结尾,功能与普通模型基本一致,但增加了VDD和VSS端口,主要用于电源感知或低功耗仿真。
每类模型均提供16种PVT条件,其中ffg、tt和ssg分别表示快、典型和慢工艺角,文件名中的0p9v等字段表示工作电压,25c等字段表示温度。不同文件描述的是同一个SRAM,只是延迟参数对应的工作条件不同,因此一次仿真只需选择一个模型即可。对于一般的功能验证,选择典型工艺、0.9V、25℃对应的普通模型来仿真接口,文件名如下:
tsdn28hpcpa512x16m4fwbaso_130a_tt0p9v25c.v3.1.2 SRAM IP的功能仿真
文件tsdn28hpcpa512x16m4fwbaso_130a_tt0p9v25c.v内部包含了两个模块:
TSDN28HPCPA512X16M4FWBASO:SRAM的顶层仿真模块,定义外部接口,并模拟读写控制、BIST、休眠、关断、端口冲突以及时序检查等行为。TSDN28HPCPA512X16M4FWBASO_Int_Array:内部存储阵列模块,使用Verilog数组保存数据,实现双端口读写、按位写入以及存储内容初始化等基本功能。
其中顶层仿真模块会例化内部存储阵列模块,因此只需要关注顶层仿真模块即可。为了便于在设计中使用,通常会在该模型外部增加一层封装,将复杂的原厂接口整理为常用的时钟、地址、写使能、写数据和读数据接口,并将BIST、休眠、关断等暂不使用的控制信号设置为固定值。后续设计和Testbench只需连接封装模块,无需直接处理SRAM原始接口。
下面进入到功能仿真阶段,大家可以参考我之前的博客《Synopsys 功能仿真入门:Makefile、VCS 与 Verdi 协同使用及 IP 核调用》,有比较详细的解释和步骤。下面的仿真流程就进行简化。
切换到rtl目录,将文件tsdn28hpcpa512x16m4fwbaso_130a_tt0p9v25c.v复制到rtl中,并创建封装模块dpsram512_16bit,内容如下:
`timescale 1ns/1ps module dpsram512_16bit ( input wire clka, input wire ena, input wire wea, input wire [8:0] addra, input wire [15:0] dina, output wire [15:0] douta, input wire clkb, input wire enb, input wire web, input wire [8:0] addrb, input wire [15:0] dinb, output wire [15:0] doutb ); localparam real INPUT_DELAY = 0.1;// Simulation-only input delay: 0.1 ns wire ena_d; wire wea_d; wire [8:0] addra_d; wire [15:0] dina_d; wire enb_d; wire web_d; wire [8:0] addrb_d; wire [15:0] dinb_d; assign #(INPUT_DELAY) ena_d = ena; assign #(INPUT_DELAY) wea_d = wea; assign #(INPUT_DELAY) addra_d = addra; assign #(INPUT_DELAY) dina_d = dina; assign #(INPUT_DELAY) enb_d = enb; assign #(INPUT_DELAY) web_d = web; assign #(INPUT_DELAY) addrb_d = addrb; assign #(INPUT_DELAY) dinb_d = dinb; TSDN28HPCPA512X16M4FWBASO u_sram ( // Low-power and test-option pins: normal operating mode. .SLP (1'b0), .SD (1'b0), .WTSEL (2'b01), .RTSEL (2'b01), .VG (1'b1), .VS (1'b1), // Port A. The original macro control signals are active low. .AA (addra_d), .DA (dina_d), .BWEBA ({16{~wea_d}}), .WEBA (~wea_d), .CEBA (~ena_d), .CLKA (clka), // Port B. The original macro control signals are active low. .AB (addrb_d), .DB (dinb_d), .BWEBB ({16{~web_d}}), .WEBB (~web_d), .CEBB (~enb_d), .CLKB (clkb), // BIST interface is disabled in normal operation. .AWT (1'b0), .AMA (9'b0), .DMA (16'b0), .BWEBMA (16'hffff), .WEBMA (1'b1), .CEBMA (1'b1), .AMB (9'b0), .DMB (16'b0), .BWEBMB (16'hffff), .WEBMB (1'b1), .CEBMB (1'b1), .BIST (1'b0), .CLKM (1'b0), .QA (douta), .QB (doutb) ); endmodule在功能仿真中,不关注低功耗和调节信号和BIST测试接口,故这里将这些信号固定为常数值。对这些信号感兴趣的朋友,可以直接去问问AI。下面稍微介绍一下要使用的读写端口:
| 信号 | 位宽 | 方向 | 作用 |
|---|---|---|---|
CLKA、CLKB | 1 | 输入 | A、B端口的独立时钟,读写操作在对应时钟的上升沿执行。 |
CEBA、CEBB | 1 | 输入 | 端口使能,低有效;为0时允许端口工作,为1时关闭对应端口并保持输出。 |
WEBA、WEBB | 1 | 输入 | 写使能,低有效;端口使能时,为0执行写操作,为1执行读操作。 |
AA、AB | 9 | 输入 | A、B端口的访问地址,可选择0~511共512个存储位置。 |
DA、DB | 16 | 输入 | A、B端口的写入数据,在写周期的时钟上升沿写入指定地址。 |
QA、QB | 16 | 输出 | A、B端口的读取数据;读周期中,地址在时钟上升沿被采样,随后经过SRAM的时钟到输出延迟更新。端口未使能时通常保持上一次读取结果,发生同址读写冲突时可能输出未知值X。 |
BWEBA、BWEBB | 16 | 输入 | 按位写使能,低有效;每一位分别控制对应的数据位,为0时允许写入,为1时保持该位原有数据。 |
信号BWEBA和BWEBB用于分别控制写入数据DA和DB中各数据位的写入权限。为了简化封装,将其所有位统一连接至写使能逻辑,因此每次写操作都会完整写入16位数据,不支持按位写入或部分更新。另外,端口使能和写使能信号都修改成高电平有效。
将除时钟外的信号延迟0.1ns后,再送入TSDN28HPCPA512X16M4FWBASO模块。原因是该仿真模型不支持控制、地址或数据信号与时钟上升沿在同一时刻发生变化,否则可能产生竞争、时序告警或未知值X,导致仿真无法正常进行。加入少量延迟可以错开输入变化与时钟采样时刻,保证模型正确识别读写操作。该延迟仅用于功能仿真,综合时通常会被忽略。注意,所有的Memory Compiler生成的存储IP都有这个特点,一定要加上延迟才能仿真正确。
切换到tb文件夹,创建仿真文件dpsram512_16bit_tb.v,内容如下:
`timescale 1ns/1ps module dpsram512_16bit_tb; parameter clk_period = 10; reg clk; reg ena; reg wea; reg [8:0]addra; reg [15:0]dina; wire [15:0]douta; reg enb; reg web; reg [8:0]addrb; reg [15:0]dinb; wire [15:0]doutb; dpsram512_16bit dpsram512_16bit_inst0( .clka(clk), .ena(ena), .wea(wea), .addra(addra), .dina(dina), .douta(douta), .clkb(clk), .enb(enb), .web(web), .addrb(addrb), .dinb(dinb), .doutb(doutb) ); `ifdef DUMP_FSDB initial begin $fsdbDumpfile("dpsram512_16bit.fsdb"); $fsdbDumpvars(0,dpsram512_16bit_tb); end `endif initial clk = 1'b1; always #(clk_period/2) clk = ~clk; task reset_port_a; ena = 1'b0;wea = 1'b0;addra = 8'd0;dina = 16'd0;#10; endtask task reset_port_b; enb = 1'b0;web = 1'b0;addrb = 8'd0;dinb = 16'd0;#10; endtask initial begin reset_port_a; //write addr 1 data 1 ena = 1'b1;wea = 1'b1;addra = 8'd1;dina = 16'd1;#10; reset_port_a; //read addr 1 ena = 1'b1;wea = 1'b0;addra = 8'd1;dina = 16'd0;#10; reset_port_a; //write addr 3 data 3 ena = 1'b1;wea = 1'b1;addra = 8'd3;dina = 16'd3;#10; reset_port_a; //read addr 3 ena = 1'b1;wea = 1'b0;addra = 8'd3;dina = 16'd0;#10; reset_port_a; //read addr 1 ena = 1'b1;wea = 1'b0;addra = 8'd1;dina = 16'd0;#10; reset_port_a; //read addr 1 ena = 1'b1;wea = 1'b0;addra = 8'd1;dina = 16'd0;#10; reset_port_a; //read addr 1 ena = 1'b1;wea = 1'b0;addra = 8'd1;dina = 16'd0;#10; reset_port_a; #200; $finish; end initial begin reset_port_b; //write addr 2 data 2 enb = 1'b1;web = 1'b1;addrb = 8'd2;dinb = 16'd2;#10; reset_port_b; //read addr 2 enb = 1'b1;web = 1'b0;addrb = 8'd2;dinb = 16'd0;#10; reset_port_b; //write addr 3 data 33 enb = 1'b1;web = 1'b1;addrb = 8'd3;dinb = 16'd33;#10; reset_port_b; //read addr 3 enb = 1'b1;web = 1'b0;addrb = 8'd3;dinb = 16'd0;#10; reset_port_b; //read addr 1 enb = 1'b1;web = 1'b0;addrb = 8'd1;dinb = 16'd0;#10; reset_port_b; //write addr 1 11 enb = 1'b1;web = 1'b1;addrb = 8'd1;dinb = 16'd11;#10; reset_port_b; //read addr 1 enb = 1'b1;web = 1'b0;addrb = 8'd1;dinb = 16'd0;#10; reset_port_b; end endmodule在dpsram512_16bit_tb.v文件中,除了验证A、B端口各自的基本读写功能外,还考虑了双端口同时访问存储器的多种特殊情况,包括两个端口同时读写不同地址、同时读取同一地址、同时向同一地址写入不同数据,以及一个端口读取而另一个端口同时写入同一地址。通过这些场景,可以验证双端口SRAM在正常并行访问和端口冲突情况下的仿真行为。
切换到sim目录下,创建文件filelist.f和Makefile,内容如下所示。filelist.f文件内容:
../tb/dpsram512_16bit_tb.v ../rtl/dpsram512_16bit.v ../rtl/tsdn28hpcpa512x16m4fwbaso_130a_tt0p9v25c.vMakefile文件内容:
# 基本变量MODULE=dpsram512_16bit TB_TOP=$(MODULE)_tb WAVE_NAME=$(MODULE).fsdb FILELIST=filelist.f SIMV=simv#伪目标.PHONY: all com sim verdi clean all:com sim verdi# 编译com: vcs-full64\-sverilog\+v2k\-timescale=1ns/1ps\-debug_access+all\-f$(FILELIST)\+define+DUMP_FSDB\-fsdb\-o$(SIMV)\-lcompile.log# 仿真sim: ./$(SIMV)-lsim.log# 调试verdi: verdi-sverilog\-f$(FILELIST)\-top$(TB_TOP)\-ssf$(WAVE_NAME)\-nologo&# 清理clean:rm-rfcsrc DVEfiles *.daidir *.log simv* *.key *.vpd *.fsdb verdiLog *.conf novas* ucli.key在sim目录下打开终端命令行,输入make运行功能仿真,自动跳出Verdi界面并查看仿真波形。
仿真波形表明,A、B端口分别向地址1和地址2写入数据后,均能正确读出对应数据;当两个端口同时向地址3写入不同数据时发生写写冲突,输出变为未知值X。随后两个端口同时读取地址1时均得到数据1;当A端口读取地址1、B端口同时向该地址写入数据0xB时发生读写冲突,A端口输出X,下一周期重新读取后得到更新数据0xB。波形结果符合双端口SRAM的预期行为。总结来说,两个端口不能同时向同一地址写入数据,两者数据都无法写入;两个端口不能同时对一个地址分别进行写和读,虽然写可以成功写入,但是读出的值为X。
3.2 ROM案例
3.2.1 ROM IP的生成
将目录tsn28hpcprom_20120200_130a/下的文件复制到目录/mem/rom512_8bit/下。向.pl文件添加可执行权限,向config.txt添加可写权限。
ROM Compiler目录中包含多个Perl脚本,用于ROM生成和数据处理。ROM的数据需要固化到芯片版图中,因此除了生成脚本外,还需要数据格式转换和内容校验脚本。
tsn28hpcprom_130a.pl:主生成脚本,读取config.txt并调用mc2-eu生成ROM IP。bin2hex.pl:将二进制ROM数据转换为十六进制格式。hex2intel.pl:将普通十六进制数据转换为ROM Compiler使用的Intel HEX格式。chk_rom_code_n28.pl:检查生成的网表或Verilog模型中的ROM内容是否与原始数据一致。
创建ROM数据文件rom512_8bit.hex,采用十六进制格式存放数据。文件共包含512行,每行对应一个ROM地址。这里采用了一个完整周期的正弦波数据,部分内容如下所示。
80 81 83 ... 7C 7E将HEX格式转换为ROM Compiler使用的Intel HEX格式,运行下方指令:
perl hex2intel.pl rom512_8bit.hex rom512_8bit.intelhex指令生成了文件rom512_8bit.intelhex,部分内容如下所示:
:01000000807F :01000100817D :01000200837A ... :0101FE007C84 :0101FF007E81 :00000001FF打开config.txt文件,自行查看Databook要求,输入下方内容:
512x8m8m rom512_8bit.intelhex配置MC2环境,并生成ROM IP核,指令和生成内容如下所示:
exportPATH=/data/SunYuhao_TSMC28/TSMC28/Memory/1/tsmc_n28hpcpmc_20120200_110a/AN61001_20180125/TSMCHOME/sram/Compiler/tsmc_n28hpcpmc_20120200_110a/MC2_2012.02.00.d/bin/Linux-64:$PATHperl tsn28hpcprom_130a.pl如上图所示,生成了ROM IP核在各个IC设计阶段需要的文件,其中文件TS3N28HPCPA512X8M8MBS.rom.v在后续的功能仿真中会被使用到。
下一步检查ROM数据是否正确固化,运行校验脚本,将输入的Intel HEX数据与生成的SPICE网表中实际固化的ROM内容进行逐地址比较,确认512个地址中的8 bit数据完全一致。指令如下所示:
perl chk_rom_code_n28.pl ts3n28hpcpa512x8m8mbs_130a/SPICE/ts3n28hpcpa512x8m8mbs_130a.spi rom512_8bit.intelhex终端命令行显示结果:
Result : All Pass3.2.2 ROM IP的功能仿真
将目录mem/rom512_8bit/ts3n28hpcpa512x8m8mbs_130a/VERILOG/下的文件ts3n28hpcpa512x8m8mbs_130a_tt0p9v25c.v复制到rtl目录下,并切换到rtl目录下。
ts3n28hpcpa512x8m8mbs_130a_tt0p9v25c.v文件中同样包含两个模块,一个是顶层仿真模块,一个是内部存储阵列模块,其中顶层仿真模块例化了内部存储阵列模块。下面创建封装模块rom512_8bit.v,内容如下所示:
`timescale 1ns/1ps module rom512_8bit ( input wire clk, input wire en, input wire [8:0] addr, output wire [7:0] dout ); // Keep the clock undelayed and delay only the external non-clock inputs. wire en_d; wire [8:0] addr_d; assign #0.1 en_d = en; assign #0.1 addr_d = addr; TS3N28HPCPA512X8M8MBS u_rom ( // Normal read interface. CEB is active low in the original macro. .A (addr_d), .CEB (~en_d), .CLK (clk), .Q (dout), // BIST interface is disabled during normal operation. .AM (9'b0), .CEBM (1'b1), .BIST (1'b0), // Normal-power and recommended process/test settings. .SLP (1'b0), .RTSEL (2'b01), .PTSEL (2'b01), .TRB (2'b01) ); endmodule切换到tb目录下,创建仿真文件rom512_8bit_tb.v,内容如下:
`timescale 1ns/1ps module rom512_8bit_tb; parameter clk_period = 10; reg clk; reg en; reg [8:0]addr; wire [7:0]dout; rom512_8bit rom512_8bit_inst( .clk(clk), .en(en), .addr(addr), .dout(dout) ); `ifdef DUMP_FSDB initial begin $fsdbDumpfile("rom512_8bit.fsdb"); $fsdbDumpvars(0,rom512_8bit_tb); end `endif initial clk = 1'b1; always #(clk_period/2) clk = ~clk; integer i; initial begin en = 1'b0; addr = 9'd0; #100; for(i=0;i<512;i=i+1)begin en = 1'b1; addr = i; #10; end en = 1'b0; #100; $finish; end endmodule切换到sim目录下,修改Makefile文件中变量MODULE = rom512_8bit即可。同时,编辑filelist.f文件内容为:
../tb/rom512_8bit_tb.v ../rtl/rom512_8bit.v ../rtl/ts3n28hpcpa512x8m8mbs_130a_tt0p9v25c.v注意:想要完成功能仿真,需要将文件TS3N28HPCPA512X8M8MBS.rom.v也复制到sim目录下。该文件在运行tsn28hpcprom_130a.pl脚本时被生成出来,目录为mem/rom512_8bit/ts3n28hpcpa512x8m8mbs_130a。在sim目录下打开终端命令行,输入命令make完成ROM的功能仿真,Verdi界面自动出现。修改信号显示为Analog Waveform,波形如下所示。
4. 总结
本文对SRAM和ROM两种IP的生成流程进行了简单介绍和演示,剩余的Memory Compiler内容需要大家自己去探索。有任何问题,欢迎大家在评论区进行讨论!