RTL Arithmetic - 整数数制

本文的目的是阐述RTL中的整数表示

RTL Arithmetic
从算法到RTL实现 - 移知


1. 整数表示

如果将最高位作为符号位,就是有符号整数,否则就是无符号整数

1.1. 无符号数 unsigned

verilog中unsigned或者不加,就为无符号数

即对于Nbit的无符号数,二进制 N ′ b 000...00 → N ′ b 111...11 N'b000...00→N'b111...11 Nb000...00Nb111...11,表示十进制 0 → ( 2 N ) − 1 0→(2^{N})-1 0(2N)1

1.2. 有符号数 signed

verilog中signed,就为有符号数。有符号数用补码表示

● 二进制正数_补:符号位(0为正)+原码。

● 二进制负数_补:符号位(1为负)+补码。

正负数转换规则为:符号位取反,其余位按位取反,末位加1。

即对于Nbit的有符号数,二进制 N ′ b 100...00 → N ′ b 011...11 N'b100...00→N'b011...11 Nb100...00Nb011...11,表示十进制 − ( 2 N − 1 ) → ( 2 N − 1 ) − 1 -(2^{N-1})→(2^{N-1})-1 (2N1)(2N1)1

例如3表示4’b0011,则-3表示为4’b1101,-5为4’b1011。

在线进制转换

1.3. signedunsigned

关键词,用于标注变量是有符号还是无符号。特性如下

● 所有常量、变量均为signed,将按照有符号数扩位、运算

● 变量:unsigned或signed截位、signed扩位等均看作unsigned。使用$signed()转化为signed

● 常量:3'd2为unsigned、$signed(3'd2)2为signed

注意signedunsigned相互赋值不会改变数值

举例如下

