P2P接口串行FIR设计

设计目标

设计一个仅使用一个乘法器单元的参数化串行FIR,要求:

  • FIR参数可配置
  • 具有双向P2P握手协议,可嵌入P2P流水线中
  • 当流水线后续被阻塞时,要求完成当前运算再进入等待状态

结构框图

structure.png

整体结构如上图所示,共分为4个模块:

  • P2P输入模块:输入模块,接收P2P握手信号,将数据传递给FIR滤波器并控制整个系统运行,为控制流起点
  • FIR滤波器:功能模块,完成FIR滤波运算
  • P2P输出端口:将功能模块的输出通过P2P握手方式发送给P2P转发模块
  • P2P转发模块:隔离FIR滤波器和后向模块,使当后向模块阻塞时FIR滤波器仍能完成当前运算且不丢失数据

参数说明

参数

默认值

功能

ADDR_WIDTH

3

配置地址位宽,要求为最小为ceil(log2(COM_NUM))

DATA_WIDTH

8

输入数据位宽

COM_NUM

6

FIR级数

端口列表

系统端口

名称

类型

位宽

功能

clk

input

1

系统时钟

rst_n

input

1

系统复位信号

配置端口

名称

类型

位宽

功能

cfg_addr

input

ADDR_WIDTH

配置数据的地址

cfg_data

input

DATA_WIDTH

配置数据

cfg_valid

input

1

配置有效信号,高有效

输入端口

名称

类型

位宽

功能

din_data

input

DATA_WIDTH

输入数据

din_valid

input

1

输入有效信号

din_busy

output

1

输入忙信号

输出端口

名称

类型

位宽

功能

dout_data

output

DATA_WIDTH

输出数据

dout_valid

output

1

输出有效信号

dout_busy

input

1

输出忙信号

设计实现

配置接口

配置接口使用寄存器组实现,掉电丢失,因此每次使用之前需要进行配置FIR参数,配置接口时序如下所示:

config_timing.png

配置地址,配置数据和配置有效信号同时有效即完成了一个位置的数据写入,要求配置地址的最大值小于COM_NUM,以防止配置数据超出限制地址。该部分的代码实现如下所示:

integer i;
reg [DATA_WIDTH-1:0]fir_params[COM_NUM-1:0];
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        for (i = 0; i < COM_NUM; i = i + 1) begin
            fir_params[i] <= 'b0;
        end
    end else begin
        if(cfg_valid && (cfg_addr < COM_NUM)) begin
            fir_params[cfg_addr] <= cfg_data;
        end
    end
end

在复位信号有效时,所有位置的数据被清零,因此每次复位后需要重新写入配置数据。当配置有效信号有效且写入数据地址处于有效区间时,配置数据被写入对应位置。

P2P输入接口

P2P输入接口是控制流和数据流的起点,主要信号为din_valid,din_busy和din_data信号,其中din_busy是唯一的输出信号,该信号有效表示后续处于处理状态,无法接收新的数据,因此该信号使用一个状态机实现:

  • INIT状态:等待状态,复位后处于该状态,当din_valid信号有效时,转移到WORK状态。
  • WORK状态:工作状态,表示后续处于工作状态,当当前运算结果已经成功传递给后续模块时,转移到INIT状态,该状态下din_busy信号为高,否则din_busy信号为低。

相关代码如下:

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        din_busy <= 'b0;
    end else begin
        case (din_busy)
            1'b0:begin
                if(din_valid) begin
                    din_busy <= 1'b1;
                end else begin
                    din_busy <= 1'b0;
                end
            end
            1'b1:begin
                if((!inter_fir_busy) && inter_fir_valid) begin
                    din_busy <= 'b0;
                end else begin
                    din_busy <= 1'b1;
                end
            end
            default:din_busy <= 'b0;
        endcase
    end
end

由于din_busy信号的行为与状态高度契合,因此直接使用din_busy作为状态变量,该信号为0时,表示该接口准备就绪,可以接受数据输入,当有信号输入(din_valid==1)时,跳转到1;当该信号为1时,表示后续正在处理或被阻塞,当输出数据已经被传输给后端((!inter_fir_busy) && inter_fir_valid,其中inter_fir_valid和inter_fir_busy分别是FIR和转发模块之间的有效和忙信号)时,可以接收下一个输入,跳转到0。

