FPGA实现“乒乓操作”

news2025/1/6 5:07:06

一、“乒乓操作”概述

1、结构       

        “乒乓操作”是一种常用于数据流控制的处理技巧,可以实现无缝高速数据流缓存。首先“乒乓操作”这个名字本身就很吸引人,其结构一般是由数据选择器和数据缓冲器构成的,数据缓冲模块可以为任何存储模块,比较常用的存储单元为双口 RAM(DPRAM) 、单口 RAM(SPRAM) 、FIFO等。乒乓ram结构:这种结构是将输入数据流通过输入数据选择单元等时地将数据流分配到两个数据缓冲区。通过两个数据缓冲区的读和写的切换,来实现数据的流水式传输。

2、原理

        乒乓操作原理:就是打乒乓球一样,一个球(数据流),两个拍子(缓存),两个拍子相互击球(轮流读写数据,写1读2,写2读1)这样就可以做到球不停一直移动(数据流不会停,数据无丢失)。 

        其实就是把数据流轮流加载进两个数据缓冲器中,注意这里的数据流始终是要从输入端移动到输出端,只是不同时间选取的路径不同,而不要理解成“乒乓操作”是数据在两端来回传输。换句话说,我们这里的“拍子”是两个缓存器,而不是两端的数据选择器。

3、处理流程

(1)第一个缓冲周期:输入数据流缓存入数据缓冲器ram A

(2)第二个缓冲周期:通过输入数据选择单元的切换,输入数据流缓存入数据缓冲器ram B,同时将ram A缓存的第1个周期数据传给输出数据选择单元

(3)第三个缓冲周期:通过输入数据选择单元的切换,输入数据流缓存如数据缓冲器ram A,同时将ram B缓存的第2个周期数据传给输出数据选择单元

…………

4、特点

        乒乓操作的最大特点是通过“输入数据选择单元”和“输出数据选择单元”按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到“数据流运算处理模块”进行运算与处理。通过乒乓操作实现低速模块处理高速数据的实质是:通过缓存单元实现了数据流的串并转换,并行用 “ 数据缓冲器ram A” 和 “ 数据缓冲器ram B” 处理分流的数据,是面积与速度互换原则的体现。

5、使用案例

        在低速处理高速数据流时,可以使用乒乓操作,举个栗子,10M的数据流,用乒乓操作,分流成两个FIFO,一个FIFO的吞吐速度只有原来的一半5M,就可以满足低速的处理方法,处理高速的数据,处理后在用合并成一个10M的数据流,数据就被不丢失且高速的处理过了,top层看起就是10M的处理速度了。

二、“乒乓操作”的verilog实现

        我们在这里就尝试实现上文提到的“乒乓操作”的例子,10M的数据流,用乒乓操作,分流成两个FIFO,一个FIFO的吞吐速度只有原来的一半5M,就可以满足低速的处理方法,处理高速的数据,处理后在用合并成一个10M的数据流。

1、设计思路

        在进行代码编写之前要做的就是明确接口信号和模块划分,在之前的内容中我们已经明确了“乒乓操作”的结构及具体功能,那首先就可以进行模块的划分:

(1)ramA,ramB:这个独立的两个数据缓冲器,这里考虑使用xilinx提供的IP核实现单口RAM。

(2)ram_ctrl:用作RAM的控制,对应结构中的输入数据选择器和输出数据选择器结构,这里选择把这两个部分写在同一个模块中了。

(3)data_gen:数据生成模块,由于我们这只是一个“乒乓操作”的测试代码,所以需要自己编写数据生成模块,实现数据的产生。

(4)clk_gen:时钟生成模块,生成5M和10M时钟,也是用IP核的方式实现

2、模块设计

2.1 ramA,ramB模块设计

        通过BRAM资源,使用的是简单双口RAM,读写位宽设置为8,深度设置为32.

2.2 clk_gen模块设计

        设计系统输入时钟是100MHZ,输出时钟有2个分别为clk_5M和clk_10M。