`timescale 1ns/1ps

module arith_tb();

logic	unsigned [3:0]	u_a1;
logic	unsigned [2:0]	u_a2;
logic	  signed [3:0]	s_a1;
logic	  signed [2:0]	s_a2;

initial begin
	u_a1 = 4'b0110;
	u_a2 = 3'b110;
	s_a1 = 4'b1001;
	s_a2 = 3'b100;	

	//--------------------------------------------------------------------------------------------------------
	// compare
	//--------------------------------------------------------------------------------------------------------
	
	$display("s_a1>4'd5 = %b",s_a1>4'd5);								// 4'd9 > 4'd5 = 1
	
	$display("s_a1>$signed(4'd5) = %b",s_a1>$signed(4'd5));				// -4'd7 > 4'd5 = 0

	$display("s_a1<-4'd5 = %b",s_a1<-4'd5);								// 4'd9 < 4'd11 = 1
	
	$display("s_a1<$signed(-4'd5) = %b",s_a1<$signed(-4'd5));			// -4'd7 < -4'd5 = 1	
	
	//--------------------------------------------------------------------------------------------------------
	// add
	//--------------------------------------------------------------------------------------------------------
	
	s_a1 = s_a2 + u_a2;					//1010 = 0100 + 0110 - automatic extract 0	
	#1;                                 
	$display("s_a1 = %b",s_a1);				
	                                    
	s_a1 = s_a2 + s_a2;					//1000 = 1100 + 1100 - automatic extract sign bits	
	#1;                                 
	$display("s_a1 = %b",s_a1);	

	s_a1 = s_a2 + 3'd2;					//0110 = 0100 + 0010 - automatic extract sign bits	
	#1;                                 
	$display("s_a1 = %b",s_a1);		
	
	s_a1 = s_a2 + $signed(3'd2);		//1110 = 1100 + 0010 - automatic extract sign bits	
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = s_a2 + s_a2[2:1];			//0110 = 0100 + 0010 - automatic extract 0	
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = s_a2 + $signed(s_a2[2:1]);	//1010 = 1100 + 1110 - automatic extract sign bits
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = s_a2 + {1'b0,s_a2};			//1000 = 0100 + 0100 - automatic extract 0	
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = s_a2 + {$signed(1'b0),s_a2};	//1000 = 0100 + 0100 - automatic extract 0
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = s_a2 + $signed({1'b0,s_a2});	//0000 = 1100 + 0100 - automatic extract sign
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	s_a1 = -s_a2 ;						//0100 - automatic inverse
	#1;                                 
	$display("s_a1 = %b",s_a1);
	
	//--------------------------------------------------------------------------------------------------------
	// bit calculation
	//--------------------------------------------------------------------------------------------------------
	
	s_a1 = u_a2;	                    //0110 - automatic extract 0
	#1;                                 
	$display("s_a1 = %b",s_a1);				
	                                    
	s_a1 = s_a2;                        //1100 - automatic extract sign bits
	#1;                                 
	$display("s_a1 = %b",s_a1);						
	 
	s_a1 = s_a1 >>> 1; 					//1110 - arithmetic right shift 
	#1;                                 
	$display("s_a1 = %b",s_a1);
	 
	s_a1 = s_a1 >> 1;                   //0111 - logic right shift 
	#1;                                 
	$display("s_a1 = %b",s_a1);	                               		
end

endmodule

1.4. 位宽确定

那么RTL中的位宽如何确定呢?

对于某个整数,按照如下方法判定位宽,防止溢出。

整数位宽python:math.ceil[math.(max abs)] (+1)

小数位宽python:math.floor[math.log2(精度)]

例如某个无符号数c范围为0-168.346,那么其整数部分为0~168位宽就为 c e i l [ l o g 2 ( 168 ) ] = 8 ceil[log2(168)] = 8 ceil[log2(168)]=8,小数部分为0.001~0.999位宽就为 f l o o r [ l o g 2 ( 0.001 ) ] = − 10 floor[log2(0.001)] = -10 floor[log2(0.001)]=10,因此c位宽为10 + 8 = 18bit

有符号数c范围为-345345.457456~178345.56754,整数位宽为 c e i l [ l o g 2 ( 345345 ) ] + 1 = 20 ceil[log2(345345)] +1= 20 ceil[log2(345345)]+1=20,小数部分为20,c位宽就为20 + 20 = 40

整数位宽的原理是,多少位宽能覆盖所有整数。
小数位宽的原理是,多少位宽可所有小数都可转化为整数。所以最小小数,即精度,对应的位宽才可覆盖所有小数。

2. 整数 2 n 2^n 2n倍乘除运算

2.1. 扩位

将一个小位宽的有符号整数增加位宽 ,可用于 2 n 2^n 2n倍乘除。无论是设计者手动扩位还是因为位宽不够自动扩位,都遵循如下规则

unsigned RTL:左扩n位{n'd0,a};,右扩n位{a,n'd0};

unsigned python:左扩n位a,右扩n位a * math.pow(2,n)

signed RTL:左扩n位{{n{a[15]}},a};,右扩n位{a,n'd0};

signed python:左扩n位a,右扩n位a * math.pow(2,n)

例如

logic unsigned	[2:0]	u_a;
logic signed	[2:0]	s_b;
logic signed	[3:0]	s_c;
initial begin
	u_a = 3'b010;
	s_b = 3'b110;
	#1;
	s_c = u_a;		//s_c为4'b0010
	s_c = s_b;		//s_c为4'b1110
end

2.2. 算术移位

注意有符号数逻辑移位会连同符号位一起移位!!!这样的操作无意义!!!

对于有符号数来说,保持符号位不变的算术移位才有意义!!!

unsigned/signed 算术移位 RTL:右移n位a >>> n;,左移n位(溢出风险)a <<< n

unsigned/signed 算术移位 python:右移n位math.floor(a / math.pow(2,n)),左移n位a * math.pow(2,n)

例如-3为4’b1101,左移为4’b1010为-6,右移4’b1110为-2。例如4’b1010为-6,右移1位为4’b1101为-3,左移1位为4’b0100为+4溢出。

2.3. 截位、四舍五入

尾部砍位和四舍五入可用于 2 n 2^n 2n倍除。如果直接抛弃小数部分,即截位。如果根据小数作近似,则为四舍五入。

unsigned 直接尾截n位,python:math.floor(a / math.pow(2,n))

unsigned直接尾截n位,RTL:a[15:n]

unsigned 四舍五入尾截n位,python:round(a / math.pow(2,n))

unsigned 四舍五入尾截n位,RTL:a[15:n] + a[n-1]

如果是signed正数,直接截位,四舍五入就为a[15:n] + a[n-1]

如果是负数,就要看a[n-1:0]是否是'b100...00100...的格式,因为只有这样的格式取反加1后a[n-1] == 1'd0,即a的模四舍五入后无需进位,其他格式都需要进位(例如'b1'b100...00'b0..010..0等)。无需进位的话,补码四舍五入后a[15:n]就需要加1。

同理,负数直接截位一定不进位,其a[15:n]就需要加1。

也就是说,[{1'b1,Z,Q}]_补先求模[{Z,Q}]_原,四舍五入进位则得到[Z+1]_原,其补码[Z+1]_补 = [Z]_反
Verilog对数据进行四舍五入(round)与饱和(saturation)截位

signed 直接尾截n位,python:math.floor(a / math.pow(2,n))

signed直接尾截n位,RTL:a[15:n] + a[15]

signed 四舍五入尾截n位,python:round(a / math.pow(2,n))

signed 四舍五入尾截n位,RTL:a[15:n] + (a[15]? (a[n-1] && (|a[n-2:0])) : a[n-1])

signed 四舍五入尾截1位,RTL:a[15:1] + (a[15]? (1'd0 : a[0])

例如有符号数+15 = 6’b001111,+15/4 = 3.75,截位就是3 = 4’b0011,四舍五入就是+4 = 4’b0100。

有符号数-15 = 6’b110001,-15/4 = -3.75,截位就是-3 = 6’b1101,四舍五入是 -4 = 6’b1100

有符号数-17 = 6’b101111,-17/4 = -4.25,截位就是-4 = 6’b1100,四舍五入是 -4 = 6’b1100

有符号数-16 = 6’b110000,-16/4 = -4,截位就是-3 = 6’b1101,四舍五入是 -4 = 6’b1100

再如-15/2 = -7.5,截位就是-7 = 5’b11001,四舍五入是-8 = 5’b11000

注意截位的溢出风险,如果+15 = 5’b01111,那么+15/4四舍五入就是5’b000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Starry丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值