Skip to main content

1 PE

下面从模块声明到最后一个 endmodule,逐行解析 PE.sv 的每一句代码。


一、模块声明与参数​

module PE #(
parameter PE_WIDTH = 16,
parameter DATA_WIDTH = 16,
parameter NUM_BLOCK = 4,
parameter NUM_PE = 4,
parameter LOG_NUM_PE = $clog2(NUM_PE),
parameter REF_LEN_WIDTH = 10,
parameter QUERY_LEN_WIDTH = 10,
parameter PE_IDX = 0,
parameter LOG_MAX_TILE_SIZE =10
) (

逐参数解释:

参数默认值含义
PE_WIDTH16分数(H/I/D)的位宽,有符号数
DATA_WIDTH16数据位宽(当前未使用,保留)
NUM_BLOCK4tile 中 block 的数量(用于回溯地址计算)
NUM_PE4PE 阵列中 PE 的个数
LOG_NUM_PElog⁡2(4)=2\log_2(4)=2PE 索引位宽
REF_LEN_WIDTH10ref 坐标位宽
QUERY_LEN_WIDTH10query 坐标位宽
PE_IDX0当前 PE 在阵列中的位置(0 到 NUM_PE-1)
LOG_MAX_TILE_SIZE10marker 位宽

二、输入端口​

input logic clk,
input logic rst,
input logic init_in,
input logic set_param,
信号含义
clk时钟,上升沿触发
rst复位,高有效
init_in初始化脉冲,来自前一个 PE 的 init_out(PE[0] 来自状态机)
set_param参数加载使能,在 SET_PARAM 状态下拉高
input param_valid_in,
input logic [2: 0] ref_in,
信号含义
param_valid_in参数有效标志,来自 !query_oflow(query 未越界时为 1)
ref_in[2:0]当前参考碱基(3-bit nt),从 ref_bram 读出后经 ascii2nt 转换
input logic [PE_WIDTH - 1: 0] sub_A_in,
input logic [PE_WIDTH - 1: 0] sub_C_in,
input logic [PE_WIDTH - 1: 0] sub_G_in,
input logic [PE_WIDTH - 1: 0] sub_T_in,
input logic [PE_WIDTH - 1: 0] sub_N_in,
input logic [PE_WIDTH - 1: 0] gap_open_in,
input logic [PE_WIDTH - 1: 0] gap_extend_in,
信号含义
sub_A/C/G/T/N_in4 个替换分数 + 1 个 N 分数,由 nt2param 根据 query nt 选出
gap_open_ingap 打开罚分(论文中的 oo)
gap_extend_ingap 延长罚分(论文中的 ee)
input logic signed [PE_WIDTH - 1: 0] INF,
信号含义
INF负无穷,用于剪枝(越界或初始化为 −∞-\infty 时使用)
input logic block,
input [1:0] init_state,
input logic signed [PE_WIDTH - 1: 0] diag_score,
信号含义
block0 表示第一个 block,1 表示后续 block
init_state[1:0]初始状态:0=H, 1=D, 2=I, 3=对角
diag_score上一个 block 最后一行的 H 分数(用于 block 衔接)
input logic [REF_LEN_WIDTH - 1: 0] start_idx,
input logic [LOG_MAX_TILE_SIZE : 0] marker,
input logic [REF_LEN_WIDTH - 1: 0] ref_idx_in,
input logic [QUERY_LEN_WIDTH - 1: 0] query_idx_in,
信号含义
start_idx当前 block 在 ref 上的起始索引
marker当前 tile 的 marker(Phase 1/Phase 2 分界点)
ref_idx_in当前格子的 ref 坐标
query_idx_in当前格子的 query 坐标
input logic [PE_WIDTH - 1: 0] D_init_in,
input logic [PE_WIDTH - 1: 0] H_init_in,
信号含义
D_init_inD 状态的初始值
H_init_inH 状态的初始值
input logic signed [PE_WIDTH - 1: 0] H_PE_prev,
input logic signed [PE_WIDTH - 1: 0] I_PE_prev,
input logic [REF_LEN_WIDTH + 1: 0] CH_PE_prev,
input logic [REF_LEN_WIDTH + 1: 0] CI_PE_prev,diag_CH_score,
信号含义
H_PE_prev左邻 PE 的 H 分数(用于 I 的 gap open)
I_PE_prev左邻 PE 的 I 分数(用于 I 的 gap extend)
CH_PE_prev左邻 PE 的 H 收敛指针
CI_PE_prev左邻 PE 的 I 收敛指针
diag_CH_score对角线来源的收敛指针

三、输出端口​

output logic init_out,
output logic [3: 0] dir_out,
output logic [2: 0] ref_out,
信号含义
init_out初始化完成信号,传给下一个 PE
dir_out[3:0]4-bit traceback pointer,写入 tb_bram
ref_out[2:0]参考碱基传给下一个 PE(延迟一拍)
output logic [REF_LEN_WIDTH - 1: 0] ref_idx_out,
output logic [QUERY_LEN_WIDTH - 1: 0] query_idx_out,
信号含义
ref_idx_outref 坐标传给下一个 PE
query_idx_outquery 坐标传给下一个 PE
output logic signed [PE_WIDTH - 1: 0] H_PE,
output logic signed [PE_WIDTH - 1: 0] I_PE,
output logic signed [PE_WIDTH - 1: 0] D_PE,
信号含义
H_PE当前格子的 H 分数,传给下一个 PE
I_PE当前格子的 I 分数,传给下一个 PE
D_PE当前格子的 D 分数
output logic [REF_LEN_WIDTH + 1: 0] CH_PE,
output logic [REF_LEN_WIDTH + 1: 0] CI_PE,
output logic [REF_LEN_WIDTH + 1: 0] CD_PE
);
信号含义
CH_PEH 状态的 convergence pointer
CI_PEI 状态的 convergence pointer
CD_PED 状态的 convergence pointer

