UDP--DDR--SFP,FPGA实现之ddr读写控制模块

news2025/5/18 13:51:12

DDR读写控制模块实现介绍

由于该模块接口数量较多,为了详细说明模块实现,采用文字流程进行介绍

  • 上级模块传输数据到来
  • 捕捉数据有效上升沿
  • 传输写指令,写有效,写指令成功被下一级模块缓存,进行写地址+一次读写长度(bit为单位)
  • 传递写数据,每次写完1k数据,进行size+1,记录最终的KB大小(以1K为单位),提供给读取内存模块,指示可读取范围
  • 接收到地址清除信号,地址回读信号,将读写输出地址置0,地址清除信号代表上位机要重写文件,将地址置0,即可表示擦除DDR数据,即将有效地址清零,标记为无效地址块;地址回读信号,即读取内存模块成功完成一次文件读取,回到0地址,进行第二次数据读取。所谓0地址,可根据所选用的DDR起始地址设定,不一定全为0,只要是数据块的起始写入位置即可,要求固定。
  • 接收到读取指令,转换为op指令,进行数据读取

DDR读写控制模块代码编写

通过文字流程描述,逻辑梳理可以将代码清晰的进行写出

module ddr_rw_control(
    input               i_ui_clk        ,
    input               i_ui_rst        ,
    /*ASYNC_BUF_DDR*/
    input   [31:0]      i_send_data     ,
    input               i_send_valid    ,
    input               i_read_cmd      ,
    input               i_raddr_clear   ,
    input               i_read_back     ,
    output  [15:0]      o_store_size    ,
    /*op-->axi*/
    output  [1 :0]      o_op_cmd        ,
    output  [29:0]      o_op_waddr      ,
    output  [29:0]      o_op_raddr      ,
    output              o_op_valid      ,
    input               i_op_ready      ,
    output  [31:0]      o_write_data    ,
    output              o_write_valid   ,
    input   [31:0]      i_read_data     ,
    input               i_read_valid    
    );

localparam      P_ADDR = 4*256          ;