2.3 data_gen模块设计

        数据生成模块的设计,这里我们可以设计数据从0开始在10MHZ时钟的驱动下,逐渐加1。

//-----------------------------<数据生成模块>-----------------------------
module data_gen(
    input clk,
    input rst,
    output data_en,
    output reg [7:0] data_gen
    );

    always@(posedge clk or posedge rst)begin
        if(rst)
            data_gen <= 8'b0;
        else if (data_gen == 8'd31)
            data_gen <= 8'b0;
        else 
            data_gen <= data_gen + 1'b1;
    end

    assign data_en = (rst == 1'b1) ? 1'b0 : 1'b1;

endmodule

2.4 ram_ctrl模块设计

module ram_ctrl(
    input clk_5M,
    input clk_10M,
    input rst,
    input [7:0] data_gen,             //数据生成模块生成的数据
    input data_en,                    //数据使能信号,表示数据是否有效
    input [7:0]ram1_data,             //ram1中读出的数据
    input [7:0]ram2_data,             //ram2中读出的数据

    output ram1_ena,
    output ram1_wea,
    output ram1_enb,
    output reg [4:0] ram1_addra,
    output reg [4:0] ram1_addrb,
    output [7:0] ram1_din,

    output ram2_ena,
    output ram2_wea,
    output ram2_enb,
    output reg [4:0] ram2_addra,
    output reg [4:0] ram2_addrb,
    output [7:0] ram2_din,

    output reg [7:0] dout
    );

//----------------------------<状态定义>---------------------------------
    parameter	IDLE  = 4'b0001;                  //初始状态
	parameter	WRAM1 = 4'b0010;                  //写RAM1
    parameter	R1_W2 = 4'b0100;                  //写RAM2,读RAM1
	parameter	W1_R2 = 4'b1000;                  //写RAM1,读RAM2
				
    reg [3:0] state,next_state;                   //状态寄存器

    always@(posedge clk_10M or posedge rst)begin
        if(rst)
            state <= IDLE;
        else 
            state <= next_state;
    end

    always@(*)begin
        case(state)
            IDLE  : next_state = data_en ? WRAM1 : IDLE ;
            WRAM1 : next_state = (ram1_addra == 5'd31) ? R1_W2 : WRAM1 ;
            R1_W2 : next_state = (ram2_addra == 5'd31) ? W1_R2 : R1_W2 ;
            W1_R2 : next_state = (ram1_addra == 5'd31) ? R1_W2 : W1_R2 ;
            default : next_state = IDLE;
        endcase
    end

    assign ram1_ena = data_en;
    assign ram1_enb = data_en;
    assign ram2_ena = data_en;
    assign ram2_enb = data_en;
    assign ram1_wea = (state == WRAM1 || state == W1_R2) ? 1'b1 : 1'b0 ;
    assign ram2_wea = (state == R1_W2) ? 1'b1 : 1'b0 ;

    always@(posedge clk_10M or posedge rst)begin 
        if(rst) begin 
            ram1_addra <= 0;
            ram2_addra <= 0;
        end
        else if (ram1_addra == 'd31 || ram1_addra == 'd31)begin 
            ram1_addra <= 0;
            ram2_addra <= 0;
        end
        else begin 
            case(state)
                WRAM1 : ram1_addra <= ram1_addra + 1'b1;
                R1_W2 : ram2_addra <= ram2_addra + 1'b1;
                W1_R2 : ram1_addra <= ram1_addra + 1'b1;
                default : begin 
                    ram1_addra <= ram1_addra;
                    ram2_addra <= ram2_addra;
                end
            endcase
        end
    end

    always@(posedge clk_10M or posedge rst)begin
        if(rst) begin 
            ram1_addrb <= 0;
            ram2_addrb <= 0;
        end
        else if (ram1_addrb == 'd31 || ram2_addrb == 'd31)begin
            ram1_addrb <= 0;
            ram2_addrb <= 0;
        end
        else begin 
            case(state)
                R1_W2 : ram1_addrb <= ram1_addrb + 1'b1;
                W1_R2 : ram2_addrb <= ram2_addrb + 1'b1;
                default : begin 
                    ram1_addrb <= ram1_addrb;
                    ram2_addrb <= ram2_addrb;
                end
            endcase
        end
    end

    assign ram1_din = (state == WRAM1 || state == W1_R2) ? data_gen : 8'b0;
    assign ram2_din = (state == R1_W2) ? data_gen : 8'b0;

//打一拍来获得正确的输出
    reg [3:0] state_reg;
    always@(posedge clk_10M)begin
        state_reg <= state;
    end

    always@(*)begin 
        case(state_reg)
            R1_W2 : dout = ram1_data;
            W1_R2 : dout = ram2_data;
            default : dout = 8'b0;
        endcase
    end

endmodule

2.5 顶层模块设计

module pingpang(
    input sys_clk,
    input rst,
    output [7:0] dout
    );

    wire clk_5M,clk_10M;
    wire data_en;
    wire [7:0] data_gen;
    wire ram1_ena,ram1_enb,ram2_ena,ram2_enb;
    wire ram1_wea,ram2_wea;
    wire [4:0] ram1_addra,ram2_addra;
    wire [4:0] ram1_addrb,ram2_addrb;
    wire [7:0] ram1_data,ram2_data;
    wire [7:0] ram1_din,ram2_din;

    clk_div clk_div_u1(
        .clk_5M      (   clk_5M    ),
        .clk_10M     (   clk_10M   ),
        .reset       (   rst       ),
        .clk_in1     (   sys_clk   )
    );

    data_gen data_gen_u1(
        .clk         (   clk_10M   ),
        .rst         (   rst       ),
        .data_en     (   data_en   ),
        .data_gen    (   data_gen  )
    );

    ram ram1(
        .clka        (   clk_10M    ),    // input wire clka
        .ena         (   ram1_ena   ),    // input wire ena
        .wea         (   ram1_wea   ),    // input wire [0 : 0] wea
        .addra       (   ram1_addra ),    // input wire [4 : 0] addra
        .dina        (   ram1_din   ),    // input wire [7 : 0] dina
        .clkb        (   clk_10M    ),    // input wire clkb
        .enb         (   ram1_enb   ),    // input wire enb
        .addrb       (   ram1_addrb ),    // input wire [4 : 0] addrb
        .doutb       (   ram1_data  )     // output wire [7 : 0] doutb
    );

    ram ram2(
        .clka        (   clk_10M    ),    // input wire clka
        .ena         (   ram2_ena   ),    // input wire ena
        .wea         (   ram2_wea   ),    // input wire [0 : 0] wea
        .addra       (   ram2_addra ),    // input wire [4 : 0] addra
        .dina        (   ram2_din   ),    // input wire [7 : 0] dina
        .clkb        (   clk_10M    ),    // input wire clkb
        .enb         (   ram2_enb   ),    // input wire enb
        .addrb       (   ram2_addrb ),    // input wire [4 : 0] addrb
        .doutb       (   ram2_data  )     // output wire [7 : 0] doutb
    );

    ram_ctrl ram_ctrl_u1(
        .clk_5M      (   clk_5M     ),
        .clk_10M     (   clk_10M    ),
        .rst         (   rst        ),
        .data_gen    (   data_gen   ),
        .data_en     (   data_en    ),
        .ram1_data   (   ram1_data  ),
        .ram2_data   (   ram2_data  ),

        .ram1_ena    (   ram1_ena   ),
        .ram1_wea    (   ram1_wea   ),
        .ram1_enb    (   ram1_enb   ),
        .ram1_addra  (   ram1_addra ),
        .ram1_addrb  (   ram1_addrb ),
        .ram1_din    (   ram1_din   ),

        .ram2_ena    (   ram2_ena   ),
        .ram2_wea    (   ram2_wea   ),
        .ram2_enb    (   ram2_enb   ),
        .ram2_addra  (   ram2_addra ),
        .ram2_addrb  (   ram2_addrb ),
        .ram2_din    (   ram2_din   ),

        .dout        (   dout       )
    );


endmodule

3、测试文件

`timescale 1ns / 1ps
module tb_pingpang();
    reg sys_clk;
    reg rst;
    wire [7:0] dout;

    always #5 sys_clk = ~sys_clk;

    initial begin
        sys_clk <= 0;
        rst     <= 0;
    #15 
        rst     <= 1;
    #10 
        rst     <= 0;
    end

    pingpang pingpang_u1(
        .sys_clk    (   sys_clk   ),
        .rst        (   rst       ),
        .dout       (   dout      )
    );

    
endmodule

三、测试结果

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

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

相关文章

【MySQL数据库原理】MySQL Community 8.0界面工具汉化

尝试以下方法来汉化 MySQL Workbench 8.0 的菜单&#xff1a; 1、使用社区翻译版本&#xff1a;有一些热心的社区成员会将 MySQL Workbench 翻译成不同的语言&#xff0c;包括中文。你可以在一些开源或社区网站上寻找这些翻译版本&#xff0c;并按照他们的说明进行安装。 2、…

抖音外卖平台区域代理怎么拿?送上申请教程!

作为占据庞大流量的短视频平台&#xff0c;它的一举一动都格外引人注意。年初&#xff0c;抖音短视频平台开始大力进军本地生活市场&#xff0c;除了开通团购业务外&#xff0c;还准备做短视频外卖&#xff0c;这一举动&#xff0c;立刻掀起了今年火热的创业浪潮。 要知道&…

sql server服务无法启动怎么办?如何正常启动?

sql server软件是一款关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点。并且有些应用软件使用过程中是需要sql server数据库的后台支持的&#xff0c;我们在数据编程操作时经常会使用这款编程软件&#xff0c;在编程时系统有时会提示sql server服务无…

pandas读取一个 文件夹下所有excel文件

我这边有个需求&#xff0c;是要求汇总一个文件夹所有的excel文件&#xff0c; 其中有.xls和 .xlsx文件&#xff0c;同时还excel文件中的数据可能还不一致&#xff0c;会有表头数据不一样需要一起汇总。 首先先遍历子文件夹并读取Excel文件&#xff1a; 使用os库来遍历包含子文…

在自定义数据集上实现OpenAI CLIP

在2021年1月&#xff0c;OpenAI宣布了两个新模型:DALL-E和CLIP&#xff0c;它们都是以某种方式连接文本和图像的多模态模型。CLIP全称是Contrastive Language–Image Pre-training&#xff0c;一种基于对比文本-图像对的预训练方法。为什么要介绍CLIP呢&#xff1f;因为现在大火…

阅读分享--重读Youtube深度学习推荐系统论文,字字珠玑,惊为神文

重读Youtube深度学习推荐系统论文,字字珠玑,惊为神文 https://zhuanlan.zhihu.com/p/52169807 废话不多说,下面就跟大家分享一下两次拜读这篇论文的不同体验和收获。 第一遍读这篇论文的时候,我想所有人都是冲着算法的架构去的,在深度学习推荐系统已经成为各大公司“基本…

Linux下安装部署Tomcat8_图解_保姆级教程

检查工作 tomcat依赖于JDK ,所以我们要先检查 Linux 下是否安装了JDK并配置了环境变量 java -version // 查看JDK版本号如需安装JDK,请参考: Linux下安装JDK,巨详细 1.将安装包放到自己所建立的文件夹之下 我这里采用的软件是Xftp,创建一个文件夹,将Xftp的路径指向刚刚创…

一站式开源持续测试平台 MerterSphere 之测试跟踪操作详解

一、MeterSphere平台介绍 MeterSphere是一站式的开源持续测试平台&#xff0c;遵循 GPL v3 开源许可协议&#xff0c;涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&#xff0c;全面兼容JMeter、Selenium 等主流开源标准&#xff0c;有效助力开发和测试团队充分利用云弹性…

这一次,大模型颠覆广告行业!

本文源自&#xff1a;量子位 百度用大模型重构一切&#xff0c;包括现金奶牛业务。 就在刚刚&#xff0c;百度营销官宣接入文心一言&#xff0c;推出新一代营销平台轻舸&#xff0c;也是全球首个AI Native的营销平台。 这么说吧&#xff0c;是有点颠覆互联网广告投放模式内味…

网络安全(红客)自学

前言 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发. 3.有时多 google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答. 4.遇到实在搞不懂的,可以先放放,以后再来解决. …

电脑文件一团乱?试试这个高效率的管理软件

如果你经常被电脑文件管理搞得一团乱&#xff0c;不妨试试这款高效率的文件管理软件——固乔文件管家。下面是如何使用这款软件进行文件管理的详细步骤。 首先&#xff0c;打开浏览器并搜索“固乔文件管家”这款软件。搜索到后&#xff0c;点击下载并安装。安装完成后&#xff…

2023-亲测有效-git clone失败怎么办?用代理?加git?

git 克隆不下来&#xff0c;超时 用以下格式&#xff1a; git clone https://ghproxy.com/https://github.com/Tencent/ncnn.git 你的网站前面加上 https://ghproxy.com/ 刷的一下就下完了&#xff01;&#xff01;

【股票价格预测】基于改进莱维飞行和混沌映射的粒子群优化BP神经网络预测股票价格研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

面试项目-黑马头条-项目介绍

1 项目介绍 B站视频黑马头条视频学习总结&#xff0c;侵权请联系删除 1.1 项目背景 随着智能手机的普及&#xff0c;人们更加习惯于通过手机来看新闻。由于生活节奏的加快&#xff0c;很多人只能利用碎片时间来获取信息&#xff0c;因此&#xff0c;对于移动资讯客户端的需求也…

薪资17K+需要什么水平?98年测试工程师面试实录…

我的情况 大概介绍一下个人情况&#xff0c;男&#xff0c;本科&#xff0c;三年多测试工作经验&#xff0c;懂python&#xff0c;会写脚本&#xff0c;会selenium&#xff0c;会性能&#xff0c;然而到今天都没有收到一份offer&#xff01;从年后就开始准备简历&#xff0c;年…

从官方文档看Redis

一.核心能力 Redis 1.In-memory data structures 内存数据结构 Redis以"键值对" 的方式来存储数据, 是一种"非关系型数据库" ps: MySQL以"表"的方式来存储数据, 是一种"关系型数据库" 2.Programmability 可编程性 Redis可以用脚本…

Eclipse 安装串口终端工具

Eclipse已集成串口终端显示&#xff0c;只需要我们自己下载安装即可使用。使用SSH连接也差不多。 查看eclipse版本信息 help->About Eclipse 查看version&#xff0c;我的是4.7.3a&#xff0c;记住代号&#xff0c;我的是“Oxygen”,下面有用。 安装eclipse自带的“Termin…

【HELLO NEW WORLD】一封来自开放自动化时代的邀请函

施耐德电气开放自动化平台&#xff0c;迈向开放、高效与韧性、可持续、以人为本的未来工业。 HELLO WORLD 是人类在信息世界开启的第一行 也是我们走进自动化领域迎来的第一句问候 如今 面临向数字化与自动化加速转型的新变局 工业领域迫切地需要一场变革 走向更加高效、…

一名IT重装操作系统后的安装环境历程

1、安装JDK&#xff0c;配置环境变量&#xff08;我一般默认安装&#xff0c;避免后期一些不必要的问题&#xff09;。 下载链接 个人安装包可从下方下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1XIsjCQ2Y-r1m9H8MBlsILQ?pwd6ekm 提取码&#xff1a;6ekm …

惊!这么好用的纯html网页模板可还行?偷偷拿去做作业真是绝绝子!!

在这个万物vue的年代&#xff0c;网页设计越来越框架化。 上网搜个资料学习学习吧&#xff0c;咵咵咵&#xff0c;“游泳健身&#xff0c;vue了解一下” 我只是想简单地学个html&#xff0c;js啊&#xff01;怎么就这么复杂&#xff01; 曾几何时&#xff0c;在网上找个网页…