FIR滤波器

该部分使用一个乘法器构成串行FIR滤波器,结构图如下:

fir_structure.png

计数器

自增计数器在FIR中充当控制器使用,其产生的结果用于选择参数,选择输入和决定累加寄存器的工作模式,代码如下:

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        count <= 'b0;
    end else if(din_busy) begin
        if(count == COM_NUM) begin
            count <= count;
        end else begin
            count <= count + 1'b1;
        end
    end else begin
        count <= 'b0;
    end
end

该部分为一个0~COM_NUM的计数器,当din_busy==1时自增(工作状态自增)。其中0~COM_NUM-1为运算流程,count==COM_NUM标记为运算部分终止,当达到该条件时,count将卡死并不再递增,直到该次运算结束清零(din_busy==0)时。

移位寄存器

移位寄存器用于存储数据,共COM_NUM级,要求从P2P接口输入一个数据时整体后移一位,同时抛弃最后一个数据,代码如下:

reg [DATA_WIDTH-1:0]fir_data[COM_NUM-1:0];
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        for (i = 0; i < COM_NUM; i = i + 1) begin
            fir_data[i] <= 'b0;
        end
    end else if(din_valid && (!din_busy)) begin
        for (i = COM_NUM-1; i > 0; i = i - 1) begin
            fir_data[i] = fir_data[i-1];
        end
        fir_data[0] <= din_data;
    end
end

复位时,所有寄存器清零,当P2P输入接口有数据输入(din_valid && (!din_busy))时,将数据整体后移一位并将输入数据放在最前一个寄存器中。注意寄存器的移位操作与din_busy置1同时完成,因此当din_busy==1时,移位寄存器数据稳定。

乘法器

乘法器根据count选择移位寄存器和参数,并完成单个乘法运算,代码如下所示:

reg [2*DATA_WIDTH-1:0]one_cycle_result;
reg finish_count_buf;
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        one_cycle_result <= 'b0;
        finish_count_buf <= 'b0;
    end else begin
        if(count <= COM_NUM-1) begin
            one_cycle_result <= (2*DATA_WIDTH)'(fir_data[COM_NUM - count - 1]) * fir_params[COM_NUM -  count - 1];
        end else begin
            one_cycle_result <= 'b0;
        end
        finish_count_buf <= finish_count;
    end
end

当运算进行时,根据count选择数据相乘,当是数据超出限制,即已经完成后,持续输出0防止后级的累加器累加错误的数据,注意(2*DATA_WIDTH)'(fir_data[COM_NUM - count - 1])为了显式表示输出数据的位数。同时该部分还缓存了运算结束信号finish_count,缓存后的信号finish_count_buf与乘法器结果one_cycle_result同步,即让控制流数据和数据流通过相同的延迟路径,以达到同步和简化功能。

累加器

累加器用于累加乘法器的输出,要求运算过程中来累加,输出过程稳定,非计算过程清零以实现下一次运算,代码如下:

reg [2*DATA_WIDTH-1:0]inter_fir_dout;
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        inter_fir_dout <= 'b0;
    end else if(din_busy) begin
        if(inter_fir_valid) begin
            inter_fir_dout <= inter_fir_dout;
        end else begin
            inter_fir_dout <= inter_fir_dout + one_cycle_result;
        end
    end else begin
        inter_fir_dout <= 'b0;
    end
end

除了工作状态以外(din_busy==1),其他状态该累加器均清0。工作状态下,当运算完成向下一级输出时(下一级为转发模块),数据保持稳定,否则累加乘法器的输入。

P2P输出接口

P2P输出接口用于FIR滤波器向转发模块发送数据,需要控制的为有效信号valid(data信号即为累加器输出),该部分代码如下:

always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        inter_fir_valid <= 'b0;
    end else if(finish_count_buf)begin
        inter_fir_valid <= 1'b1;
    end else if(!inter_fir_busy) begin
        inter_fir_valid <= 'b0;
    end
end

当结束信号有效(finish_count_buf==1)时,valid信号拉高,注意该信号相比finish_count_buf落后一个周期,与累加器输出同步(落后乘法器输出一个时钟周期),当传输完成时(inter_fir_busy==0 且finish_count_buf !=0 )时清零。

