【EDA软件】【联合Modelsim 同步FIFO仿真】
1. 背景
使用者完成功能RTL代码开发,需要进行功能仿真。RTL代码中可能使用了IP,使用者不知道这些IP的仿真库文件。
2. 分析
不同的FPGA厂商都各自不同的仿真库文件,并且不同系列型号仿真库文件也不同,而且IP也可能需要*.vp(HME是这种文件类型)文件。
关于HME的EDA如何联合Modelsim进行仿真,请查阅此【EDA软件】【联合Modelsim仿真使用方法】_eda仿真-CSDN博客帖子。
3. 同步FIFO仿真
步骤如下:
a. 为了方便快速仿真,使用了HME自有内部晶振、PLL和POR IP,以此IP产生时钟和复位,供TestBench使用;
b. 选择FIFO IP,生成RTL代码;
c. 自己编写简单同步FIFO;
d. TestBench编写激励,例化HME的FIFO IP文件,以及RM FIFO文件,如下图。
e. 使用Modelsim仿真,查看并对比波形。
图1
3.1 EDA产生基本IP
oscillator_v1:是EDA工具自带80MHz的晶振IP;
pll_v1:是EDA工具自带的PLL IP;因为使用了oscillator_v1,PLL的输入时钟需要设置为80MHz,输出个数和频率可以自行设置。该举例是1个输出的100MHz频率;
por_v1_1:是EDA工具自带的POR IP。
以上三个IP的使用,可以查阅HME的相关资料。另外本次举例是选用HME H7系列。
3.2 EDA生成同步FIFO
图2
a. 选中同步FIFO;
b. 设置地址位宽以及数据宽度;
c. First word fall through:预读功能,举例不选。
图3
d. 端口的选择,举例仅使用上下溢出;
图4
e. 读写清除、操作、阈值设置、读写计数,举例仅操作。
生成后EDA界面如图5.
图5
3.3 编写RM的同步FIFO代码
RM 的FIFO分为三部分:
rm_sync_fifo.v:同步FIFO的接口,内部实现FIFO CTRL、SDPRAM的例化;
module rm_sync_fifo #(parameter DATA_WIDTH = 32 ,parameter ADDR_WIDTH = 10 ,parameter ALFULL_THD = ((1 << ADDR_WIDTH) - 10),parameter ALEMPTY_THD = 10
)
(input clk ,input rst_n ,input wen ,input ren ,input [DATA_WIDTH-1:0] wdata ,output wire [DATA_WIDTH-1:0] rdata ,output wire rempty ,output wire wfull ,output wire overflow ,output wire underflow ,output wire almost_full ,output wire almost_empty
);wire wr_en ;
wire [ADDR_WIDTH-1:0] wr_addr ;
wire [DATA_WIDTH-1:0] wr_data ;
wire rd_en ;
wire [ADDR_WIDTH-1:0] rd_addr ;
wire [DATA_WIDTH-1:0] rd_data ;rm_sync_fifo_ctrl #(.DATA_WIDTH ( DATA_WIDTH ),.ADDR_WIDTH ( ADDR_WIDTH ),.ALFULL_THD ( ALFULL_THD ),.ALEMPTY_THD ( ALEMPTY_THD )
)
u_rm_sync_fifo_ctrl (.clk ( clk ), // i.rst_n ( rst_n ), // i.wren ( wen ), // i.rden ( ren ), // i.wdata ( wdata ), // i.rdata ( rdata ), // o.rempty ( rempty ), // o.wfull ( wfull ), // o.overflow ( overflow ), // o.underflow ( underflow ), // o.almost_full ( almost_full ), // o.almost_empty ( almost_empty), // o.wr_en ( wr_en ), // o.wr_addr ( wr_addr ), // o.wr_data ( wr_data ), // o.rd_en ( rd_en ), // o.rd_addr ( rd_addr ), // o.rd_data ( rd_data ) // i
);rm_sdpram #(.DATA_WIDTH ( DATA_WIDTH ),.ADDR_WIDTH ( ADDR_WIDTH )
)
u_rm_sdpram (.wr_clk ( clk ), // i.wr_rst_n ( rst_n ), // i.wr_en ( wr_en ), // i.wr_addr ( wr_addr ), // i.wr_data ( wr_data ), // i.rd_clk ( clk ), // i.rd_rst_n ( rst_n ), // i.rd_en ( rd_en ), // i.rd_addr ( rd_addr ), // i.rd_data ( rd_data ) // o
);endmodule
rm_sync_fifo_ctrl.v:FIFO CTRL实现读写地址、空满信号;
module rm_sync_fifo_ctrl #(parameter DATA_WIDTH = 32 ,parameter ADDR_WIDTH = 10 ,parameter ALFULL_THD = ((1 << ADDR_WIDTH) - 10),parameter ALEMPTY_THD = 10
)
(input clk ,input rst_n ,input wren ,input rden ,input [DATA_WIDTH-1:0] wdata ,output wire [DATA_WIDTH-1:0] rdata ,output wire rempty ,output wire wfull ,output wire overflow ,output wire underflow ,output wire almost_full ,output wire almost_empty ,output wire wr_en ,output reg [ADDR_WIDTH-1:0] wr_addr ,output wire [DATA_WIDTH-1:0] wr_data ,output wire rd_en ,output reg [ADDR_WIDTH-1:0] rd_addr ,input wire [DATA_WIDTH-1:0] rd_data
);localparam DEPTH = (1 << ADDR_WIDTH);
reg [ADDR_WIDTH-1:0] cnt ;
reg [ADDR_WIDTH :0] wr_cnt ;
reg [ADDR_WIDTH :0] rd_cnt ;
reg [ADDR_WIDTH-1:0] fifo_cnt ;assign wr_en = wren & (!wfull);
assign rd_en = rden & (!rempty);
assign wr_data = wdata;
assign rdata = rd_data;always @ (posedge clk or negedge rst_n) beginif (!rst_n) beginwr_addr <= {ADDR_WIDTH{1'b0}};endelse if (wr_en) beginwr_addr <= wr_addr + 1'b1;endelse;
endalways @ (posedge clk or negedge rst_n) beginif (!rst_n) beginrd_addr <= {ADDR_WIDTH{1'b0}};endelse if (rd_en) beginrd_addr <= rd_addr + 1'b1;endelse;
endalways @ (posedge clk or negedge rst_n) beginif (!rst_n) beginwr_cnt <= {(ADDR_WIDTH+1){1'b0}};endelse if (wr_en) beginwr_cnt <= wr_cnt + 1'b1;endelse;
endalways @ (posedge clk or negedge rst_n) beginif (!rst_n) beginrd_cnt <= {(ADDR_WIDTH+1){1'b0}};endelse if (rd_en) beginrd_cnt <= rd_cnt + 1'b1;endelse;
endalways @ (posedge clk or negedge rst_n) beginif (!rst_n) begincnt <= {ADDR_WIDTH{1'b0}};endelse begincase ({wr_en,rd_en})2'b10: cnt <= cnt + 1'b1;2'b01: cnt <= cnt - 1'b1;default:;endcaseend
endassign wfull = (cnt == DEPTH) ? 1'b1 : 1'b0;
assign rempty = (cnt == {ADDR_WIDTH{1'b0}}) ? 1'b1 : 1'b0;
assign overflow = (wr_cnt > rd_cnt) ? 1'b1 : 1'b0;
assign underflow = (rd_cnt > wr_cnt) ? 1'b1 : 1'b0;assign almost_full = 1'b0;
assign almost_empty = 1'b0;endmodule
rm_sdpram.v:寄存器搭建的RAM
module rm_sdpram #(parameter DATA_WIDTH = 32 ,parameter ADDR_WIDTH = 10 ,parameter FISRT_WORD_EN = 1
)
(input wr_clk ,input wr_rst_n ,input wr_en ,input [ADDR_WIDTH-1:0] wr_addr ,input [DATA_WIDTH-1:0] wr_data ,input rd_clk ,input rd_rst_n ,input rd_en ,input [ADDR_WIDTH-1:0] rd_addr ,output reg [DATA_WIDTH-1:0] rd_data
);localparam DEPTH = (1 << ADDR_WIDTH);reg [DATA_WIDTH-1:0] memory [DEPTH-1:0];always @ (posedge wr_clk or negedge wr_rst_n) begin : WRITE_DATAinteger i;if (!wr_rst_n) beginfor (i = 0; i < DEPTH; i = i + 1) begin : INIT_MEMmemory[i] <= {DATA_WIDTH{1'b0}};endendelse if (wr_en == 1'b1) beginmemory[wr_addr] <= wr_data;endelse;
endalways @ (posedge rd_clk or negedge rd_rst_n) begin : READ_DATAif (!rd_rst_n) beginrd_data <= {DATA_WIDTH{1'b0}};endelse if (rd_en == 1'b1) beginrd_data <= memory[rd_addr];endelse;
endendmodule
此处使用了UVM中RM(Reference Model:参考模型)思想,使用者也可以不用跳过此步,使用TestBench例化同步FIFO IP。
3.4 TestBench编码
tb_top.v
module tb_top ();wire inner_osc_clk ;
wire clk ;
wire rst_n ;reg [9:0] cnt ;
reg fifo_wen ;
reg [31:0] fifo_wdata ;
reg fifo_ren ;
wire [31:0] fifo_rdata ;
wire fifo_rempty ;
wire fifo_wfull ;wire [31:0] rm_fifo_rdata ;
wire rm_fifo_rempty ;
wire rm_fifo_wfull ;oscillator_v1 u_oscillator_v1(.clkout ( inner_osc_clk )
);pll_v1 u_pll_v1(.clkin0 ( inner_osc_clk ), // i.locked ( pll_locked ), // o.clkout0 ( fpga_clk ) // o
);por_v1_1 u_por_v1_1(.O ( por_locked )
);assign sys_rst_n = pll_locked & por_locked;assign clk = fpga_clk;
assign rst_n = sys_rst_n;always @ (posedge clk or negedge rst_n) beginif (~rst_n) begincnt <= 10'd0;endelse begincnt <= cnt + 1'd1;end
endalways @ (posedge clk or negedge rst_n) beginif (~rst_n) beginfifo_wen <= 1'b0;endelse if ((~fifo_wfull) && ((cnt[1:0] == 2'd0))) beginfifo_wen <= 1'b1;endelse beginfifo_wen <= 1'b0;end
endalways @ (posedge clk or negedge rst_n) beginif (~rst_n) beginfifo_wdata <= 32'b0;endelse if (fifo_wen == 1'b1) beginfifo_wdata <= fifo_wdata + 1'b1;endelse;
endalways @ (posedge clk or negedge rst_n) beginif (~rst_n) beginfifo_ren <= 1'b0;endelse if ((~fifo_rempty) && (cnt[1:0] == 2'd3)) beginfifo_ren <= 1'b1;endelse beginfifo_ren <= 1'b0;end
endfifo_v2 u_fifo_v2 (.clk ( clk ), // i.rst_n ( rst_n ), // i.wdata ( fifo_wdata ), // i.rdata ( fifo_rdata ), // o.wen ( fifo_wen ), // i.ren ( fifo_ren ), // i.overflow ( ), // o.underflow ( ), // o.almost_full ( ), // o.almost_empty ( ), // o.rempty ( fifo_rempty ), // o.wfull ( fifo_wfull ) // o
);rm_sync_fifo u_rm_sync_fifo (.clk ( clk ), // i.rst_n ( rst_n ), // i.wdata ( fifo_wdata ), // i.rdata ( rm_fifo_rdata ), // o.wen ( fifo_wen ), // i.ren ( fifo_ren ), // i.overflow ( ), // o.underflow ( ), // o.almost_full ( ), // o.almost_empty ( ), // o.rempty ( rm_fifo_rempty ), // o.wfull ( rm_fifo_wfull ) // o
);endmodule
3.5 仿真工程
复用FPGA工程目录,添加sim和sim_src文件夹,如图6。
图6
sim文件夹存放仿真脚本,如图7;
图7
关于以上文件的内容可以参考【EDA软件】【联合Modelsim仿真使用方法】_eda仿真-CSDN博客帖子。
重点描述dut_files.list
D:/02_Fuxi/2024-12-31-win64-rel-10/data/lib/p0_sim.v
../src/ip/pll_v1.v
../src/ip/por_v1_1.v
../src/ip/oscillator_v1.v
../src/ip/fifo_v2_emb_v1.v
../src/ip/fifo_v2.v
../ip_core/fifo_v2/sim/P0/src/hme_ip_syn_fifo_v2_0.vp
文件1行:p0_sim.v文件是基本的仿真库文件;
文件2~6行的文件是EDA 产生IP的文件;
文件7行:同步FIFO必须的hme_ip_syn_fifo_v2_0.vp文件,如果使用异步FIFO,需要使用hme_ip_asyn_fifo_v2_0.vp文件。注意H7对应的路径是P0。
sim_src文件夹存放RM代码和激励文件,如图8。
图8
3.6 仿真结果
点击run_sim.bat,查看运行结果,如图9。
图9
4. 缺点
该举例最终通过"肉眼"比较波形判断是否正确,这种方法是不可取的。使用者要是熟练使用UVM,可以使用UVM,这边便于查看结果。
本举例目的使用HME平台代码仿真时,同步FIFO IP所需要的必须文件。