位宽说明:REF_LEN_WIDTH + 1 = 11 bits,低 2 位是状态标记(00=H, 01=I, 10=D, 11=marker 前一格),高 9 位是 ref 坐标。


四、局部参数与内部信号​

localparam VER = 2, HOR = 1, DIAG = 0;
localparam CONV_DONT_CARE=0;
localparam NU_DONT_CARE=5;
参数值含义
VER2垂直方向(dir[1:0] = 2'b10)
HOR1水平方向(dir[1:0] = 2'b01)
DIAG0对角方向(dir[1:0] = 2'b00)
CONV_DONT_CARE0收敛指针不参与比较的标记
NU_DONT_CARE5ref 越界标记(来自 ascii2nt 的 N=0,但 PE 内部用 5 表示越界)
// dir tb
// ??00 Diagonal H
// ?001 Horizontal H
// ?101 Horizontal D
// 0?10 Vertical H
// 1?10 Vertical I

注释:这是 dir[3:0] 的编码表,说明 4-bit traceback pointer 的含义。

logic signed [PE_WIDTH - 1: 0] sub_A;
logic signed [PE_WIDTH - 1: 0] sub_C;
logic signed [PE_WIDTH - 1: 0] sub_G;
logic signed [PE_WIDTH - 1: 0] sub_T;
logic signed [PE_WIDTH - 1: 0] sub_N;
logic signed [PE_WIDTH - 1: 0] gap_open;
logic signed [PE_WIDTH - 1: 0] gap_extend;
logic signed [PE_WIDTH - 1: 0] match_reward;
logic param_valid;
信号含义
sub_A/C/G/T/N参数寄存器(从 *_in 加载)
gap_open, gap_extendgap 罚分寄存器
match_reward当前格子的匹配/错配分数(组合逻辑)
param_valid参数有效寄存器
//logic xdrop_flag_reg;
logic [REF_LEN_WIDTH - 1: 0] ref_idx;
logic [QUERY_LEN_WIDTH - 1: 0] query_idx;
信号含义
ref_idx当前格子的 ref 坐标(寄存器)
query_idx当前格子的 query 坐标(直接来自 query_idx_in,见后面 assign)
logic signed [PE_WIDTH - 1: 0] HV_score;
logic signed [PE_WIDTH - 1: 0] IV_score;
logic signed [PE_WIDTH - 1: 0] I_score;

logic signed [PE_WIDTH - 1: 0] HH_score;
logic signed [PE_WIDTH - 1: 0] DH_score;
logic signed [PE_WIDTH - 1: 0] D_prev;
logic signed [PE_WIDTH - 1: 0] H_prev;
logic signed [PE_WIDTH - 1: 0] D_score;

logic signed [PE_WIDTH - 1: 0] HD_score;
logic signed [PE_WIDTH - 1: 0] H_score;
信号含义
HV_scoreI 从 H 打开 gap:Hi,j−1−oH_{i,j-1} - o
IV_scoreI 从 I 延长 gap:Ii,j−1−eI_{i,j-1} - e
I_scoreIi,j=max⁡(HV,IV)I_{i,j} = \max(HV, IV)
HH_scoreD 从 H 打开 gap:Hi−1,j−oH_{i-1,j} - o
DH_scoreD 从 D 延长 gap:Di−1,j−eD_{i-1,j} - e
D_scoreDi,j=max⁡(HH,DH)D_{i,j} = \max(HH, DH)
D_prev, H_prev同一 PE 上一行的 D、H 分数(寄存器)
HD_score对角线 + 匹配:Hi−1,j−1+WH_{i-1,j-1} + W
H_scoreHi,j=max⁡(HD,D,I)H_{i,j} = \max(HD, D, I)
logic signed [PE_WIDTH - 1: 0] H_PE_prev_reg;
logic signed [PE_WIDTH - 1: 0] H_PE_prev2_reg;
logic signed [PE_WIDTH - 1: 0] H_prev_reg;
logic signed [PE_WIDTH - 1: 0] I_PE_prev_reg;
logic signed [PE_WIDTH - 1: 0] D_prev_reg;
logic signed [PE_WIDTH - 1: 0] H_PE_prev2;
信号含义
H_PE_prev_reg对齐后的左邻 H(组合逻辑)
H_PE_prev2_reg对齐后的对角线 H(组合逻辑,原点时强制为 0)
H_prev_reg对齐后的上边 H(组合逻辑,初始化时用 H_init_in)
I_PE_prev_reg对齐后的左邻 I(组合逻辑)
D_prev_reg对齐后的上边 D(组合逻辑,初始化时用 D_init_in)
H_PE_prev2对角线 H 的寄存器(延迟一拍)
logic [REF_LEN_WIDTH + 1: 0] CH_score;
logic [REF_LEN_WIDTH + 1: 0] CI_score;
logic [REF_LEN_WIDTH + 1: 0] CD_score;
logic [REF_LEN_WIDTH + 1: 0] CH_score_reg;
logic [REF_LEN_WIDTH + 1: 0] CH_score_reg2;
logic [REF_LEN_WIDTH + 1: 0] CD_score_reg;
信号含义
CH_score当前格子的 H 收敛指针(组合逻辑)
CI_score当前格子的 I 收敛指针(组合逻辑)
CD_score当前格子的 D 收敛指针(组合逻辑)
CH_score_reg左边格子的 H 收敛指针(寄存器)
CH_score_reg2对角线格子的 H 收敛指针(寄存器)
CD_score_reg上边格子的 D 收敛指针(寄存器)
logic [3: 0] dir;
logic [1:0] H_flag;
logic D_flag;
logic I_flag;
信号含义
dir[3:0]4-bit traceback pointer(组合逻辑)
H_flag[1:0]未使用
I_flagI 是从 H 打开(0)还是从 I 延长(1)
D_flagD 是从 H 打开(0)还是从 D 延长(1)

五、match_reward_calculation 组合逻辑​

always_comb begin : match_reward_calculation
case ({ref_in})
3'b111 : match_reward = sub_N;
3'b001 : match_reward = sub_A;
3'b010 : match_reward = sub_C;
3'b011 : match_reward = sub_G;
3'b100 : match_reward = sub_T;
default : match_reward = 0;
endcase
end

逐行解释:

  • ref_in 是 3-bit nt,从 ref_bram 读出后经 ascii2nt 转换。
  • 3'b111 不是标准 nt 编码,这里应该是 N 的一种表示(代码中 NU_DONT_CARE=5 也说明越界时 ref_in 可能为 5)。
  • 根据 ref 的 nt,从 sub_A/C/G/T/N 中选出对应的分数。
  • sub_X 是由 nt2param 根据 query nt 选出的替换矩阵行。
  • 例如:query 是 A,ref 是 C,则 match_reward = sub_C,对应 W 矩阵中 A-C 的分数。

注意:3'b001=A, 3'b010=C, 3'b011=G, 3'b100=T,这是 ascii2nt 的编码。


六、boundary_conditions 组合逻辑​

always_comb begin: boundary_conditions
if (!query_idx && !ref_idx)
H_PE_prev2_reg = 0;
else
H_PE_prev2_reg = H_PE_prev2;
  • 如果当前是原点 (0,0)(0,0)(query_idx=0 且 ref_idx=0),对角线 H 强制为 0。
  • 否则,H_PE_prev2_reg 等于寄存器 H_PE_prev2。
H_PE_prev_reg = H_PE_prev;
I_PE_prev_reg = I_PE_prev;
  • H_PE_prev_reg 和 I_PE_prev_reg 直接来自左邻 PE。
  • 这两个信号用于 I 状态的 gap open 和 gap extend。
if (!init_out&&init_in) begin
H_prev_reg = H_init_in;
D_prev_reg = D_init_in;
end
else begin
H_prev_reg = H_prev;
D_prev_reg = D_prev;
end
end
  • 当 init_in=1 且 init_out=0(初始化脉冲的上升沿)时,用外部初始值 H_init_in 和 D_init_in。
  • 否则,使用寄存器 H_prev 和 D_prev(上一行同 PE 的分数)。

物理意义:这是处理 tile 起点初始化的逻辑。当一个新的 block 或 tile 开始时,PE 需要用外部传入的初始值,而不是自己上一行的值。


七、score_calculation 组合逻辑​

这是 PE 最核心的分数计算,对应论文公式 (1)–(3)。

always_comb begin : score_calculation
// I (i,j) calculation
HV_score = H_PE_prev_reg + gap_open;
IV_score = I_PE_prev_reg + gap_extend;
if ($signed(HV_score) >= $signed(IV_score)) begin
I_score = HV_score;
I_flag = 0;
end
else begin
I_score = IV_score;
I_flag = 1;
end

逐行解释:

  1. HV_score = H_PE_prev_reg + gap_open:从 H 状态打开 gap,付 gap_open 罚分。H_PE_prev_reg 是左邻 PE 的 H 分数(因为插入使 query 前进,ref 不动,所以来源是左边的格子)。
  2. IV_score = I_PE_prev_reg + gap_extend:从 I 状态延长 gap,付 gap_extend 罚分。
  3. 如果 HV_score >= IV_score,说明从 H 打开 gap 更优,I_flag = 0。
  4. 否则,从 I 延长 gap 更优,I_flag = 1。
  5. I_score 是两者最大值。

注意:这里用的是加法,不是减法,因为 gap_open 和 gap_extend 在参数中已经是负值(罚分)。

// D (i,j) calculation
HH_score = H_prev_reg + gap_open;
DH_score = D_prev_reg + gap_extend;
if ($signed(HH_score) >= $signed(DH_score)) begin
D_score = HH_score;
D_flag = 0;
end
else begin
D_score = DH_score;
D_flag = 1;
end

逐行解释:

  1. HH_score = H_prev_reg + gap_open:从 H 状态打开 gap。H_prev_reg 是同一 PE 上一行的 H 分数(因为删除使 ref 前进,query 不动,所以来源是上边的格子)。
  2. DH_score = D_prev_reg + gap_extend:从 D 状态延长 gap。
  3. 如果 HH_score >= DH_score,D_flag = 0(从 H 打开)。
  4. 否则,D_flag = 1(从 D 延长)。
  5. D_score 是两者最大值。
// H (i,j) calculation
HD_score = H_PE_prev2_reg + match_reward;
H_score = HD_score;
dir[1:0] = DIAG;
if(D_score>H_score) begin
H_score = D_score;
dir[1:0] = HOR;
end
if(I_score>H_score) begin
H_score = I_score;
dir[1:0] = VER;
end

逐行解释:

  1. HD_score = H_PE_prev2_reg + match_reward:对角线来源。H_PE_prev2_reg 是对角线格子的 H 分数,match_reward 是匹配/错配分数。
  2. 默认 H_score = HD_score,dir[1:0] = DIAG = 0。
  3. 如果 D_score > H_score,更新 H_score = D_score,dir[1:0] = HOR = 1。
  4. 如果 I_score > H_score,更新 H_score = I_score,dir[1:0] = VER = 2。
  5. 最终 H_score 是三者最大值,dir[1:0] 记录来源。

注意:这里用 > 而不是 >=,所以优先级是:对角 > 水平 > 垂直。即如果水平和对角相等,选对角。

if (I_flag) begin
dir[3] = 1;
end
else begin
dir[3] = 0;
end

if (D_flag) begin
dir[2] = 1;
end
else begin
dir[2] = 0;
end
  • dir[3] = I_flag:记录 I 是从 H 打开(0)还是从 I 延长(1)。
  • dir[2] = D_flag:记录 D 是从 H 打开(0)还是从 D 延长(1)。

4-bit dir 完整编码:

  • dir[3:0] = ??00:来自对角线
  • dir[3:0] = ?001:来自水平 H
  • dir[3:0] = ?101:来自水平 D
  • dir[3:0] = 0?10:来自垂直 H
  • dir[3:0] = 1?10:来自垂直 I
if(PE_IDX==0&&!block&&init_state!=3&&!init_out) begin
H_score=init_state==0?0:-INF;
I_score=init_state==2?0:-INF;
D_score=init_state==1?0:-INF;
end

逐行解释:

  • 只在 PE_IDX==0(第一个 PE)、!block(第一个 block)、init_state!=3(不是对角初始化)、!init_out(未初始化)时生效。
  • 如果 init_state==0(H),则 H_score=0,否则 -INF。
  • 如果 init_state==2(I),则 I_score=0,否则 -INF。
  • 如果 init_state==1(D),则 D_score=0,否则 -INF。

物理意义:这是 DP 矩阵左上角的初始化。根据 init_state 决定从哪个状态开始。

if(ref_in==NU_DONT_CARE) begin
H_score=-INF;
end
end
  • 如果 ref 越界(ref_in == NU_DONT_CARE = 5),H 分数设为 −∞-\infty。
  • 这是 X-Drop 剪枝的硬件实现:越界的格子不参与计算。

八、convergence_logic 组合逻辑​

这是 TALCO 的核心,计算 convergence pointer。

always_comb begin : convergence_logic
if (ref_idx + query_idx == marker - 1) begin
CH_score = {ref_idx, 2'b11};
CI_score = {ref_idx, 2'b11}; // not required
CD_score = {ref_idx, 2'b11}; // not required
end

逐行解释:

  • ref_idx + query_idx == marker - 1:当前格子在 marker 前一格的反对角线上。
  • CH_score = {ref_idx, 2'b11}:H 收敛指针 = {ref_idx, 2'b11},低 2 位 11 是特殊标记。
  • CI_score 和 CD_score 同理,但注释说不需要。

物理意义:这是 marker 前一格的特殊处理,用于标识 marker 边界。

else if (ref_idx + query_idx == marker) begin
CH_score = {ref_idx, 2'b00};
CI_score = {ref_idx, 2'b01};
CD_score = {ref_idx, 2'b10};
end

逐行解释:

  • ref_idx + query_idx == marker:当前格子在 marker 上。
  • CH_score = {ref_idx, 2'b00}:H 收敛指针 = 自己,低 2 位 00 表示 H 状态。
  • CI_score = {ref_idx, 2'b01}:I 收敛指针 = 自己,低 2 位 01 表示 I 状态。
  • CD_score = {ref_idx, 2'b10}:D 收敛指针 = 自己,低 2 位 10 表示 D 状态。

物理意义:这是 marker 上的初始化。每个格子根据自己的状态,获得唯一 id {ref_idx, state}。这正是论文中"为 marker 上每个 cell 分配唯一 id"的硬件实现。

else begin
if (I_flag) begin
CI_score = CI_PE_prev;
end
else begin
CI_score = CH_PE_prev;
end

逐行解释:

  • 非 marker 格子,正常传播。
  • 如果 I_flag=1(从 I 延长 gap),CI_score = CI_PE_prev(继承左邻的 I 收敛指针)。
  • 如果 I_flag=0(从 H 打开 gap),CI_score = CH_PE_prev(继承左邻的 H 收敛指针)。

物理意义:I 状态的回溯来源决定它继承哪个收敛指针。

if (D_flag) begin
CD_score = CD_score_reg;
end
else begin
CD_score = CH_score_reg;
end

逐行解释:

  • 如果 D_flag=1(从 D 延长 gap),CD_score = CD_score_reg(继承上边的 D 收敛指针)。
  • 如果 D_flag=0(从 H 打开 gap),CD_score = CH_score_reg(继承上边的 H 收敛指针)。

物理意义:D 状态的回溯来源决定它继承哪个收敛指针。

casex (dir)
4'b??00 : CH_score = CH_score_reg2;
4'b?001 : CH_score = CH_score_reg;
4'b?101 : CH_score = CD_score_reg;
4'b0?10 : CH_score = CH_PE_prev;
4'b1?10 : CH_score = CI_PE_prev;
default: CH_score = CH_score_reg2;
endcase
end

逐行解释:

  • casex 使用通配符 ?,只匹配某些位。
  • 4'b??00:来自对角线 → CH_score = CH_score_reg2(继承对角线的 H 收敛指针)。
  • 4'b?001:来自水平 H → CH_score = CH_score_reg(继承左边的 H 收敛指针)。
  • 4'b?101:来自水平 D → CH_score = CD_score_reg(继承左边的 D 收敛指针)。
  • 4'b0?10:来自垂直 H → CH_score = CH_PE_prev(继承左边的 H 收敛指针)。
  • 4'b1?10:来自垂直 I → CH_score = CI_PE_prev(继承左边的 I 收敛指针)。
  • default:默认继承对角线的 H 收敛指针。

物理意义:这正是论文中的 redirect 函数。根据 traceback 方向,继承前驱格子的 convergence pointer。

if(ref_in==NU_DONT_CARE) begin
CH_score=CONV_DONT_CARE;
CD_score=CONV_DONT_CARE;
CI_score=CONV_DONT_CARE;
end
end
  • 如果 ref 越界,所有收敛指针设为 CONV_DONT_CARE = 0。
  • 这是为了在 reduction tree 中不参与收敛比较。

九、assign query_idx​

assign query_idx = query_idx_in;
  • query_idx 直接来自 query_idx_in,没有寄存器延迟。
  • 这样 boundary_conditions 和 convergence_logic 可以立即使用当前 query 坐标。

十、ref_out 寄存器​

always_ff @ (posedge clk) begin
if(rst)
ref_out <= 0;
else
ref_out <= ref_in;
end
  • ref_out 延迟一拍输出 ref_in。
  • 这样下一个 PE 在下一周期收到当前 PE 的 ref 碱基。
  • 这是 systolic 阵列的流水线对齐。

十一、主寄存器块 always_ff​

always_ff @ (posedge clk) begin
if (rst) begin
sub_A <= 0;
sub_C <= 0;
sub_G <= 0;
sub_T <= 0;
sub_N <= 0;
gap_open <= 0;
gap_extend <= 0;

H_prev <= 0;
D_prev <= 0;
H_PE_prev2 <= 0;
CH_score_reg <= 0;
CD_score_reg <= 0;
CH_score_reg2 <= 0;

ref_idx <= 0;

init_out <= 0;
ref_idx_out <= 0;
query_idx_out <= 0;
I_PE <= -INF;
H_PE <= -INF;
D_PE <= -INF;
CH_PE <= 0;
CI_PE <= 0;
CD_PE <= 0;
dir_out <= 0;
end

复位分支:所有寄存器清零,H/I/D 初始化为 -INF。

else if (set_param) begin
sub_A <= sub_A_in;
sub_C <= sub_C_in;
sub_G <= sub_G_in;
sub_T <= sub_T_in;
sub_N <= sub_N_in;
gap_open <= gap_open_in;
gap_extend <= gap_extend_in;
H_PE<=-INF;
param_valid<=param_valid_in;
end

参数加载分支:在 set_param=1 时,把打分参数加载到寄存器。这是 SET_PARAM 状态下的操作。

else if(param_valid) begin
init_out <= init_in;
dir_out <= dir;


ref_idx <= ref_idx_in;
ref_idx_out <= ref_idx_in;
if (init_in) begin

正常运行时:

  • init_out <= init_in:初始化信号向后传递。
  • dir_out <= dir:traceback pointer 输出。
  • ref_idx <= ref_idx_in:更新 ref 坐标。
  • ref_idx_out <= ref_idx_in:ref 坐标传给下一个 PE。
  • 如果 init_in=1(初始化),进入下面的分支。
H_prev <= H_score;
D_prev <= D_score;
H_PE_prev2 <= H_PE_prev;

CH_score_reg <= CH_score;
CD_score_reg <= CD_score;
CH_score_reg2 <= CH_PE_prev;

query_idx_out <= query_idx_in + 1;
I_PE <= I_score;
H_PE <= H_score;
D_PE <= D_score;
CH_PE <= CH_score;
CI_PE <= CI_score;
CD_PE <= CD_score;
end

初始化分支(init_in=1):

  • H_prev <= H_score:当前 H 存入 H_prev,供下一行使用。
  • D_prev <= D_score:当前 D 存入 D_prev。
  • H_PE_prev2 <= H_PE_prev:左邻 H 存入对角线寄存器。
  • CH_score_reg <= CH_score:当前 H 收敛指针存入左边寄存器。
  • CD_score_reg <= CD_score:当前 D 收敛指针存入上边寄存器。
  • CH_score_reg2 <= CH_PE_prev:左邻 H 收敛指针存入对角线寄存器。
  • query_idx_out <= query_idx_in + 1:query 坐标加 1 传给下一个 PE。
  • I_PE <= I_score:I 分数输出。
  • H_PE <= H_score:H 分数输出。
  • D_PE <= D_score:D 分数输出。
  • CH_PE <= CH_score:H 收敛指针输出。
  • CI_PE <= CI_score:I 收敛指针输出。
  • CD_PE <= CD_score:D 收敛指针输出。
else begin
H_PE_prev2 <= block ? (PE_IDX?-INF:diag_score): H_init_in;
CH_score_reg2 <= block ? (PE_IDX?CONV_DONT_CARE:diag_CH_score): CONV_DONT_CARE;
H_prev <= -INF;
D_prev <= -INF;
CH_score_reg<=CONV_DONT_CARE;
CD_score_reg<=CONV_DONT_CARE;
CH_PE <= CONV_DONT_CARE;
CI_PE <= CONV_DONT_CARE;
CD_PE <= CONV_DONT_CARE;
end
end
end

非初始化分支(init_in=0):

  • H_PE_prev2 <= block ? (PE_IDX ? -INF : diag_score) : H_init_in:
    • 如果是后续 block,且 PE_IDX=0,用 diag_score(上一个 block 的分数);其他 PE 用 -INF。
    • 如果是第一个 block,用 H_init_in。
  • CH_score_reg2 <= block ? (PE_IDX ? CONV_DONT_CARE : diag_CH_score) : CONV_DONT_CARE:
    • 类似,后续 block 的 PE[0] 用 diag_CH_score。
  • H_prev <= -INF:H 清零。
  • D_prev <= -INF:D 清零。
  • CH_score_reg <= CONV_DONT_CARE:收敛指针清零。
  • CD_score_reg <= CONV_DONT_CARE。
  • CH_PE <= CONV_DONT_CARE。
  • CI_PE <= CONV_DONT_CARE。
  • CD_PE <= CONV_DONT_CARE。

物理意义:非初始化时,PE 处于空闲状态,所有输出清零,等待下一个 block 或 tile 的初始化信号。


十二、完整的数据流总结​

┌─────────────────────────────────────────────────────────────┐
│ 输入 │
│ ref_in, sub_*, gap_*, H_PE_prev, I_PE_prev, │
│ H_prev, D_prev, H_PE_prev2, CH_PE_prev, CI_PE_prev, │
│ CH_score_reg, CD_score_reg, CH_score_reg2, init_in │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ match_reward_calculation (组合逻辑) │
│ match_reward = 查表(ref_in, sub_A/C/G/T/N) │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ boundary_conditions (组合逻辑) │
│ H_PE_prev2_reg = (原点) ? 0 : H_PE_prev2 │
│ H_prev_reg = (init_in) ? H_init_in : H_prev │
│ D_prev_reg = (init_in) ? D_init_in : D_prev │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ score_calculation (组合逻辑) │
│ I_score = max(H_PE_prev + go, I_PE_prev + ge) │
│ D_score = max(H_prev + go, D_prev + ge) │
│ H_score = max(H_PE_prev2 + match, D_score, I_score) │
│ dir[3:0] = 方向编码 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ convergence_logic (组合逻辑) │
│ marker 上:CH = {ref_idx, 00}, CI = {ref_idx, 01}, ... │
│ 非 marker:根据 dir 继承前驱的收敛指针 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ always_ff (时钟上升沿) │
│ 更新所有输出寄存器和内部寄存器 │
└─────────────────────────────────────────────────────────────┘

十三、与论文的精确对应​

论文公式/概念PE 代码行
Ii,j=max⁡(Hi,j−1−o,Ii,j−1−e)I_{i,j} = \max(H_{i,j-1}-o, I_{i,j-1}-e)HV_score, IV_score, I_score, I_flag
Di,j=max⁡(Hi−1,j−o,Di−1,j−e)D_{i,j} = \max(H_{i-1,j}-o, D_{i-1,j}-e)HH_score, DH_score, D_score, D_flag
Hi,j=max⁡(I,D,Hi−1,j−1+W)H_{i,j} = \max(I,D,H_{i-1,j-1}+W)HD_score, H_score, dir[1:0]
4-bit traceback pointerdir[3:0]
Marker 初始化ref_idx + query_idx == marker 分支
Redirect 函数casex(dir) 继承前驱收敛指针
X-Drop 剪枝ref_in == NU_DONT_CARE → H_score = -INF
Convergence pointerCH_PE, CI_PE, CD_PE
Block 衔接block ? diag_score : H_init_in
原点初始化!query_idx && !ref_idx → H_PE_prev2_reg = 0

十四、关键设计细节​

14.1 为什么 H_PE_prev2 需要延迟一拍?​

在 systolic 阵列中,对角线来源的 H 分数需要跨两个 PE 传递。H_PE_prev2 是一个寄存器,存储"上一周期左邻 PE 的 H",这样当前 PE 在计算时,H_PE_prev2_reg 正好是对角线格子的 H。

14.2 为什么 CH_score_reg2 存的是 CH_PE_prev?​

对角线来源的收敛指针需要跨两个 PE。CH_score_reg2 <= CH_PE_prev 表示"上一周期左邻 PE 的 CH",正好对应对角线格子。

14.3 为什么用 casex 而不是 case?​

casex 允许 ? 通配符,可以只匹配 dir 的部分位。例如 4'b??00 匹配所有低 2 位为 00 的情况,不管高 2 位是什么。

14.4 为什么 init_in 是脉冲?​

init_in 是一个单周期脉冲,表示"当前格子是 tile 的起点"。PE 只在 init_in=1 时更新分数输出,其他时候保持清零。

14.5 为什么 block 影响 H_PE_prev2?​

当 block=1(后续 block)时,PE[0] 的对角线分数来自 diag_score(上一个 block 的最后一行),其他 PE 用 -INF。这是因为 block 衔接时,只有 PE[0] 需要从上一个 block 继承分数。


十五、一句话总结​

PE 是 TALCO-XDrop 的基本计算单元,它在一个周期内完成:匹配分数查表、H/I/D 递推计算、4-bit traceback pointer 生成、convergence pointer 传播、X-Drop 剪枝,以及所有寄存器更新。PE 之间通过 systolic 方式连接,ref 和分数向右传递,每个 PE 绑定一个 query 碱基。PE 的设计精确对应论文 Algorithm 1 中的 AlignScore 和 redirect 函数。