posit_decoder
我们开始对 posit_decoder.sv
文件的前 20 行进行逐行分析。该模块的功能是将一个 N 位的 Posit 编码解码为:符号位、regime+exponent、尾数(mantissa)。
📝 第 1 行:模块说明
// Posit decoder
📘 说明该模块用于将 Posit 格式解码成其内部数值表示。
🔧 第 2–8 行:模块与参数定义
module posit_decoder #(
parameter int unsigned n = 16, // word size
parameter int unsigned es = 1, // exponent size
//do not change
parameter int unsigned nd = pdpu_pkg::clog2(n-1),
parameter int unsigned EXP_WIDTH = nd+es, // not include sign bit
parameter int unsigned MANT_WIDTH = n-es-3 // not include implicit bit
)(
参数 | 含义 |
---|---|
n |
Posit 编码的总位宽(例如 n=16 ) |
es |
exponent 字段的最大位宽 |
nd |
最大可能的 regime 位数(clog2(n-1)) |
EXP_WIDTH |
regime + exponent 的总位宽 |
MANT_WIDTH |
尾数的最大长度(去掉 sign, regime 最少1位, es) |
注:这些参数与之前的
posit_encoder
模块保持一致,确保解码-编码的对称性。
🔌 第 9–13 行:输入输出端口定义
input logic [n-1:0] operand_i,
output logic sign_o,
output logic signed [EXP_WIDTH:0] rg_exp_o,
output logic [MANT_WIDTH:0] mant_norm_o
);
信号名 | 方向 | 含义 |
---|---|---|
operand_i |
输入 | 原始 Posit 编码 |
sign_o |
输出 | 符号位 |
rg_exp_o |
输出 | regime + exponent 联合位段(有符号) |
mant_norm_o |
输出 | 规格化后的尾数(包含隐藏位) |
🧱 第 15–17 行:注释说明
// ---------------
// Perform two's complement if negative
// ---------------
📘 说明即将进行符号提取及补码处理,Posit 编码采用 sign-magnitude 编码方式,负数需转换为补码以统一后续处理。
🔧 第 18–19 行:内部变量定义
logic sign;
logic [n-2:0] operand_value;
变量名 | 含义 |
---|---|
sign |
Posit 的符号位(最高位) |
operand_value |
除去符号位的剩余位串(用于 regime/exponent 解码) |
这些是标准 Posit 解码流程的前置准备步骤:
- 提取 sign 位
- 准备对负数进行反码/补码转换
- 进入 regime 解析流程
是否继续分析接下来如何实现补码操作和 regime 字段提取?
我们继续分析 posit_decoder.sv
中第 21–40 行的逻辑,这部分实现了:
- 提取符号位并进行补码转换(若为负数)
- 准备进行 regime 解码(通过前导 0 或 1 计数)
➕ 行 22:符号位提取
assign sign = operand_i[n-1];
📘 Posit 编码的最高位是符号位。
♻️ 行 23:补码转换
assign operand_value = sign ? (~operand_i[n-2:0]+1) : operand_i[n-2:0];
🧠 解释:
- 若为负数(sign=1),将剩下的位串取补码(反码+1)
- 否则直接取剩余位作为 operand value
- 这样处理后的
operand_value
可以统一进行 regime + exponent 解码
🔍 行 26–27:regime 字段符号提取
logic regS;
assign regS = operand_value[n-2];
- regime 的符号是编码中的次高位(bit n-2)
regS = 1
表示 regime 以 1 开头(即k >= 0
),否则k < 0
🔄 行 28–29:准备 LZC 输入
logic [n-2:0] lzc_operand;
assign lzc_operand = regS ? (~operand_value) : operand_value;
📘 为了用 LZC 模块统一处理 regime 位长度:
- 若
regS == 1
(即 1 开头),将其取反变为 0 开头 - 然后用 leading-zero-counter 来数连续 0 的数量
这样不论是连续 1
(正 regime)还是连续 0
(负 regime),都能转化为数 0。
🔧 行 30–32:LZC 模块接口定义
logic [nd-1:0] leading_zero_count;
logic lzc_zeroes;
- 输出
leading_zero_count
是前导 0 或 1 的数量(用于确定 regime 位长) - 输出
lzc_zeroes
用于检测是否全部为 0(非法 Posit)
⚙️ 行 33–39:实例化 LZC
lzc #(
.WIDTH(n-1),
.MODE(1'b1)
) u_lzc (
.in_i(lzc_operand),
📌 LZC 使用我们之前转换的模块,mode 设置为 1
表示数从 MSB 开始的前导 0。
此处用于 regime 解码:
regS=1
(即 regime 开头是 1)时,取反后得到若干个 0 → 用 LZC 数出 regime 的长度。regS=0
(即负 regime)时,也可以直接 LZC。
是否继续分析 LZC 输出与 regime 指数组合字段(rg_exp_o
)的赋值部分?
我们继续分析 posit_decoder.sv
的第 41–60 行,这部分主要完成:
- 根据 LZC 结果计算 regime 的长度 与 k 值
- 移除 regime 字段准备提取 exponent 和 mantissa
✅ 行 41–43:LZC输出连接
.cnt_o(leading_zero_count),
.empty_o(lzc_zeroes)
);
📌 LZC 输出前导 0 的数量(leading_zero_count
)与是否全 0(lzc_zeroes
)
🔢 行 45–47:定义中间变量
logic [nd-1:0] runlength; // 连续相同位的个数
logic [nd-1:0] regime_bits; // regime 字段总长度
logic signed [nd:0] regime_k; // regime 表示的实际值 k
这些变量用于计算 regime k 值,并为指数字段提供基础。
🧮 行 49–51:计算 regime k 值及长度
assign runlength = lzc_zeroes ? (n-1) : leading_zero_count;
assign regime_bits = lzc_zeroes ? (n-1) : (leading_zero_count+1);
assign regime_k = regS ? ({1'b0,runlength-1}) : ({1'b1,~runlength+1});
表达式 | 含义 |
---|---|
runlength |
若全 0,则最大长度,否则取 LZC 结果 |
regime_bits |
regime 字段的总长度为连续相同位数 + 1(符号位) |
regime_k |
实际 regime 值 k,根据 regS 正负号处理,采用补码计算 |
注:
regS=1
表示正 k,k = runlength - 1regS=0
表示负 k,k = -runlength(已用补码表达)
🧹 行 54–55:准备移除 regime 字段
// Left shift regime field
logic [n-2:0] op_no_rg;
变量 op_no_rg
用于保存 移除 regime 后的位串(接下来将从中提取 exponent 和 mantissa)
🌀 行 57–59:桶形移位器准备
barrel_shifter #(
.WIDTH(n-1),
.SHIFT_WIDTH(nd),
调用我们之前转换过的 barrel_shifter
,用于将 operand_value
左移 regime_bits
位,从而剥离 regime 字段。
是否继续分析 barrel_shifter
的连接、剩余 exponent 与 mantissa 提取过程?
我们继续分析 posit_decoder.sv
的第 41–60 行,这部分主要完成:
- 根据 LZC 结果计算 regime 的长度 与 k 值
- 移除 regime 字段准备提取 exponent 和 mantissa
✅ 行 41–43:LZC输出连接
.cnt_o(leading_zero_count),
.empty_o(lzc_zeroes)
);
📌 LZC 输出前导 0 的数量(leading_zero_count
)与是否全 0(lzc_zeroes
)
🔢 行 45–47:定义中间变量
logic [nd-1:0] runlength; // 连续相同位的个数
logic [nd-1:0] regime_bits; // regime 字段总长度
logic signed [nd:0] regime_k; // regime 表示的实际值 k
这些变量用于计算 regime k 值,并为指数字段提供基础。
🧮 行 49–51:计算 regime k 值及长度
assign runlength = lzc_zeroes ? (n-1) : leading_zero_count;
assign regime_bits = lzc_zeroes ? (n-1) : (leading_zero_count+1);
assign regime_k = regS ? ({1'b0,runlength-1}) : ({1'b1,~runlength+1});
表达式 | 含义 |
---|---|
runlength |
若全 0,则最大长度,否则取 LZC 结果 |
regime_bits |
regime 字段的总长度为连续相同位数 + 1(符号位) |
regime_k |
实际 regime 值 k,根据 regS 正负号处理,采用补码计算 |
注:
regS=1
表示正 k,k = runlength - 1regS=0
表示负 k,k = -runlength(已用补码表达)
🧹 行 54–55:准备移除 regime 字段
// Left shift regime field
logic [n-2:0] op_no_rg;
变量 op_no_rg
用于保存 移除 regime 后的位串(接下来将从中提取 exponent 和 mantissa)
🌀 行 57–59:桶形移位器准备
barrel_shifter #(
.WIDTH(n-1),
.SHIFT_WIDTH(nd),
调用我们之前转换过的 barrel_shifter
,用于将 operand_value
左移 regime_bits
位,从而剥离 regime 字段。
是否继续分析 barrel_shifter
的连接、剩余 exponent 与 mantissa 提取过程?
我们继续分析 posit_decoder.sv
的第 61–80 行,这部分主要完成:
- 调用桶形移位器移除 regime 字段
- 提取 sign 输出
- 开始处理 exponent 位段的提取
🔄 行 61–66:桶形移位器调用完毕
.MODE(1'b0)
) u_barrel_shifter(
.operand_i(operand_value),
.shift_amount(regime_bits),
.result_o(op_no_rg)
);
📌 使用 barrel_shifter
将 operand_value
左移 regime_bits
位,去除 regime 段。
- 得到的
op_no_rg
即为剩余位串(接下来将从中提取 exponent 和 mantissa)
🔧 行 68–70:符号位输出连接
// Extract sign
assign sign_o = sign;
- 原始最高位
sign
直接赋值为输出信号sign_o
📤 行 73–75:指数字段处理准备
logic [es:0] exp;
if(es==0) begin
assign exp = 0;
📌 Posit 中 es=0
是合法情况(如 posit<8,0>),此时 exponent 字段不存在,直接设为 0。
否则将提取 exponent 字段的位串。
🔄 行 76:开始处理非零 es 的指数提取
else begin
后续将用 op_no_rg
中前 es
位作为 exponent,再合并 regime_k
输出最终 rg_exp_o
是否继续分析 exponent 提取、指数拼接与尾数提取逻辑?
我们继续分析 posit_decoder.sv
的最后 20 行,完成了 exponent 与 mantissa 提取,并正式结束模块:
🔢 行 81:提取 exponent 位段
assign exp = op_no_rg[n-2:n-2-es+1];
📌 从 op_no_rg
中提取 es
位作为 exponent(高位优先)
例如:
n = 16
,es = 2
⇒ 提取位[14:13]
为 exponent
🔗 行 83:拼接 regime 与 exponent
assign rg_exp_o = regime_k << es | exp;
🧠 意思:
- 将
regime_k
左移es
位,腾出空间 - 然后和 exponent 做按位或,构成一个完整的 指数编码输出
该值为有符号数,代表最终浮点指数(注意 Posit 没有显式 bias)
🧵 行 86–87:尾数提取准备
logic implicit_bit;
assign implicit_bit = |operand_i[n-2:0];
-
Posit 有隐藏位(implicit bit)机制:
- 只要除 sign 外的内容非 0,就说明尾数有效 ⇒ 隐式 bit = 1
- 否则为 0
🧮 行 88:输出尾数(mantissa)
assign mant_norm_o = {implicit_bit, op_no_rg[n-2-es:2]};
📌 拼接:
- 首位是 implicit bit
- 后面是除去 exponent 字段后的剩余比特(从
op_no_rg[n-2-es]
到bit 2
)
注意:
- 舍弃了低两位(
bit 1
和bit 0
),这些位通常为保留/精度外位
🔚 行 89:模块结束
endmodule
✅ 总结:posit_decoder
功能概览
功能模块 | 说明 |
---|---|
sign 提取 | 最高位 |
two's complement | 负数取补码,便于统一处理 |
regime 解码 | 利用前导 0/1 计数器,获得 k |
exponent 提取 | es 位,从 regime 后的位串中获取 |
mantissa 提取 | 隐式位 + 剩余位构成 |
是否将该模块转换为 Verilog-2001 格式?(移除 logic
、always_comb
、包引用等 SV 特性)