转发模块

该转发模块用于隔离FIR滤波器和后级输出:

  • 若没有该模块,当后级忙时,FIR将被卡在当前运算处无法接受下一个输入,知道后级可接受信号
  • 若添加该模块,当后级忙时,FIR仍然可以完成当前运算并开始下一次计算,因此留给后一级的相应时间延长。

该部分为一个简单的P2P直连接口,没有内部组合逻辑,其对FIR的接口部分需要控制busy信号,代码如下所示:

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        inter_fir_busy <= 'b0;
    end else begin
        case (inter_fir_busy)
            1'b0:begin
                if(inter_fir_valid) begin
                    inter_fir_busy <= 1'b1;
                end else begin
                    inter_fir_busy <= 1'b0;
                end
            end
            1'b1:begin
                if(dout_valid && !dout_busy) begin
                    inter_fir_busy <= 1'b0;
                end else begin
                    inter_fir_busy <= 1'b1;
                end
            end
            default :inter_fir_busy <= 'b0;
        endcase
    end
end

该部分与P2P输入接口完全相同,具体说明可以参见【P2P输入接口】,其对下一级的输出部分代码如下所示:

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        dout_data <= 'b0;
    end else if(inter_fir_valid && !inter_fir_busy) begin
        dout_data <= inter_fir_dout;
    end
end

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        dout_valid <= 'b0;
    end else if(inter_fir_valid && !inter_fir_busy) begin
        dout_valid <= 1'b1;
    end else if(dout_valid && !dout_busy) begin
        dout_valid <= 'b0;
    end
end

dout_data为输出数据,当与FIR传输信号握手成功(inter_fir_valid && !inter_fir_busy)时更新,否则一直保持。dout_valid为对下一级的输出valid信号,当有新数据传入时置1,当传输成功时置0,表示当前传输结束。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PaddlePaddle

【进阶篇】在不同的集群框架下完成分布式训练

编写|PaddlePaddle 排版|wangp 本文将介绍如何使用PaddlePaddle在不同的集群框架下完成分布式训练。分布式训练架构如下图所示: ? A...

43150
来自专栏大数据文摘

TensorFlow 1.2.0新版本发布:新增Intel MKL优化深度学习基元

48540
来自专栏云计算教程系列

如何在Ubuntu 14.04第1部分上查询Prometheus

Prometheus是一个开源监控系统和时间序列数据库。Prometheus最重要的一个方面是它的多维数据模型以及随附的查询语言。此查询语言允许您对维度数据进行...

13200
来自专栏AI研习社

将 TensorFlow 训练好的模型迁移到 Android APP上(TensorFlowLite)

最近在做一个数字手势识别的APP(关于这个项目,我会再写一篇博客仔细介绍,博客地址:一步步做一个数字手势识别APP,源代码已经开源在github上,地址:Chi...

46130
来自专栏CSDN技术头条

面向机器智能的TensorFlow实践:产品环境中模型的部署

在了解如何利用TesnsorFlow构建和训练各种模型——从基本的机器学习模型到复杂的深度学习网络后,我们就要考虑如何将训练好的模型投入于产品,以使其能够为其他...

49260
来自专栏lgp20151222

bootstrap的模态框

12610
来自专栏梦里茶室

TensorFlow深度学习笔记 Tensorboard入门

Github工程地址:https://github.com/ahangchen/GDLnotes 官方教程:https://www.tensorflow.org...

22080
来自专栏Deep learning进阶路

caffe随记(八)---使用caffe训练FCN的pascalcontext-fcn32s模型(pascal-context数据集)

本篇讨论利用caffe进行FCN训练(采用的是pascal-context数据集) 1、下载FCN的框架 https://github.com/shelham...

52800
来自专栏云霄雨霁

计算题总结

39100
来自专栏人工智能LeadAI

用python编写一个本地论文管理器

介绍和引入 最近初学NLP相关的深度学习,下了很多论文,数量一多,发现论文管理是个问题。 首先论文数目一多,必须要按类别放到子文件夹下。但是某一篇论文,往往有...

39890

扫码关注云+社区

领取腾讯云代金券