前言2026年的嵌入式芯片大赛FPGA创新赛道即将开启为了方便选手们更好地掌握紫光同创FPGA相关的开发流程笔者在闲暇时间专门撰写了相关的避坑指南欢迎各位选手阅读希望对各位的参赛获奖过程能有所帮助。开发环境介绍PDS版本Pango Design Suite 2022.2-SP6.4开发板型号MES50HP小眼睛科技盘古50K开发板底板Modelsim版本Modelsim SE 2020.4DDR控制器例化方法在PDS里建立好工程后可以直接在文件管理器内右键空白处选择New IP并左键点击在DDR-Soft一栏内选择DDR3 Interface并规定IP核的保存路径和实例化时所用的名称如图所示。如上图所示也可以在最上方的选项栏内选择Tools-IP compiler同样按上文所述选择对应的IP核完成生成路径设置和实例命名后按照下面展示的图片对IP核进行设置对于MES50HP开发板一定要严格按照下图的设置才能正常运行。完成以上所有设置后还要在IP compiler界面的左上角点击Generate待IP核生成完成后方可关闭IP compiler界面并在你的工程内实例化对应的DDR3 Interface IP核。DDR控制器仿真教程DDR3 Interface IP核简介HMIC_S IP是紫光同创推出的一款DDR3 IP基于Logos系列FPGA产品资源实现仅支持DDR3可通过公司PDSPango Design Suite套件中的IPCIP Compiler工具完成IP模块的配置和生成。HMIC_S IP产品的主要特性如下支持DDR3支持最大数据位宽32bit用户接口精简的AXI4总线接口、APB总线接口支持可配低功耗模式Self-Refresh和Power Down支持DDR3的最高数据速率达到800MbpsBurst Length 8和单Rank。以上内容来自于紫光同创官方的HMIC_S IP核手册读者如果需要原文件可以在PDS内的IP compiler界面找到这个选项来阅读IP核文件目录简介example_design这是官方在IP核文件内置的一个仿真demo包含ddr3_tb.v这一仿真平台ddr_test_top_tb.v、ddr3芯片对应的参数文件ddr3_parameters.vh和行为级模型ddr3.sv。避坑指南主要讲解如何搭建仿真环境而避坑指南中会详细解释仿真平台内代码的功能和修改方式请各位读者敬请期待。pnr这一文件夹内重要的文件是.fdc文件其中包含了DDR3 Interface IP所需的引脚约束语句请读者把这些引脚约束文件添加到自己工程内的fdc文件中防止出现编译不通过等低级问题。rtlIP核的RTL代码除外部文件以外已经被加密关于这部分的踩坑问题会在后文详细讲解。simIP核的Tcl/Makefile仿真脚本关于这部分的踩坑问题会在后文详细讲解。sim_libIP核的仿真文件库关于这部分的踩坑问题也会在后文详细讲解。IP核与DDR3行为模型的连接在实例化IP核之后读者需要参考这段代码来实现DDR3 Interface与DDR3芯片行为模型的连接////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: Iseealurt // // Create Date: 2025-11-25 15:47 // Design Name: // Module Name: tb_OV_DDR_HDMI_v_2_0 // Project Name: Double-Camera Computer Vision System (DC-CVS) // Target Devices: Pango // Tool Versions: // Description: // // Dependencies: // // Revision: // // // // // // // ////////////////////////////////////////////////////////////////////////////////// timescale 1ps/1ps module tb_OV_DDR_HDMI_v_2_0(); // ----------------------------------- global definition ----------------------------------- define DDR_CLK_SKEW_PS #0 define SIMULATION define sg25E define den4096Mb define x16 // ----------------------------------- included file ----------------------------------- include E:/FPGA OBJ/Double_camera_system/ipcore/ddr3_50h/example_design/bench/mem/ddr3_parameters.vh //这部分按照自己的文件路径进行设置 // ----------------------------------- parameter definition ----------------------------------- parameter MEM_ROW_ADDR_WIDTH 15 ; parameter MEM_COL_ADDR_WIDTH 10 ; parameter MEM_BADDR_WIDTH 3 ; parameter MEM_DQ_WIDTH 32 ; parameter MEM_DQS_WIDTH MEM_DQ_WIDTH/8 ; parameter MEM_DM_WIDTH 4 ; parameter MEM_NUM MEM_DQ_WIDTH/16 ; parameter MEM_ADDR_WIDTH 15 ; // ----------------------------------- variale definition ----------------------------------- reg rst_n; wire mem_rst_n; wire mem_ck; wire mem_ck_n; wire mem_cke; wire mem_cs_n; wire mem_ras_n; wire mem_cas_n; wire mem_we_n; wire mem_odt; wire [MEM_ROW_ADDR_WIDTH-1:0] mem_a; wire [MEM_BADDR_WIDTH-1:0] mem_ba; wire [MEM_DQS_WIDTH-1:0] mem_dqs; wire [MEM_DQS_WIDTH-1:0] mem_dqs_n; wire [MEM_DQ_WIDTH-1:0] mem_dq; wire [MEM_DM_WIDTH-1:0] mem_dm; reg [MEM_NUM:0] mem_ck_dly; reg [MEM_NUM:0] mem_ck_n_dly; // ----------------------------------- hardware template definition for simulation ----------------------------------- GTP_GRS GRS_INST ( .GRS_N(rst_n) // INPUT ); //这是必须例化的全局复位原语没有该原语无法进行仿真 // ---------------------------------------- DDR3 verification model ---------------------------------------- always (*) begin mem_ck_dly[0] lt; mem_ck; mem_ck_n_dly[0] lt; mem_ck_n; end wire [15:0] mem_addr; assign mem_addr {{(ADDR_BITS-MEM_ADDR_WIDTH){1b0}},{mem_a}}; genvar gen_mem; generate for(gen_mem0; gen_memlt;MEM_NUM; gen_memgen_mem1) begin : i_mem always (*) begin mem_ck_dly[gen_mem1] amp;lt;DDR_CLK_SKEW_PS mem_ck_dly[gen_mem]; mem_ck_n_dly[gen_mem1] amp;amp;lt;DDR_CLK_SKEW_PS mem_ck_n_dly[gen_mem]; end ddr3 mem_core ( .rst_n (mem_rst_n ), .ck (mem_ck_dly[gen_mem1] ), .ck_n (mem_ck_n_dly[gen_mem1] ), .cs_n (mem_cs_n ), .addr (mem_addr ), .dq (mem_dq[16gen_mem15:16gen_mem] ), .dqs (mem_dqs[2gen_mem1:2gen_mem] ), .dqs_n (mem_dqs_n[2gen_mem1:2gen_mem] ), .dm_tdqs (mem_dm[2gen_mem1:2gen_mem] ), .tdqs_n ( ), .cke (mem_cke ), .odt (mem_odt ), .ras_n (mem_ras_n ), .cas_n (mem_cas_n ), .we_n (mem_we_n ), .ba (mem_ba ) ); end endgenerate // ---------------------------------------- DUT ---------------------------------------- DUT #( // 可配置参数 - 根据实际需求调整 ) dut ( // 全局信号 .clk_in_50m (clk_in_50m), // 50MHz系统时钟输入 .rst_n (rst_n), // 低电平复位 // DDR SDRAM信号 .mem_rst_n (mem_rst_n), // 内存复位低有效 .mem_ck (mem_ck), // 内存时钟正相 .mem_ck_n (mem_ck_n), // 内存时钟反相 .mem_cke (mem_cke), // 内存时钟使能 .mem_cs_n (mem_cs_n), // 内存片选低有效 .mem_ras_n (mem_ras_n), // 内存行地址选通低有效 .mem_cas_n (mem_cas_n), // 内存列地址选通低有效 .mem_we_n (mem_we_n), // 内存写使能低有效 .mem_odt (mem_odt), // 内存片上终端 .mem_a (mem_a), // 内存地址总线 .mem_ba (mem_ba), // 内存bank地址 .mem_dqs (mem_dqs), // 内存数据选通 .mem_dqs_n (mem_dqs_n), // 内存数据选通反相 .mem_dq (mem_dq), // 内存数据总线 .mem_dm (mem_dm), // 内存数据掩码 ); // ---------------------------------------- Clock signal ---------------------------------------- initial begin clk_in_50m 0; forever #10_000 clk_in_50m ~ clk_in_50m; end //---------------------------------------- Reset ---------------------------------------- initial begin rst_n 0; #100_000 rst_n 1; $display(%0d ps , reset was released,$time); end endmodule注意在仿真过程中DDR_CLK_SKEW_PS参数不能设置得太大笔者常用的数值在5-10左右同样也不建议设置成0这可能会影响DDR3 Interface的自检校准流程导致不能正常初始化。脚本自动化仿真简易教程在实际的项目迭代中笔者并不会使用PDS内置的联合仿真功能而是编写功能不同的文件搭建出一个随时可以启动的仿真验证平台在每一次回归测试后收集仿真日志和功能覆盖率来评估DUT的设计进度。编译文件编写可蒸馏成skills供agent使用在使用Modelsim作为仿真软件时编写Tcl脚本实现自动化仿真是很有必要的一份合适的run_behave_compile.tcl脚本的代码示例如下# ---------------------------------------- # Created on: Mon Sep 22 21:12:57 2025 # Auto generated by Pango # Modified for Full System Simulation # ---------------------------------------- vlib work vmap work ./work vmap usim /PDS_sim_library/usim vmap adc /PDS_sim_library/adc vmap ddrc /PDS_sim_library/ddrc vmap ddrphy /PDS_sim_library/ddrphy vmap hsst_e2 /PDS_sim_library/hsst_e2 vmap iolhr_dft /PDS_sim_library/iolhr_dft vmap ipal_e1 /PDS_sim_library/ipal_e1 vmap pciegen2 /PDS_sim_library/pciegen2 All RTL and IP Core Files vlog -sv -f define.f -work work .../ddr3_50h/ddr3_50h.v .../ddr3_50h/ddr3_50h_ddrphy_top.v .../ddr3_50h/rtl/ipsxb_rst_sync_v1_1.v .../ddr3_50h/rtl/pll/ipsxb_ddrphy_pll_v1_0.v .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_calib_mux_v1_3.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_calib_top_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_control_path_adj_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_data_slice_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_data_slice_dqs_gate_cal_v1_3.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_data_slice_wrlvl_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_dfi_v1_4.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_dll_update_ctrl_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_dqsi_rdel_cal_v1_2.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_dqs_gate_coarse_cal_v1_3.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_dqs_rddata_align_v1_3.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_drift_ctrl_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_gate_update_ctrl_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_gatecal_v1_3.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_info_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_init_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_main_ctrl_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_rdcal_v1_2.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_reset_ctrl_v1_4.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_rst_debounce_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_slice_rddata_align_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_training_ctrl_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_upcal_v1_5.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_wdata_path_adj_v1_0.vp .../ddr3_50h/sim_lib/rtl/ddrphy/ipsxb_ddrphy_wrlvl_v1_0.vp .../ddr3_50h/rtl/ddrphy/ipsxb_ddrphy_slice_top_v1_5.v .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_apb_cross_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_calib_delay_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_cfg_apb_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcd_bm_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcd_rowaddr_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcd_sm_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcd_top_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcp_back_ctrl_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcp_buf_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcp_out_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dcp_top_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_dfi_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_lp_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_mrs_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_prefetch_fifo_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_rdatapath_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_reg_fifo2_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_ui_axi_v1_2a.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_wdatapath_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_wdp_align_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_wdp_dcp_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/ipsxb_mcdq_wrapper_v1_2b.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_com_timing_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_tfaw_timing_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_tfaw_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_act2wr_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_act_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_pre_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_rd_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_ref_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_timing_wr_pass_v1_2.vp .../ddr3_50h/sim_lib/rtl/mcdq_ctrl/syn_mod/ipsxb_mcdq_trc_timing_v1_2.vp .../ddr3_50h/rtl/mcdq_ctrl/distributed_fifo/ipsxb_distributed_fifo_v1_0.v .../ddr3_50h/rtl/mcdq_ctrl/distributed_fifo/rtl/ipsxb_distributed_fifo_ctr_v1_0.v .../ddr3_50h/rtl/mcdq_ctrl/distributed_fifo/rtl/ipsxb_distributed_fifo_v1_0_distributed_fifo_v1_0.v .../ddr3_50h/rtl/mcdq_ctrl/distributed_fifo/rtl/ipsxb_distributed_sdpram_v1_0_distributed_fifo_v1_0.v .../ddr3_50h/example_design/bench/mem/ddr3.sv .../Verification_model/tb_DUT.v脚本内的第一部分是需要用到的仿真库文件第二部分是需要仿真的IP核RTL代码和Bench。注PDS 2022.2-SP6.4联合仿真DDR3 Interface会覆盖上述的Tcl文件同时会把错误的RTL代码路径加入到Compile文件中导致仿真失败因此同样不建议使用联合仿真功能。在完成了编译脚本后还需要继续编写波形观测文件、参数定义文件和仿真启动脚本。波形观测文件Run_behave_simulate.tcl类似地笔者会贴出一个示例供读者参考# # Full System Simulation Waveform Observation Script (ModelSim Only) # Module: OV_DDR_HDMI_v_2_0 (Double Camera System) # Top Testbench: tb_OV_DDR_HDMI_v_2_0 # vsim -vopt -L work -L usim -L adc -L ddrc -L ddrphy -L hsst_e2 -L iolhr_dft -L ipal_e1 -L pciegen2 work.tb_OV_DDR_HDMI_v_2_0 usim.GTP_GRS -voptargsaccnpr Base Path Definitions set DUT /tb_OV_DDR_HDMI_v_2_0/dut set TB /tb_OV_DDR_HDMI_v_2_0 Module Waveform Groups Testbench Level add wave -noupdate -group tb_top ${TB}/* DUT Top Level (wires and interconnects between sub-modules) add wave -noupdate -group dut_top ${DUT}/* video_init - 系统初始化状态机 add wave -noupdate -group video_init ${DUT}/video_init_inst/* uart_cfg - 摄像头I2C配置模块 x2 add wave -noupdate -group cmr1_uart_cfg ${DUT}/cmr_1_uart_cfg_inst/* add wave -noupdate -group cmr2_uart_cfg ${DUT}/cmr_2_uart_cfg_inst/* ms72xx_ctl - HDMI驱动芯片初始化模块 add wave -noupdate -group ms_init ${DUT}/ms_init_module/* PLL - 像素时钟生成 add wave -noupdate -group pix_clk_gene ${DUT}/pix_clk_gene/* DVP_AXI_v_2_0 - 摄像头数据采集转AXI x2 add wave -noupdate -group cmr1_dvp_axi ${DUT}/cmr_1_dvp_axi/* add wave -noupdate -group cmr2_dvp_axi ${DUT}/cmr_2_dvp_axi/* ddr3_50h - DDR3控制器IP核 add wave -noupdate -group ddr3_controller ${DUT}/ddr3_50h_inst/* SGM_TOP - 立体匹配算法顶层含内部子模块 add wave -noupdate -group sgm_top ${DUT}/u_sgm_top/* add wave -noupdate -group sgm_data_engine ${DUT}/u_sgm_top/u_sgm_data_engine/* add wave -noupdate -group {rgb2gray_left} ${DUT}/u_sgm_top/u_rgb2gray_left/* add wave -noupdate -group {rgb2gray_right} ${DUT}/u_sgm_top/u_rgb2gray_right/* add wave -noupdate -group {census_5x5_left} ${DUT}/u_sgm_top/u_census_5x5_left/* add wave -noupdate -group {census_5x5_right} ${DUT}/u_sgm_top/u_census_5x5_right/* add wave -noupdate -group sgm_core ${DUT}/u_sgm_top/u_sgm_new/* add wave -noupdate -group VESA_AXI ${DUT}/u_sgm_top/u_VESA_AXI/* add wave -noupdate -group VESA_AXI ${DUT}/u_sgm_top/u_VESA_AXI/no_buffer_data_acquisition/* add wave -noupdate -group sgm_cmr1_pix_fifo ${DUT}/u_sgm_top/u_cmr1_pix_fifo//* add wave -noupdate -group sgm_cmr2_pix_fifo ${DUT}/u_sgm_top/u_cmr2_pix_fifo//* add wave -noupdate -group sgm_cmr1_census_fifo ${DUT}/u_sgm_top/u_cmr1_census_fifo//* add wave -noupdate -group sgm_cmr2_census_fifo ${DUT}/u_sgm_top/u_cmr2_census_fifo//* add wave -noupdate -group sgm_vsync_fifo ${DUT}/u_sgm_top/u_sgm_vsync_fifo//* axi_arbiter_v_2_0 - 4-user AXI仲裁器 (扁平化架构) add wave -noupdate -group axi_arbiter ${DUT}/u_axi_arbiter_v_2_0//* frame_switch - 帧切换模块 x2 add wave -noupdate -group cmr1_frame_switch ${DUT}/cmr1_frame_switch_v_2_0/* add wave -noupdate -group cmr2_frame_switch ${DUT}/cmr2_frame_switch_v_2_0/* drm_fifo_256b_8d - 像素数据FIFO x2 add wave -noupdate -group cmr1_pix_fifo ${DUT}/cmr_1_pix_fifo/* add wave -noupdate -group cmr2_pix_fifo ${DUT}/cmr_2_pix_fifo/* double_camera_disp - 双摄像头显示驱动 add wave -noupdate -group double_camera_disp ${DUT}/double_camera_disp_inst/* sgm_disp - SGM视差图叠加显示 add wave -noupdate -group sgm_disp ${DUT}/u_sgm_disp/* Waveform Display Configuration configure wave -signalnamewidth 1 configure wave -namecolwidth 250 configure wave -valuecolwidth 100 configure wave -justifyvalue left configure wave -timelineunits ns configure wave -griddelta 40 configure wave -timeline 0 Open Waveform Windows view wave view structure view signals update Debug Helpers - Run these in ModelSim console if needed find signals -r ${DUT}/* run 1000 ms参数定义文件define.f这个文件主要是为DDR3行为模型选择对应的参数读者的工程内如果有类似的机制也可在这个文件内添加对应的定义。defineSIMULATION definesg25E defineden4096Mb definex16仿真启动脚本run_behav.bat以上脚本编写完成后我们还需要一个仿真启动脚本来将Tcl脚本注入到Modelsim中执行一个简单的示例如下echo off set bin_pathC:/modeltech64_2020.4/win64 cd G:/FPGA OBJ/Double_camera_system/Behave_simulation if exist work ( echo Found work folder, deleting... rmdir /s /q work echo work folder deleted. ) call %bin_path%/modelsim -do do {run_behav_compile.tcl};do {run_behav_simulate.tcl} -l run_behav_simulate.log if %errorlevel%1 goto END if %errorlevel%0 goto SUCCESS :END exit 1 :SUCCESS exit 0注Modelsim每次仿真前都需要删除之前留下的work文件夹否则可能会让仿真器仿真上一次的代码所以脚本内的删除语句是必要的。