reg     [31:0]          ri_send_data    ;
reg                     ri_send_valid   ;
reg     [31:0]          ri_send_data_1d ;
reg                     ri_send_valid_1d;
reg                     ri_read_cmd     ;
reg     [1 :0]          ro_op_cmd       ;
reg     [29:0]          ro_op_waddr     ;
reg     [29:0]          ro_op_raddr     ;
reg                     ro_op_valid     ;
reg                     ri_op_ready     ;
reg     [31:0]          ri_read_data    ;
reg                     ri_read_valid   ;
reg         [15:0]      ro_store_size   ;
(*mark_debug = "true"*)wire                    w_send_pos      ;
(*mark_debug = "true"*)wire                    w_read_pos      ;
wire                    w_send_neg      ;
assign  w_send_pos      = ~ri_send_valid & i_send_valid ;
assign  w_send_neg      = ~ri_send_valid & ri_send_valid_1d;
assign  w_read_pos      = ~ri_read_cmd   & i_read_cmd   ;
assign  o_op_cmd        = ro_op_cmd     ;
assign  o_op_waddr      = ro_op_waddr   ;
assign  o_op_raddr      = ro_op_raddr   ;
assign  o_op_valid      = ro_op_valid   ;
assign  o_write_data    = ri_send_data  ;
assign  o_write_valid   = ri_send_valid ;
assign  o_store_size    = ro_store_size ;

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ri_op_ready <= 1'b0;
    else
        ri_op_ready <= i_op_ready;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst) begin
        ri_send_data     <= 32'd0;
        ri_send_valid    <= 1'b0 ;
        ri_send_data_1d  <= 32'd0;
        ri_send_valid_1d <= 1'b0 ;
    end
    else begin
        ri_send_data     <= i_send_data  ;
        ri_send_valid    <= i_send_valid ;
        ri_send_data_1d  <= ri_send_data ;
        ri_send_valid_1d <= ri_send_valid;
    end
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ro_store_size <= 16'h0;
    else if(i_raddr_clear)
        ro_store_size <= 16'h0;
    else if(w_send_neg)
        ro_store_size <= ro_store_size + 1'b1;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ri_read_cmd <= 1'b0;
    else
        ri_read_cmd <= i_read_cmd;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ro_op_cmd <= 2'b00;
    else if(w_send_pos)
        ro_op_cmd <= 2'b01;
    else if(w_read_pos)
        ro_op_cmd <= 2'b10;
    else if(ro_op_valid && i_op_ready)
        ro_op_cmd <= 2'b00;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ro_op_valid <= 1'b0;
    else if(ro_op_valid && i_op_ready)
        ro_op_valid <= 1'b0;
    else if(~ro_op_valid && w_send_pos)
        ro_op_valid <= 1'b1;
    else if(~ro_op_valid && w_read_pos)
        ro_op_valid <= 1'b1;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ro_op_waddr <= 'd0;
    else if(i_raddr_clear || i_read_back)
        ro_op_waddr <= 'd0;
    else if(ro_op_valid && i_op_ready && ro_op_cmd == 2'b01)
        ro_op_waddr <= ro_op_waddr + P_ADDR;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst)
        ro_op_raddr <= 'd0;
    else if(i_raddr_clear || i_read_back)
        ro_op_raddr <= 'd0;
    else if(ro_op_valid && i_op_ready && ro_op_cmd == 2'b10)
        ro_op_raddr <= ro_op_raddr + P_ADDR;
end

always @(posedge i_ui_clk,posedge i_ui_rst) begin
    if(i_ui_rst) begin
        ri_read_data  <= 32'd0;
        ri_read_valid <= 1'b0 ;
    end
    else begin
        ri_read_data  <= i_read_data ;
        ri_read_valid <= i_read_valid;
    end
end

endmodule

接下来对代码中几个比较关键的讲解
首先是ro_op_waddr 和ro_op_raddr ,为什么其要在op总线信号握手成功后,再进行地址递增操作
这是参考了AXIS的涉及,ro_op_valid信号指示读写地址、指令等信息的有效,i_op_ready指示下一级模块成功接收到这些信息,如果地址与op_valid同步处理,会造成基础地址,不会被写入数据,直接是基础地址+2000了,所以要在每次握手成功后,进行地址的加操作
cmd为1表示写操作,cmd为2表示读操作
同样对于ro_store_size 这个信号,其表示缓存了多少KB,则在每次传入有效信号的下降沿进行+1,则可以表示写入了DDR多少KB,若是在上升沿操作,其理论上也不会出现问题。
关于代码的说明便介绍到这里,接下来进行仿真实验。

DDR读写控制模块仿真

即在之前章节的基础上,添加对该模块的例化即可,另外,考虑到内存块读取模块尚未实现,对于输入的read_cmd指令,笔者便先通过仿真给信号实现。完整的tb文件代码如下

module tb_module(

    );

reg                 i_udp_clk       = 1'b0;
reg                 i_udp_rst       = 1'b0;
reg                 i_ui_clk        = 1'b0;
reg                 i_ui_rst        = 1'b0;
reg                 i_sfp_clk       = 1'b0;
reg                 i_sfp_rst       = 1'b0;
reg     [7 :0]      i_udp_data      = 8'd0;
reg                 i_udp_valid     = 1'd0;
reg                 i_rcmd          = 1'b0;
wire    [7 :0]      w_store_udp_data    ;
wire                w_store_udp_valid   ;
wire                w_store_done        ;
wire                w_raddr_clear       ;
wire                w_sync_clear        ;
wire                w_read_cmd          ;
reg     [31:0]      r_read_data     = 32'd0;
reg                 r_read_valid    = 1'd0 ;
wire    [31:0]      w_send_data         ;
wire                w_send_valid        ;
wire    [1 :0]      w_op_cmd            ;
wire    [29:0]      w_op_waddr          ;
wire    [29:0]      w_op_raddr          ;
wire                w_op_valid          ;
wire    [31:0]      w_write_data        ;
wire                w_write_valid       ;
wire    [15:0]      w_store_size        ;

integer i = 0;
integer j = 0;
always #4   i_udp_clk = ~i_udp_clk;
always #2.5 i_ui_clk  = ~i_ui_clk ;
always #2   i_sfp_clk = ~i_sfp_clk;
initial begin
    i_udp_rst = 1;
    i_sfp_rst = 1;
    i_ui_rst  = 1;
    #100
    @(i_sfp_clk) begin
        i_udp_rst <= 1'b0;
        i_sfp_rst <= 1'b0;
        i_ui_rst  <= 1'b0;
    end
    #100
    /*传输擦除指令*/
    @(posedge i_udp_clk)
        udp_cmd(64'HD5D5D5D5_FCFCFCFC);
    /*传输256KB*/
    @(posedge i_udp_clk)
    for(i = 0;i < 256; i = i + 1) begin
        @(posedge i_udp_clk)
            udp_send(1024);
        #500
        @(posedge i_udp_clk);
    end
    /*传输完成指令*/
    @(posedge i_udp_clk)
        udp_cmd(64'HA5A5A5A5_BCBCBCBC);
    #1000
    @(posedge i_udp_clk)
        i_rcmd <= 1'b1;
    @(posedge i_udp_clk)
        i_rcmd <= 1'b0;
    #100
    @(posedge i_udp_clk)
        udp_cmd(64'HD5D5D5D5_FCFCFCFC);   
end

/*指令监测,输出监测后数据*/
udp_cmd_check udp_cmd_check_u0(
    .i_clk              (i_udp_clk          ),
    .i_rst              (i_udp_rst          ),
    .i_udp_data         (i_udp_data         ),
    .i_udp_valid        (i_udp_valid        ),
    .o_udp_data         (w_store_udp_data   ),
    .o_udp_valid        (w_store_udp_valid  ),
    .o_store_done       (w_store_done       ),
    .o_raddr_clear      (w_raddr_clear      )
    );

/*跨时钟域处理,1Byte-->4Bytes,udp-->ddr*/
ASYNC_BUF_DDR ASYNC_BUF_DDR_U0(
    .i_udp_clk          (i_udp_clk          ),
    .i_udp_rst          (i_udp_rst          ),
    .i_ui_clk           (i_ui_clk           ),
    .i_ui_rst           (i_ui_rst           ),
    .i_udp_data         (w_store_udp_data   ),
    .i_udp_valid        (w_store_udp_valid  ),
    .o_send_data        (w_send_data        ),
    .o_send_valid       (w_send_valid       )
    );

/*读取地址清除信号跨时钟*/
sync_s2f sync_s2f_u0(
    .i_clk_slow	        (i_udp_clk          ),
    .i_signal	        (w_raddr_clear      ),
    .i_clk_fast 	    (i_ui_clk           ),
    .o_sync		        (w_sync_clear       )
);

ddr_rw_control ddr_rw_control_u0(
    .i_ui_clk           (i_ui_clk           ),
    .i_ui_rst           (i_ui_rst           ),
    .i_send_data        (w_send_data        ),
    .i_send_valid       (w_send_valid       ),
    .i_read_cmd         (i_rcmd             ),
    .i_raddr_clear      (w_sync_clear       ),
    .i_read_back        (1'b0               ),
    .o_store_size       (w_store_size       ),
    .o_op_cmd           (w_op_cmd           ),
    .o_op_waddr         (w_op_waddr         ),
    .o_op_raddr         (w_op_raddr         ),
    .o_op_valid         (w_op_valid         ),
    .i_op_ready         (1'b1               ),
    .o_write_data       (w_write_data       ),
    .o_write_valid      (w_write_valid      ),
    .i_read_data        (32'd0              ),
    .i_read_valid       (1'b0               )
    );

task udp_send(input    [15:0]  byte_len);begin : data
    integer i;
    i_udp_data   = 8'd0;
    i_udp_valid  = 1'd0;
    @(posedge i_udp_clk);
    for(i = 0;i < byte_len ;i = i + 1)
    begin
        i_udp_data  <= i_udp_data + 1'b1;
        i_udp_valid <= 1'b1;
        @(posedge i_udp_clk);
    end
    i_udp_data   <= 8'd0;
    i_udp_valid  <= 1'd0;
end
endtask

task udp_cmd(input    [63:0]  i_cmd);begin : cmd
    integer i;
    i_udp_data   = 8'd0;
    i_udp_valid  = 1'd0;
    @(posedge i_udp_clk);
    for(i = 0;i < 8 ;i = i + 1)
    begin
        i_udp_data  <= i_cmd[63:56];
        i_cmd <= {i_cmd[55:0],8'h0};
        i_udp_valid <= 1'b1;
        @(posedge i_udp_clk);
    end
    i_udp_data   <= 8'd0;
    i_udp_valid  <= 1'd0;
end
endtask
endmodule

模拟仿真的流程与前两节类似,只是在udp文件传输完成后,加入了一次读操作,观察指令输出情况,以及一次擦除指令下发,因为初始时地址为0,无法观察擦除命令是否成功执行,需要注意,此时还没有链接DDR_AXI模块,所以不会有数据读出,只是检验指令输出过程是否正确。
由下图所示,为第一帧数据传输进来,op指令进行相应转译,cmd为1,写地址为0,握手成功后,写地址+0x2000,
在这里插入图片描述
观察最后一帧数据传输完成,size记录为256KB,记录正确,waddr的下一次写地址为30’h0x00200000,即256X1024X8,正确,注意使用AXI控制器读写地址时,单位为bit。
在这里插入图片描述
观察read_cmd以及DDR擦除指令传输情况,若下图所示,成功进行了一次读取指令译码,以及当clear指令拉高时,可以看出size以及操作地址等变量成功清零。
在这里插入图片描述
经过上述仿真,可以看出该模块正常工作,对于该模块的编写是较为简单的,在开发中,是十分常用的模块,有必要进行掌握。关于本节代码的问题,以及优化意见,欢迎大家在评论区指出,如果想要对应工程进行学习,欢迎大家私信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

云计算与大数据进阶 | 26、解锁云架构核心:深度解析可扩展数据库的5大策略与挑战(上)

在云应用/服务的 5 层架构里&#xff0c;数据库服务层稳坐第 4 把交椅&#xff0c;堪称其中的 “硬核担当”。它的复杂程度常常让人望而生畏&#xff0c;不少人都将它视为整个架构中的 “终极挑战”。 不过&#xff0c;也有人觉得可扩展存储系统才是最难啃的 “硬骨头”&#…

AI Agent | Coze 插件使用指南:从功能解析到实操步骤

一、前言 在人工智能技术飞速发展的今天&#xff0c;低代码开发模式正成为构建智能应用的主流趋势。对于希望快速搭建 AI Bot 的开发者和业务人员而言&#xff0c;coze作为一款强大的低代码 AI 开发平台&#xff0c;凭借其高度模块化的插件体系脱颖而出。这些插件就像搭建智能…

MK米客方德SD NAND:无人机存储的高效解决方案

在无人机技术迅猛发展的当下&#xff0c;飞控系统的数据记录对于飞行性能剖析、故障排查以及飞行安全保障极为关键。以往&#xff0c;SD 卡是飞控 LOG 记录常见的存储介质&#xff0c;但随着技术的革新&#xff0c;新的存储方案不断涌现。本文聚焦于以 ESP32 芯片为主控制器的无…

【vscode】解决vscode无法安装远程服务器插件问题,显示正在安装

文章目录 现状分析采用VSIX离线安装第一步&#xff1a;离线下载插件包第二步&#xff1a;把下载好的插件文件上传到远程服务器上第三步&#xff1a;在windows下打开vscode&#xff0c;并链接远端&#xff0c;进行安装 现状分析 vscode无法远程安装扩展插件&#xff0c;显示正在…

【Spring】Spring的请求处理

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 欢迎评论交流&#xff0c;感谢您的阅读&#x1f604;。 目录 引言HTTP/HTTPS协议Spring Web与Spring Web MVCSpring WebFlux 自定义的TPC/IP协议FTP、S…

粒子群算法(PSO算法)

粒子群算法概述 1.粒子群优化算法&#xff08;Particle Swarm Optimization&#xff0c;简称PSO&#xff09;。粒子群优化算法是在1995年由Kennedy博士和Eberhart博士一起提出的&#xff0c;它源于对鸟群捕食行为的研究。 2.基本核心是利用群体中的个体对信息的共享从而使得整…

LLM智能体新纪元:深入解析MCP与A2A协议,赋能智能自动化协作

LLM智能体&#xff08;LLM agents&#xff09;是能够自主行动以实现特定目标的AI系统。在实际应用中&#xff0c;智能体能够将用户请求拆解为多个步骤&#xff0c;利用知识库或API获取数据&#xff0c;最终整合出答案。这让智能体相比于传统独立聊天机器人拥有更强大的能力——…

SAP学习笔记 - 开发豆知识01 - CDS SDK命令出乱码 (cds init CAP-Test03 --add java)

1&#xff0c;现象 安装完VSCode以及各种需要的插件&#xff08;比如SAP CDS Language Support&#xff09;&#xff0c;就可以做CAP开发。 用这个命令创建Project&#xff1a;cds init CAP-Test03 --add java 然后出来一个乱码错误 adding java The derived package name c…

(C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)(网页版预告)(html)(js)(json)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c json_export.h json_export.c tasks.json idex.html script.js 相关步骤&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第五步…

进阶-数据结构部分:​​​​​​​2、常用排序算法

飞书文档https://x509p6c8to.feishu.cn/wiki/FfpIwIPtviMMb4kAn3Sc40ABnUh 常用排序算法 这几种算法都是常见的排序算法&#xff0c;它们的优劣和适用场景如下&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;简单易懂&#xff0c;时间复杂度较高&…

25、DeepSeek-R1论文笔记

DeepSeek-R1论文笔记 1、研究背景与核心目标2、核心模型与技术路线3、蒸馏技术与小模型优化4、训练过程简介5、COT思维链&#xff08;Chain of Thought&#xff09;6、强化学习算法&#xff08;GRPO&#xff09;7、冷启动**1. 冷启动的目的****2. 冷启动的实现步骤****3. 冷启动…

LeetCode --- 156双周赛

题目列表 3541. 找到频率最高的元音和辅音 3542. 将所有元素变为 0 的最少操作次数 3543. K 条边路径的最大边权和 3544. 子树反转和 一、找到频率最高的元音和辅音 分别统计元音和辅音的出现次数最大值&#xff0c;然后相加即可&#xff0c;代码如下 // C class Solution {…

npm 报错 gyp verb `which` failed Error: not found: python2 解决方案

一、背景 npm 安装依赖报如下错&#xff1a; gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题&#xff0c;其实并不是你python环境问题&#xf…

初识Linux · IP协议· 下

目录 前言&#xff1a; 内网IP和公网IP 内网IP 公网IP 路由 前言&#xff1a; 前文我们介绍了IP协议的协议头&#xff0c;通过源码等方式我们理解了IP协议中的字段&#xff0c;比如8位协议&#xff0c;比如通过环回问题引出的8位最大生存时间&#xff0c;比如8位协议&…

JAVA的常见API文档(上)

游戏打包 注意API文档中的方法不需要记忆&#xff01;&#xff01; 了解之后如果需要可以查询API文档 对Math的方法总结&#xff1a; 运用刚学的Math方法加快代码的运行效率 可以减少循环次数 找规律&#xff1a; 发现因子有规律&#xff1a; 必定一个大于平方根&#xff0c;…

Spark,连接MySQL数据库,添加数据,读取数据

连接数据库 可以看到shell中我们读取出的数据 在IDEA中打代码如果能输出跟shell中一样的结果即证明连接成功 【出错反思】 像我前面出错的原因就是在打代码时将密码输入错误 添加数据 读取数据就是在上面代码中一起展示了&#xff0c;这里我就不单独说了

【EDA软件】【联合Modelsim仿真使用方法】

背景 业界EDA工具仿真功能是必备的&#xff0c;例如Vivado自带仿真工具&#xff0c;且无需联合外部仿真工具&#xff0c;例如MoodelSim。 FUXI工具仿真功能需要联合Modelsim&#xff0c;才能实现仿真功能。 方法一&#xff1a;FUXI联合ModelSim 1 添加testbench文件 新建to…

【离散化 线段树】P3740 [HAOI2014] 贴海报|普及+

本文涉及知识点 C线段树 [HAOI2014] 贴海报 题目描述 Bytetown 城市要进行市长竞选&#xff0c;所有的选民可以畅所欲言地对竞选市长的候选人发表言论。为了统一管理&#xff0c;城市委员会为选民准备了一个张贴海报的 electoral 墙。 张贴规则如下&#xff1a; electoral…

CSP 2024 提高级第一轮(CSP-S 2024)单选题解析

单选题解析 第 1 题 在 Linux 系统中&#xff0c;如果你想显示当前工作目录的路径&#xff0c;应该使用哪个命令&#xff1f;&#xff08;A&#xff09; A. pwd B. cd C. ls D. echo 解析&#xff1a;Linux 系统中&#xff0c;pwd命令可以显示当前工作目录的路径。pwd&#x…

六、绘制图片

文章目录 1.创建一个红色图片2.加载bmp图片3.加载png、jpg图片 前面的几个示例&#xff0c;我们已经展示过如果在Linux系统下使用xlib接口向窗口中绘制文本、线、矩形&#xff1b;并设置文本、线条的颜色。并利用xlib提供的接口结合事件处理机制完成了一个自绘按钮控件功能。有…