Menu Close

Verilog 移位操作符

Verilog 提供了移位运算符方便移位操作,移位操作符有四种分别是逻辑左移<<逻辑右移>> 算术左移<<<算术右移>>>。移位操作符是双目操作数运算符。

1. 逻辑左移<<

左操作数按照右操作数(RHS) 指定的位数左移左操作数移出的位数丢弃,右边补零;

例:

wire [7:0] a,b;

assign a = 7;

assign b = a << 3;

由于a的2进制数是8’b0000_0111, 左移三位后的数值是8’b0011_1000; 因此b的值为8’b0011_1000。

在有效位移出之前相当于乘以23

2. 逻辑右移>>

左操作数按照右操作数(RHS) 指定的位数右移左操作数移出的位数丢弃,左边补零;

例:

wire [7:0] a,b;

assign a = 31;

assign b = a >> 3;

由于a的2进制数是8’b0001_1111, 右移三位后的数值是8’b0000_0011; 因此b的值为8’b0000_0011,十进制3。

相当于除以23 取整。

例:

设计文件 logic_shift.v

module logic_shift
(
    input  [7:0]  a,b,
    output [15:0] c,d
);
 
assign c = a << b;
assign d = a >> b;
 
endmodule

 

仿真文件tb.v

`timescale 1ns/1ps
 
module tb
(
 
);
 
 
reg  [7:0]  a, b;
 
wire [15:0] c;
wire [15:0] d;
 
initial 
begin
    a = 'b0;
    b = 'b0;
    #10
 
    a = 'd15;
    b = 'd3;
    #10
 
    a = 'd31;
    b = 'd4;
end
 
logic_shift logic_shift_dut
(
    .a (a),
    .b (b),
    .c (c),
    .d (d)
);
 
endmodule

 

Modelsim 仿真波形

%title插图%num

从上图可以看出当a==8’b0001_1111,且左移4位时,主要c的位宽足够宽,左移时并没有将溢出的位数丢弃。可见在左移前,右边的变量会根据赋值语句左边的变量自动调整宽度。

3. 算术左移 <<<

算术左移将被移位的操作数左移指定的位数,左边移出的位数丢弃(注意赋值语句位宽调整),右边补零。除了被移位的操作数按有符号数,其它与<<几乎没有差别。

4. 算术右移 >>>

算术右移将被移位的操作数右移指定的位数,右边移出的位数丢弃(注意赋值语句位宽调整),左边补符号位。一般有符号数的最高位(2进制)为符号位,因此右移时,符号位在左边不断复制并右移。

例:

wire signed [7:0] a;

wire signed [7:0] c;

assign c = a >>> 3;

则c 的值实际为 {a[7],a[7],a[7],a[7:3]};

要实现算术右移的结果,被移位的操作数必须定义为有符号数,如:

wire signed [7:0] a; 否则其结果与>>一致。

例:

设计文件 arith_shift.v:

module arith_shift
(
    input   signed [7:0]  a,b,
 
    output  signed [3:0]  test,
    output  signed [15:0] c,d
);
 
assign test = a <<< b;
assign c = a <<< b;
assign d = a >>> b;
 
endmodule

 

仿真文件 tb.v

`timescale 1ns/1ps
 
module tb
(
 
);
 
reg  [7:0]  a, b;
 
wire [3:0]  test;
wire [15:0] c;
wire [15:0] d;
 
initial 
begin
    a = 'b0;
    b = 'b0;
    #10
 
    a = 'd15;
    b = 'd3;
    #10
 
    a = 'd31;
    b = 'd4;
    #10 
 
    a = -'d3;
    b = 'd4;
    #10
 
    b = 'd6;
    #10
 
    b = 'd8;
    #10
 
    b = 'd10;
    #10
 
    b = 'd11;
    #10
 
    b = 'd12;
    #10
 
    b = 'd13;
    #10
 
    b = 'd14;
    #10
 
    b = 'd15;
    #10
 
    b = 'd16;
end
 
 
arith_shift   arith_shift_dut
(
    .a    (a),
    .b    (b),
    .test (test),
    .c    (c),
    .d    (d)
);
 
endmodule

 

Modelsim 仿真波形

%title插图%num

%title插图%num

从上图可以看出,除了复制符号位扩展,算术左移与逻辑左移几乎没有区别,右移自动填充符号位。

 

对应视频:

Posted in FPGA, FPGA 教材教案, Verilog, Verilog

2 Comments

  1. wangff

    学习本篇文章后有几点收获,1、逻辑左移<>,代表除以2的幂次方;3、在左移前,右边的变量会根据赋值语句左边的变量自动调整宽度;4、算术右移 >>>,右边移出的位数丢弃(注意赋值语句位宽调整),左边补符号位;5、赋值语句,当算数移位中的位宽调整时,当左边操作数位宽右边操作数位宽时,右边操作数先扩展符号位,与左边操作数位宽对齐,然后再进行移位。

  2. 匿名

    纠正:1、逻辑左移,代表乘以2的幂次方;2、逻辑右移,代表除以2的幂次方;3、算数右移,左边补充符号位(注意赋值语句位宽调整);4、赋值语句,当左边操作数位宽大于右边操作数位宽时,右边操作数先扩展符号位,与左边操作数位宽对齐,然后再进行移位;当左边操作数位宽小于右边操作数位宽时,右边操作数先进行移位,最后截取左边操作数位宽长度;

发表回复

相关链接