本文首发于个人博客
设计基于单口SRAM的转置型FIR,半并行实现,要求满足:
名称 | 默认值 | 说明 |
---|---|---|
PALL_PAM | 4 | 并行阶数 |
PALL_PAM_LOG | 2 | 并行阶数LOG值 |
SERI_PAM | 4 | 串行阶数 |
SERI_PAM_LOG | 2 | 串行阶数LOG值 |
DATA_WIDTH | 16 | 数据位宽 |
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
clk | input | 1 | 系统时钟 |
rst_n | input | 1 | 系统复位信号,低有效 |
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
cfg_valid | input | 1 | 配置有效信号 |
cfg_addr | input | PALL_PAM_LOG*SERI_PAM_LOG | 配置地址 |
cfg_data | input | DATA_WIDTH | 配置数据 |
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
din_valid | input | 1 | 输入有效信号 |
din_busy | output | 1 | 输入忙信号 |
din_data | input | DATA_WIDTH | 输入数据 |
dout_valid | output | 1 | 输出有效信号 |
dout_busy | input | 1 | 输出忙信号 |
dout_data | output | DATA_WIDTH | 输出数据 |
structure.png
该FIR共分为四个部分:
以一个六阶的FIR为例,并行度为2,串行度为3(每个串行处理单元串行处理3个乘加操作),整体有以下数据流:
可以发现,对于:
而言,前一部分的部分和在PE0的第0~2cycle中计算,后一部分的部分和在PE1的3~5cycle中计算,同时,PE0在第3~5个周期中计算
的部分和。因此对于
阶的FIR(并行度为m,串行度为n),每个串行单元负责一个FIR结果的n个乘法的计算。对于第i个串行单元,负责
和对应输入数据的乘法。现在考虑第k个输出
,相关伪代码如下所示:
k_result = 0;
for(int i = 0;i < m;i++) { // 不同PE分时并行实现
float this_result;
for(int j = 0;j < n;j++) { // 串行实现
if(j == 0) {
this_result = x(k+j*m+i) * h((n-j)*m-i) + k_result;
} else {
this_result = x(k+j*m+i) * h((n-j)*m-i) + this_result;
}
}
k_result = this_result;
}
对于第i个PE(PE的标号计算从1开始),在第j个周期(周期标号从0开始),输出的权值为
,每个PE的标号i是固定的,因此ROM对应的地址仅与当前串行周期数有关。对于第z个周期的输入(z计数从0开始,输出
的周期为第0周期),对应的输入数据应为
,因此对于数据RAM取数据的地址除了与周期数z有关外,还与k有关。
输入模块包括输入数据寄存器和数据RAM,需要实现以下功能:
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
clk | input | 1 | 系统时钟 |
rst_n | input | 1 | 系统复位信号,低有效 |
din_valid | input | 1 | 输入P2P接口有效信号 |
din_busy | input | 1 | 输入P2P接口忙信号,控制器生成 |
din_data | input | DATA_WIDTH | 输入P2P数据信号 |
control_ram_addr | input | SERL_PRAM_LOG+PALL_PRAM_LOG | 读写数据ram的地址 |
control_ram_write | input | 1 | 写ram请求信号 |
unit_din | output | DATA_WIDTH | ram输出数据 |
din_structure.png
该部分设计如上图,共两个部分,如下所示:
该部分不包括控制流部分,仅实现输入的数据流,控制流由控制器生成。输出端口的数据来源为RAM或输入寄存器。当执行RAM写入操作时,内部输出数据来源于输入寄存器,否则来源于数据RAM。
串行处理单元,实现串并行处理的串行部分,多个串行处理单元并行实现并行部分,单个单元的需求为:
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
clk | input | 1 | 系统时钟 |
rst_n | input | 1 | 系统复位信号,低有效 |
cfg_valid | input | 1 | 配置有效信号,高有效 |
cfg_addr | input | PALL_PAM_LOG+SERI_PAM_LOG | 配置目标地址 |
cfg_data | input | DATA_WIDTH | 配置数据 |
unit_din | input | DATA_WIDTH | 乘法操作数,来自输入模块 |
unit_partsum_din | input | DATA_WIDTH*2 | 部分和累加操作数,来自上一个串行单元 |
unit_partsum_dout | output | DATA_WIDTH*2 | 部分和,输出到下一个串行单元 |
control_rom_addr | input | SERI_PAM_LOG | 参数ROM地址,产生ROM的乘法操作数 |
control_mux_controller | input | 2 | 控制信号,控制累加器功能 |
serial_structure.png
串行处理单元如上图所示,该部分仅包括数据流,控制流由控制器统一产生。分为以下几个部分:
cfg_*
接口进行参数配置。非配置时根据控制器提供的地址输出乘法操作数unit_din
进行相乘对于一次操作,数据输入和ROM地址对应的数据输出到乘法器完成乘法,根据控制信号加法器将乘法结果与部分和输入或累加结果进行相加,累加寄存器的值输出到部分和输出端口。其中的reg用于保证数据对齐。
该设计使用中央控制的方式进行控制,所有控制信号均由控制器生成,包括:
名称 | 类型 | 位宽 | 说明 |
---|---|---|---|
clk | input | 1 | 系统时钟 |
rst_n | input | 1 | 系统复位,低有效 |
din_valid | input | 1 | 输入数据P2P端口有效信号 |
din_busy | output | 1 | 输入数据P2P端口忙信号 |
control_ram_addr | output | SERL_PRAM_LOG+PALL_PRAM_LOG | 读写数据ram的地址 |
control_ram_write | output | 1 | 写数据ram请求信号 |
control_rom_addr | output | SERI_PAM_LOG | 参数ROM地址,产生ROM的乘法操作数 |
control_mux_controller | output | 2 | 控制信号,控制累加器功能 |
dout_busy | input | 1 | 输出数据P2P端口忙信号 |
dout_valid | output | 1 | 输出数据P2P端口有效信号 |
该部分的核心是一个状态机,该状态机控制所有部件的运行,状态机的流程图如下所示:
controller_fsm.png
该状态机有四个状态:
输入P2P端口需要控制的信号是din_busy
信号,该信号仅在状态机状态为INIT时为低,否则为高。
输入部分RAM地址的控制信号为
,其中:
输入部分RAM写请求信号在COMP的最后一个周期拉高,将数据写入RAM,同时将输入寄存器的值作为数据输出
串行处理单元的ROM地址信号在COMP状态从SERI_PAM-1到0递减,每时钟周期减1
串行处理单元的MUX控制信号如下所示:
输出部分控制信号为dout_valid
,在进入WRITE状态3个时钟周期后将该信号拉高,退出WRITE状态时拉低