FPGA实现“乒乓操作”

2023-11-01

一、“乒乓操作”概述

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

三、测试结果

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FPGA实现“乒乓操作” 的相关文章

  • 在 C 中操作 80 位数据类型

    我正在用 C 实现一些加密算法 其中涉及 80 位密钥 特定操作涉及将密钥旋转移位 x 个位数 我已经尝试过 long double 类型 如果我没记错的话 它是 80 位 但这不适用于位移运算符 我能想到的唯一替代方案是使用 10 个元素
  • x 和 z 值在 Verilog 中到底代表什么?

    Verilog 标准定义了四种类型的位值 0 1 x 和 z 其中 0 表示低 1 表示高 x 表示未知 z 表示未驱动网络 有几个问题 x 是否意味着我们不知道该值是 0 还是 1 0 或 1 或 z 或者该值是未知的并且可以是 0 1
  • 如何在verilog中逐行读取文本文件?

    我有一个 SREC 文件 它是一个简单的文本文件 我想在 verilog 中逐行读取它 我怎样才能做到这一点 以下读取文件 每个时钟周期 1 行 预期的数据格式是每行一个十进制数 integer data file file handler
  • 如何在RTL中使用时钟门控?

    我正在对一些时钟进行门控latch以及我设计中的逻辑 我在综合和布局布线方面没有太多经验 在 RTL 中实现时钟门控的正确方法是什么 示例1 always comb begin gated clk clk latch update en e
  • Matlab图像处理系列——图像复原之噪声模型仿真

    微信公众号上线 搜索公众号 小灰灰的FPGA 关注可获取相关源码 定期更新有关FPGA的项目以及开源项目源码 包括但不限于各类检测芯片驱动 低速接口驱动 高速接口驱动 数据信号处理 图像处理以及AXI总线等 本节目录 一 图像复原的模型 二
  • Verilog:添加寄存器的各个位(组合逻辑,寄存器宽度可参数化)

    我正在尝试想出一种方法来添加寄存器的各个位 例如 if regA 111000 then regB 3 位的总和regA 1 Verilog或SystemVerilog中是否有可以直接使用的可综合函数 运算符来执行此操作 如果不是 那么问题
  • 将枚举转换为逻辑

    考虑以下模块声明 module DFF d q CLK RESET parameter W 2 input W 1 0 d input CLK input RESET output logic W 1 0 q endmodule 当 d 和
  • 「HDLBits题解」Gates4

    本专栏的目的是分享可以通过HDLBits仿真的Verilog代码 以提供参考 各位可同时参考我的代码和官方题解代码 或许会有所收益 题目链接 Gates4 HDLBits module top module input 3 0 in out
  • verilog 中的“<<”运算符

    我有一个verilog代码 其中有一行如下 parameter ADDR WIDTH 8 parameter RAM DEPTH 1 lt lt ADDR WIDTH 这里将存储什么RAM DEPTH以及什么是 lt lt 操作员在这里做
  • DSCA190V 57310001-PK

    DSCA190V 57310001 PK DSCA190V 57310001 PK 具有两个可编程继电器功能 并安装在坚固的 XP 外壳中 DSCA190V 57310001 PK 即可使用 只需最少的最终用户校准 DSCA190V 573
  • 从测试台访问 uvm_config_db 的最佳方式?

    我想在我的顶级测试平台中创建一个时钟 其周期可以通过测试进行控制 我所做的是将周期设置到 uvm config db 中并将其返回到测试台中 我必须输入 1 以确保构建阶段已完成 否则 get 返回错误值 module testbench
  • 在verilog中将wire值转换为整数

    我想将电线中的数据转换为整数 例如 wire 2 0 w 3 b101 我想要一个将其转换为 5 并将其存储在整数中的方法 我怎样才能以比这更好的方式做到这一点 j 1 for i 0 i lt 2 i i 1 begin a a w i
  • 如何将时钟门映射到技术库单元

    我的设计中有以下时钟门 module my clkgate clko clki ena Clock gating latch triggered on the rising clki edge input clki input ena ou
  • 如何在 Verilog 中综合 While 循环?

    我尝试设计一个 Booth 乘法器 它在所有编译器中运行良好 包括 Modelsim Verilogger Extreme Aldec Active Hdl 和 Xilinx Isim 我知道模拟和综合是两个不同的过程 而且只有少数Veri
  • 从 OpenCV 代码到 FPGA 代码的转换是否比 Matlab 代码更容易? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想做一个关于图像处理的项目 我想知道如果我想在FPGA上实现这个项目 我应该在第一阶段选择Matla
  • 在 Verilog 设计中产生时钟故障

    我正在使用 Verilog 设计芯片 我有一个 3 位计数器 我希望当计数器处于第 8 次循环时 应该有一个时钟故障 之后就可以正常工作了 在 Verilog 设计中产生时钟故障的可能方法是什么 在时钟信号上注入毛刺的一种方法是使用forc
  • 在测试台中显示信号名称/文字

    是否可以在 Verilog 中引用 显示信号的名称 文字 对于在 Verilog 测试台中创建通用信号检查功能来说 这将是一个有用的功能 我知道使用 display 时 m 将打印信号的范围 是否有显示信号名称的等效项 在 Verilog
  • 如何在 Verilog 中推断 Block RAM

    我在一个项目中遇到了一个非常具体的问题 这个问题已经困扰我好几天了 我有以下 RAM 模块的 Verilog 代码 module RAM param clk addr read write clear data in data out pa
  • verilog 中的案例陈述

    我遇到了优先级编码器设计 并找到了一种使用 case 语句来实现它的新方法 唯一令人困惑的是 case语句是否优先考虑case 例子 case 1 b1 A 3 Y lt 4 b1000 A 2 Y lt 4 b0100 A 1 Y lt
  • reg 声明中的位顺序

    如果我需要使用 4 个 8 位数字 我会声明以下 reg reg 7 0 numbers 3 0 我对第一个和第二个声明 7 0 和 3 0 之间的区别感到很困惑 他们应该按什么顺序来 第一个是保留数字的大小 而第二个是保留数字的数量 还是

