`timescale 1ns/1ps module Top_Project ( // ========================================== // 1. 系统全局时钟与复位输入 // ========================================== input wire sys_clk_50m, // 开发板上的 50MHz 晶振输入 input wire sys_rst_n, // 开发板上的复位按键 (低电平有效,按下为0) // ========================================== // 2. 外部 SDRAM 物理芯片接口 (匹配 W9825G6JH-6) // ========================================== output wire [12:0] sdram_ADDR, // 13位行地址 / 9位列地址复用 output wire [1:0] sdram_BA, // 2位 Bank 地址 inout wire [15:0] sdram_DQ, // 16位数据总线 (必须是 inout 双向类型) output wire [1:0] sdram_DQM, // 数据掩码 output wire sdram_CASn, // 列选通 (低有效) output wire sdram_CKE, // 时钟使能 output wire sdram_CSn, // 片选 (低有效) output wire sdram_RASn, // 行选通 (低有效) output wire sdram_WEn, // 写使能 (低有效) output wire sdram_CLK, // 输出给 SDRAM 芯片的时钟引脚 (带相位偏移) // ========================================== // 3. 测试状态指示灯 (新增) // ========================================== output wire led_test_pass, // 测试通过指示灯 (建议接绿色 LED) output wire led_test_fail // 测试失败指示灯 (建议接红色 LED) ); // ========================================== // 内部信号声明 // ========================================== wire clk_100m; // PLL 输出的 100MHz (0度相位,给 FPGA 内部逻辑用) wire clk_100m_shift; // PLL 输出的 100MHz (-90度相位,给 SDRAM 芯片用) wire pll_locked; // PLL 锁定指示信号 (高电平表示时钟已稳定输出) wire [15:0] sdram_DQ_read; // 从双向引脚分离出的读数据 wire [15:0] sdram_DQ_write; // 准备输出到双向引脚的写数据 wire [15:0] sdram_DQ_writeEnable; // 控制总线方向的写使能信号 // --- 新增:用户逻辑交互总线连线 --- wire user_cmd_valid; wire user_cmd_ready; wire user_is_write; wire [23:0] user_address; wire [15:0] user_write_data; wire user_rsp_valid; wire [15:0] user_rsp_data; // ========================================== // 模块 1:例化系统 PLL IP 核 (sys_pll) // ========================================== sys_pll u_sys_pll ( // ALTPLL 的 areset 是高电平有效。 // 因为板载按键 sys_rst_n 是低电平有效,所以这里必须加非逻辑 '~' .areset (~sys_rst_n), .inclk0 (sys_clk_50m), .c0 (clk_100m), // 内部 100MHz 时钟 .c1 (clk_100m_shift),// 偏移 100MHz 时钟 .locked (pll_locked) ); // 将带有 -90 度相位偏移的时钟直接连到顶层输出引脚,送给 SDRAM 物理芯片 assign sdram_CLK = clk_100m_shift; // ========================================== // 模块 2:复位信号反相与同步处理 // ========================================== // SpinalHDL 生成的 MyCustomSdramTop 是【高电平复位】的。 // 我们在此生成一个全局的【高电平有效】复位信号 system_reset。 wire system_reset; assign system_reset = (~sys_rst_n) | (~pll_locked); // ========================================== // 模块 3:处理 SDRAM 双向数据总线 (inout 三态门) // ========================================== // 读取通道:直接将外部管脚状态输入给控制器 assign sdram_DQ_read = sdram_DQ; // 写入通道:通过三态门控制 // 当控制器拉高写使能 (16'hFFFF) 时,将写数据推送到总线上。 assign sdram_DQ = (sdram_DQ_writeEnable == 16'hFFFF) ? sdram_DQ_write : 16'hZZZZ; // ========================================== // 模块 4:例化由 SpinalHDL 生成的 SDRAM 控制器 // ========================================== MyCustomSdramTop u_sdram_ctrl ( // --- 时钟与复位 --- .clk (clk_100m), // 接入稳定的 100MHz 内部时钟 .reset (system_reset), // 接入经过处理的高有效复位信号 // --- 连接到底层的 SDRAM 物理接口信号 --- .io_sdram_ADDR (sdram_ADDR), .io_sdram_BA (sdram_BA), .io_sdram_DQ_read (sdram_DQ_read), .io_sdram_DQ_write (sdram_DQ_write), .io_sdram_DQ_writeEnable (sdram_DQ_writeEnable), .io_sdram_DQM (sdram_DQM), .io_sdram_CASn (sdram_CASn), .io_sdram_CKE (sdram_CKE), .io_sdram_CSn (sdram_CSn), .io_sdram_RASn (sdram_RASn), .io_sdram_WEn (sdram_WEn), // --- 连接到测试模块的用户逻辑接口 --- .io_userCmdValid (user_cmd_valid), .io_userCmdReady (user_cmd_ready), .io_userIsWrite (user_is_write), .io_userAddress (user_address), .io_userWriteData (user_write_data), .io_userWriteMask (2'b11), // 写数据掩码 (2'b00表示16位全部写入,不屏蔽) .io_userRspValid (user_rsp_valid), .io_userRspData (user_rsp_data) ); // ========================================== // 模块 5:例化读写校验测试模块 (新增) // ========================================== Sdram_Tester #( .TEST_LENGTH(24'd4096) // 测试的地址长度,这里设置为测试前 4096 个地址 ) u_tester ( .clk (clk_100m), // 注意:Tester 内部是下降沿复位 (低有效),所以需要将高有效的 system_reset 取反 .rst_n (~system_reset), // --- 挂载到 SDRAM 用户总线 --- .cmd_valid (user_cmd_valid), .cmd_ready (user_cmd_ready), .is_write (user_is_write), .address (user_address), .write_data (user_write_data), .rsp_valid (user_rsp_valid), .rsp_data (user_rsp_data), // --- 连接到顶层外部引脚 --- .test_pass (led_test_pass), .test_fail (led_test_fail) ); endmodule