随机推荐

  • 请使用正确的入口登录面板—解决方案

    错误信息提示 解决方案 一 找回安全登录地址 宝塔登录地址 http 你的服务器ip 8888 但是这种格式是不安全的 目前新安装的宝塔面板默认都开启了安全目录登录 所以如果使用这种不带有8位字符随机码的登录地址就会提示 请使用正确的入口登
  • 【AI】《动手学-深度学习-PyTorch版》笔记(二十一):目标检测

    AI学习目录汇总 1 简述 通过前面的学习 已经了解了图像分类模型的原理及实现 图像分类是假定图像中只有一个目标 算法上是对整个图像做的分类 下面我们来学习 目标检测 即从一张图像中找出需要的目标 并标记出位置 2 边界框 边界框 boun
  • xctf攻防世界—Web新手练习区robots单题思路

    xctf攻防世界 Web新手练习区robots单题思路 邱邱邱自强 前言 随着互联网的发展 互联网界的行为也越来越被重视 那么国际互联网界通行的道德规范是什么呢 它就是Robots协议 一 Robots协议是什么 robots协议也叫rob
  • 点击Path环境变量编辑不展开的问题

    分析 将 MYSQL HOME bin移动到 JAVA HOME bin的上面 点击确定 再次点击Path环境变量就会出现编辑不展开的问题 如图所示 分析原因 因为我把两个 MYSQL HOME bin和 JAVA HOME bin一起放在
  • zip、unzip命令使用

    1 zip压缩命令 1 压缩文件 zip test test txt 将text txt文件压缩到test zip文件中 2 压缩文件夹 r zip r attack zip attack 将当前路径下attack文件夹中的文件进行压缩 压
  • assert在debug 和 release版本中的区别

    转自 https blog csdn net panfengsoftware article details 8910468 debug版本与release的不同点 debug版本中含有调试信息 不会对程序进行优化 assert相应的宏会被
  • python-一些坑点

    一些python使用中遇到的坑点 记录一下 同样的问题也可能只是对当前我的环境下有作用 AttributeError module urllib has no attribute splittype 使用urllib中的一些工具时 提示这个
  • FPGA中task语法基本使用

    1 task定义为任务 完成的是某个具体功能 可以在initial语句和always语句中使用 不过initial语句使用较多 2 task如何使用 1 定义任务 task 任务名 端口及数据类型声明语句 语句1 语句2 语句n endta
  • Qt 3D的研究(三):显示3D模型

    Qt 3D的研究 三 显示3D模型 上一篇文章介绍了如何使用最少的代码创建一个Qt 3D的应用 和大家最初接触的glut一样 对于3D应用来说 需要做的准备工作还真不少 不过呢 Qt 3D把一些窗口相关的琐碎事情解决了 剩下的 该由我们完成
  • 修复nanopi2的SPI无法使用50MHZ传输的问题(S5P4418)

    关于S5P4418使用SPI DMA传输时出现的超时问题 一 问题背景 二 启用SPI的DMA传输 2 1 修改cfg main h 文件 2 2 make menuconfig 配置SPI 2 3 修改SPI主机驱动代码 2 4 增加设备
  • vue cmd 创建新项目在指定文件夹

    1 cmd 进入 后 转到指定目录 cd D 2 创建vue新项目 vue create test
  • 基于minikube搭建的SpringBoot实战

    现在比较多的互联网公司都在尝试将微服务迁到云上 这样的能够通过一些成熟的云容器管理平台更为方便地管理微服务集群 从而提高微服务的稳定性 同时也能较好地提升团队开发效率 但是迁云存在一定的技术难点 今天这篇文章主要介绍如何从0开始搭建一套基于
  • 我的世界服务器修改空岛范围,我的世界空岛指令权限大全

    发布时间 2016 08 07 我的世界ess指令是什么 我的世界ess指令在ess插件运行中十分重要的一部分 那么今天小编就为大家带来了我的世界ess指令用法大全 一起看看吧 我的世界ess指令 Essentials插件 用户组权限管理插
  • OTA:目标检测的最优运输分配

    引言 该论文主要是关于目标检测中的标签分配问题 作者创新性地从全局的角度重新审视了该问题 并提出将标签分配问题看成是一个最优运输问题 要知道最优传输问题是当前最优化理论和GAN理论研究领域中的一个很火的研究课题 论文的实验效果俱佳 而且作者
  • redis系列,redis是如何执行命令(一)

    文章目录 前言 一 从io读取数据 二 解析buf数据 三 解析命令流程 总结 前言 上篇文章介绍了sds的结构 和sds的使用方法 这章我们在回到读取io数据的地方来看 redis是如何从io 读取数据最后转化成执行命令的过程 本篇文章需
  • 计量经济学及Stata应用 第三章习题

    3 1 对于随机变量X 证明Var X E x2 E X 2 3 2对于随机变量X与Y 证明Cov X Y E XY E X E Y 3 3对于随机变量X Y Z 证明Cov X Y Z Cov X Y Cov X Z 3 4 二维随机向量
  • MySQL----MySQL数据库出现Lost connection to MySQL server during query错误的解决办法

    原文链接 MySQL MySQL数据库出现Lost connection to MySQL server during query错误的解决办法 问题描述 Mysql数据库在查询数据库的时候回报出了如下异常 Lost connection
  • Winsock 2 I/O Methods 5 Part 11

    Winsock 2 I O Methods 5 Part 11 What do we have in this chapter 5 part 11 Testing the Client server Program Completion P
  • 中国医疗信息化行业发展规模及投资方向研究报告2021~2027年

    第1章 中国医疗信息化行业发展背景1 1 医疗信息化行业发展背景及意义 1 1 1 医疗信息化行业定义 1 1 2 医疗信息化行业发展背景 1 信息技术促进医疗行业变革 2 医疗卫生改革推动医疗信息化行业发展 3 我国医疗信息化处于临床信息
  • FPGA实现“乒乓操作”

    一 乒乓操作 概述 1 结构 乒乓操作 是一种常用于数据流控制的处理技巧 可以实现无缝高速数据流缓存 首先 乒乓操作 这个名字本身就很吸引人 其结构一般是由数据选择器和数据缓冲器构成的 数据缓冲模块可以为任何存储模块 比较常用的存储单元为双