在本博客中,将围绕许多设计中存在的非常有用的电路(桶形移位器电路)设计电路。将从最简单的方法开始实现固定位宽字的单向旋转桶形移位器,最后设计一个具有可参数化字宽的多功能双向桶形移位器电路。
桶形移位器是一种数字电路,可以将数据字移位指定位数,而不使用任何顺序逻辑,仅使用纯组合逻辑。它有一个控制输入,指定它移动的位数。桶移位器类似于移位寄存器(多位),不同之处在于寄存器的移位位被移回寄存器的另一端。例如,在右移操作中,从寄存器移出的 LSB 被移入 MSB。
桶形移位器适用于数字信号处理器和处理器。
实现桶形移位器的一种方法是作为一系列多路复用器,其中一个多路复用器的输出以取决于移位距离的方式连接到下一个多路复用器的输入。当使用一系列移位多路复用器实现桶式移位器时,对于不同的 k k k 值,每个多路复用器将一个字移位 2 k 2^k 2k 位位置( 1 , 2 , 4 , 8 , 16 , 32... 1,2,4,8,16,32... 1,2,4,8,16,32...)。复用级的数量与输入向量的宽度有关。
下图显示了 32 位字的右移桶形移位器。
通过更复杂的多路复用器和一些用于处理末端填充选项的额外电路,桶形移位器可以处理处理器指令集中的所有标准位移指令。
从最简单、最直接的方法开始,使用包含所有可能旋转组合的 case 语句,在 Verilog HDL 中对右移桶形移位器进行编程。
将设计一个简单的 8 位桶式移位器,它可以向右旋转任意位数。该电路有一个 8 位数据输入 data 和一个 3 位控制信号 amt,它指定要旋转的量。
该设计使用选定的信号分配语句来详尽地列出 amt 信号和相应旋转结果的所有组合。
使用 case 语句的 SystemVerilog 实现
`timescale 1ns / 10ps
module barrel_shifter_built_case(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
always_comb
begin
case (amt)
3'o0: out = data;
3'o1: out = {data[0], data[7:1]};
3'o2: out = {data[1:0], data[7:2]};
3'o3: out = {data[2:0], data[7:3]};
3'o4: out = {data[3:0], data[7:4]};
3'o5: out = {data[4:0], data[7:5]};
3'o6: out = {data[5:0], data[7:6]};
default out = {data[6:0], data[7]};
endcase
end
endmodule
Test bench and simulation
测试台使用 for 循环来迭代指示旋转量的信号的所有可能性。
`timescale 1ns / 10ps
module barrel_shifter_built_case_testbench;
logic [7:0] data;
logic [2:0] amt;
logic [7:0] out;
barrel_shifter_built_case uut(.*);
initial begin
for (byte i = 0; i < 8; ++i)
begin
data = 8'b1111_0000; amt = 3'(i); #10;
end
$stop;
end
endmodule
在示波器图像中,可以看到值如何移至右侧 amt 位、LSB 位,以及 MSB 如何填充溢出位,从而从左到右旋转信息。
#### RTL Elaborated Design Schematics
这种设计桶形移位器的方法意味着使用宽 3 至 8 多路复用器
该代码很简单,但意味着一个宽复用器,这使得合成变得困难并导致较大的传播延迟。
如果输入位数增加就会很麻烦。
或者,我们可以分阶段构建电路。在第 n n n 级中,输入信号要么直接传递到输出,要么向右旋转 2 n 2^n 2n 个位置。第 n n n级由 a m t amt amt 信号的第 n n n 位控制。
如果 a m 2 , a m 1 , a m 0 {am2, am1, am0} am2,am1,am0 是 a m t amt amt 信号的3位,则三级后总旋转量为 a m 2 ∗ 2 2 + a m 1 ∗ 2 + a m 0 am2 * 2^2 + am1 * 2 + am0 am2∗22+am1∗2+am0,这就是所需的旋转量。
`timescale 1ns / 10ps
module barrel_shifter_staged(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
logic [7:0] stage0;
logic [7:0] stage1;
always_comb
begin
//stage 0, shift 0 or 1 bit
if(amt[0]) begin
stage0 = {data[0], data[7:1]};
end else
begin
stage0 = data;
end
//stage 1, shift 0 or 2 bits
if(amt[1]) begin
stage1 = {stage0[1:0], stage0[7:2]};
end else
begin
stage1 = stage0;
end
//stage 2, shift 0 or 4 bits
if(amt[2]) begin
out = {stage1[3:0], stage1[7:4]};
end else
begin
out = stage1;
end
end
endmodule
或者使用“?:”三元表达式更紧凑但并不更清晰。
module barrel_shifter_staged(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
logic [7:0] s0;
logic [7:0] s1;
assign s0 = amt[0]? {data[0], data[7:1]} :data ;
assign s1 = amt[1]? {s0[1:0], s0[7:2]} :s0 ;
assign out = amt[2]? {s1[3:0], s1[7:4]} :s1;
endmodule
该电路现在使用一系列 2 比 1 多路复用器
The circuit now uses a series of 2 to 1 multiplexors
将通过添加一个新信号使我们的桶式移位器变得复杂一些,该信号允许控制位移位的方向。通过这个信号,可以告诉桶形移位器电路,是否希望输出的位向右或向左移动一定数量的位。
这个8位移位电路可以执行右旋转和左旋转操作。附加的 1 位控制信号 dir_lr 指定所需的方向。
首先,将使用一个右循环电路、一个左循环电路和 2 对 1 多路复用器来设计电路来选择所需的结果。
A possible SystemVerilog implementation
`timescale 1ns / 10ps
const logic ROTATE_LEFT = 1'b1;
const logic ROTATE_RIGHT = 1'b0;
module barrel_shifter_multifunction(
input logic [7:0] data,
input logic [2:0] amt,
input logic dir_lr, // 1 rotate left, 0 rotate right
output logic [7:0] out
);
logic [7:0] outl;
logic [7:0] outr;
barrel_shifter_right bsr(.*, .out(outr));
barrel_shifter_left bsl(.*, .out(outl));
assign out = dir_lr == ROTATE_LEFT ? outl:outr;
endmodule
// rotates amt bits of data to the right
module barrel_shifter_right(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
logic [7:0] s0;
logic [7:0] s1;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? {data[0], data[7:1]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? {s0[1:0], s0[7:2]} :s0 ;
//stage 1, shift 0 or 4 bits
assign out = amt[2]?{s1[3:0], s1[7:4]} :s1;
endmodule
// rotates amt bits of data to the left
module barrel_shifter_left(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
logic [7:0] s0;
logic [7:0] s1;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? { data[6:0], data[7]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? { s0[5:0], s0[7:6]} :s0 ;
//stage 1, shift 0 or 4 bits
assign out = amt[2]?{s1[3:0], s1[7:4]} :s1;
endmodule
该代码包括两个 8 位旋转移位器,一个向右,一个向左。使用 1 至 2 多路复用器,选择移位器之一的输出。
两个扩展移位器,每个移位器有3个1对2多路复用器,用于3位移位量信号所需的每一级。
Test-bench and simulation
测试台代码循环遍历两种可能的移位情况(左和右)的所有可能的移位量可能性。
module barrel_shifter_multifunction_testbench;
logic [7:0] data;
logic [2:0] amt;
logic [7:0] out;
logic dir_lr;
barrel_shifter_multifunction uut(.*);
initial begin
assign dir_lr = ROTATE_LEFT;
for (byte i = 0; i < 8; ++i)
begin
data = 8'b1111_0000; amt = 3'(i); #10;
end
assign dir_lr = ROTATE_RIGHT;
for (byte i = 0; i < 8; ++i)
begin
data = 8'b1111_0000; amt = 3'(i); #10;
end
$stop;
end
Simulation
范围视图首先显示原始字符串 8’b1111_0000 的所有左移情况,然后显示所有右移情况
综合设计使用 44 个cells
该电路也可以通过一个带有前置和后置反转电路的旋转移位器来实现,因此基本的多路复用功能可以执行这两种操作。反转电路要么传递原始输入,要么按位反转输入。
`timescale 1ns / 10ps
const logic ROTATE_LEFT = 1'b1;
const logic ROTATE_RIGHT = 1'b0;
// Barrel Shifter using a Rotate-right-shifter with pre- and post-reversing circuits
module barrel_shifter_multi_rev(
input logic [7:0] data,
input logic [2:0] amt,
input logic dir_lr, // 1 rotate left, 0 rotate right
output logic [7:0] out
);
logic [7:0] out_right; // output result
logic [7:0] reversed_outr; // reversed output rotation for right rotation
logic [7:0] reversed_data; // reversed input data for left rotation
// if rotate left use rotated input data
barrel_shifter_8_right bsr(.data((dir_lr == ROTATE_LEFT) ? reversed_data:data), .amt(amt) , .out(out_right));
// reverse input circuit
inverter_8 input_inverter(.data(data), .out(reversed_data));
// reverse output circuit
inverter_8 output_inverter(.data(out_right), .out(reversed_outr));
// if rotate right use rotated output
assign out = (dir_lr == ROTATE_LEFT) ? reversed_outr:out_right;
endmodule
// reverse 8-bit data
module inverter_8(
input logic [7:0] data,
output logic [7:0] out
);
// reverse a vector using right-to-left streaming with the default block size of 1 bit
assign out = {<<{data}};
endmodule
// rotates amt bits of data to the right
module barrel_shifter_8_right(
input logic [7:0] data,
input logic [2:0] amt,
output logic [7:0] out
);
logic [7:0] s0;
logic [7:0] s1;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? {data[0], data[7:1]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? {s0[1:0], s0[7:2]} :s0 ;
//stage 1, shift 0 or 4 bits
assign out = amt[2]?{s1[3:0], s1[7:4]} :s1;
endmodule
为了反转日期,使用从右到左的流式传输,默认块大小为 1 位:分配 out = {<<{data}};
Test bench
使用与前一个案例相同的测试平台。
`timescale 1ns / 10ps
module barrel_shifter_multi_rev_testbench;
logic [7:0] data;
logic [2:0] amt;
logic [7:0] out;
logic dir_lr;
barrel_shifter_multi_rev uut(.*);
initial begin
assign dir_lr = ROTATE_LEFT;
for (byte i = 0; i < 8; ++i)
begin
data = 8'b0000_0001; amt = 3'(i); #10;
end
assign dir_lr = ROTATE_RIGHT;
for (byte i = 0; i < 8; ++i)
begin
data = 8'b0000_0001; amt = 3'(i); #10;
end
$stop;
end
endmodule
Simulation
在本例中,使用字符串 8’b0000_0001 进行模拟
在精心设计的原理图中,可以观察到两个反转电路,它们反转输入和输出处的位位置。两个多路复用器根据移位方向和右移位电路选择非反相或反相输出输入。在这种情况下只需要一个移位器电路。
将扩展之前的电路,使其能够处理 32 位长的字。
`timescale 1ns / 10ps
const logic ROTATE_LEFT = 1'b1;
const logic ROTATE_RIGHT = 1'b0;
module barrel_shifter_32_multi_rev(
input logic [31:0] data,
input logic [4:0] amt,
input logic dir_lr, // 1 rotate left, 0 rotate right
output logic [31:0] out
);
logic [31:0] out_right;
logic [31:0] reversed_outr;
logic [31:0] reversed_data;
barrel_shifter_32_right bsr(.data((dir_lr == ROTATE_LEFT) ? reversed_data:data), .amt(amt) , .out(out_right));
// reverse input circuit
inverter_32 input_inverter(.data(data), .out(reversed_data));
// reverse output circuit
inverter_32 output_inverter(.data(out_right), .out(reversed_outr));
// if rotate right use rotated output
assign out = (dir_lr == ROTATE_LEFT) ? reversed_outr:out_right;
endmodule
// rotates amt bits of data to the right
module barrel_shifter_32_right(
input logic [31:0] data,
input logic [4:0] amt,
output logic [31:0] out
);
logic [31:0] s0;
logic [31:0] s1;
logic [31:0] s2;
logic [31:0] s3;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? {data[0], data[31:1]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? {s0[1:0], s0[31:2]} :s0 ;
//stage 2, shift 0 or 4 bits
assign s2 = amt[2]?{s1[3:0], s1[31:4]} :s1;
//stage 3, shift 0 or 8 bits
assign s3 = amt[3]?{s2[7:0], s2[31:8]} :s2;
//stage 4, shift 0 or 16 bits
assign out = amt[4]?{s3[15:0], s3[31:16]} :s3;
endmodule
// reverse 32-bit data
module inverter_32(
input logic [31:0] data,
output logic [31:0] out
);
// reverse a vector using right-to-left streaming with the default block size of 1 bit
assign out = {<<{data}};
endmodule
主要变化影响数据信号的长度以及指示要移位量的信号。在这种情况下,对于 32 位,移位信号必须为 5 位长才能循环到所有情况。
右移电路也发生了变化,必须添加新的移位级才能达到所需的 5 个级。
Test bench
测试程序还必须适应新的尺寸。
`timescale 1ns / 10ps
module barrel_shifter_32_multi_rev_testbench;
logic [31:0] data;
logic [4:0] amt;
logic [31:0] out;
logic dir_lr;
barrel_shifter_32_multi_rev uut(.*);
initial begin
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt = i; dir_lr = ROTATE_LEFT; #10;
end
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt =i; dir_lr = ROTATE_RIGHT; #10;
end
$stop;
end
endmodule
Simulation
在本例中,使用字符串 32’b0000_0000_0000_0000_0000_0000_0000_0001 进行模拟
详细设计的方案与针对8位详细设计的方案类似,可以看到数据总线的大小发生了变化,它们已扩大到新的大小。
扩展桶形移位器块,发现由于需要新的移位级,多路复用器的数量有所增加。
可以使用 32 位长度执行与之前相同的练习,但在这种情况下,我们不使用前后反转电路,而是使用两个桶形移位器,一个向右,一个向左,可通过另一个多路复用器进行选择。
`timescale 1ns / 10ps
const logic ROTATE_LEFT = 1'b1;
const logic ROTATE_RIGHT = 1'b0;
module barrel_shifter_32_multi(
input logic [31:0] data,
input logic [4:0] amt,
input logic dir_lr, // 1 rotate left, 0 rotate right
output logic [31:0] out
);
logic [31:0] out_right;
logic [31:0] out_left;
barrel_shifter_32_right bsr(.data(data), .amt(amt) , .out(out_right));
barrel_shifter_32_left bsl(.data(data), .amt(amt) , .out(out_left));
// if rotate right use rotated output
assign out = (dir_lr == ROTATE_LEFT) ? out_left:out_right;
endmodule
// rotates amt bits of data to the right
module barrel_shifter_32_right(
input logic [31:0] data,
input logic [4:0] amt,
output logic [31:0] out
);
logic [31:0] s0;
logic [31:0] s1;
logic [31:0] s2;
logic [31:0] s3;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? {data[0], data[31:1]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? {s0[1:0], s0[31:2]} :s0 ;
//stage 2, shift 0 or 4 bits
assign s2 = amt[2]?{s1[3:0], s1[31:4]} :s1;
//stage 3, shift 0 or 8 bits
assign s3 = amt[3]?{s2[7:0], s2[31:8]} :s2;
//stage 4, shift 0 or 16 bits
assign out = amt[4]?{s3[15:0], s3[31:16]} :s3;
endmodule
// rotates amt bits of data to the left
module barrel_shifter_32_left(
input logic [31:0] data,
input logic [4:0] amt,
output logic [31:0] out
);
logic [31:0] s0;
logic [31:0] s1;
logic [31:0] s2;
logic [31:0] s3;
//stage 0, shift 0 or 1 bit
assign s0 = amt[0]? {data[30:0], data[31]} :data ;
//stage 1, shift 0 or 2 bits
assign s1 = amt[1]? {s0[29:0], s0[31:30]} :s0 ;
//stage 2, shift 0 or 4 bits
assign s2 = amt[2]?{s1[27:0], s1[31:28]} :s1;
//stage 3, shift 0 or 8 bits
assign s3 = amt[3]?{s2[23:0], s2[31:24]} :s2;
//stage 4, shift 0 or 16 bits
assign out = amt[4]?{s3[15:0], s3[31:16]} :s3;
endmodule
module barrel_shifter_32_multi_testbench;
logic [31:0] data;
logic [4:0] amt;
logic [31:0] out;
logic dir_lr;
barrel_shifter_32_multi uut(.*);
initial begin
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt = i; dir_lr = ROTATE_LEFT; #10;
end
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt =i; dir_lr = ROTATE_RIGHT; #10;
end
$stop;
end
endmodule
该代码包括之前的右桶形移位器,并在左侧的相反方向添加一个新的移位器。根据地址信号 dir_lr 选择其中之一。
扩展两个桶形移位器,可以看到该解决方案比前一个解决方案需要更多的多路复用器。
Test bench
可以使用与之前的情况类似的测试平台,但更改被测单元 uut 的实例化。
module barrel_shifter_32_multi_testbench;
logic [31:0] data;
logic [4:0] amt;
logic [31:0] out;
logic dir_lr;
barrel_shifter_32_multi uut(.*);
initial begin
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt = i; dir_lr = ROTATE_LEFT; #10;
end
for (int i = 0; i < 32; ++i)
begin
data = 32'b1; amt =i; dir_lr = ROTATE_RIGHT; #10;
end
$stop;
end
endmodule
Simulation
在模拟中,可以以图形方式看到偏移如何影响高位的单个位。
综合设计与前一个类似,有 198 cells
最后制作一个具有可参数化数据长度的版本。
在这个参数化的桶形移位器中,输入宽度可以由参数 N N N 指定。输入的宽度将为 2 N 2^N 2N
`timescale 1ns / 1ps
const logic ROTATE_LEFT = 1'b1;
const logic ROTATE_RIGHT = 1'b0;
// The width of input will be 2^N ie N=5 width 32
module barrel_shifter_N #(parameter N=4) (
input logic [2**N-1:0] data,
input logic [N-1:0] amt,
input logic dir_lr, // 1 rotate left, 0 rotate right
output logic [2**N-1:0] out
);
localparam WIDTH = 2**N;
logic [WIDTH-1:0] out_right;
logic [WIDTH-1:0] reversed_outr;
logic [WIDTH-1:0] reversed_data;
barrel_shifter_N_right #(.N(N)) bsr(.data((dir_lr == ROTATE_LEFT) ? reversed_data:data), .amt(amt) , .out(out_right));
// reverse input circuit
inverter_width #(.WIDTH(WIDTH)) input_inverter(.data(data), .out(reversed_data));
// reverse output circuit
inverter_width #(.WIDTH(WIDTH))output_inverter(.data(out_right), .out(reversed_outr));
// if rotate right use rotated output
assign out = (dir_lr == ROTATE_LEFT) ? reversed_outr:out_right;
endmodule
// rotates amt bits of data to the right staged implementation
//
module barrel_shifter_N_right #(parameter N = 4)(
input logic [2**N-1:0] data,
input logic [N-1:0] amt,
output logic [2**N-1:0] out
);
localparam WIDTH = 2**N;
logic [N-1:0][WIDTH-1:0] stage_out;
generate
genvar stage ;
assign stage_out[0] = amt[0] ? { data[0], data[WIDTH-1:1]} : data;
for (stage = 1; stage < N ; ++stage)
begin
assign stage_out[stage] = amt[stage] ?
{stage_out[stage-1][stage**2:0], stage_out[stage-1][WIDTH-1:2**stage]}
: stage_out[stage -1];
end
assign out = stage_out[N-1];
endgenerate
endmodule
// reverse 32-bit data
module inverter_width #(parameter WIDTH=32) (
input logic [WIDTH-1:0] data,
output logic [WIDTH-1:0] out
);
// reverse a vector using right-to-left streaming with the default block size of 1 bit
assign out = {<<{data}};
endmodule
module barrel_shifter_N_testbench;
localparam N = 3;
localparam WIDTH = 2**N;
logic [WIDTH-1:0] data;
logic [N-1:0] amt;
logic [WIDTH-1:0] out;
logic dir_lr;
barrel_shifter_N #(.N(N)) uut(.*);
initial begin
for (int i = 0; i < WIDTH; ++i)
begin
data = 5; amt = i; dir_lr = ROTATE_LEFT; #10;
end
for (int i = 0; i < WIDTH; ++i)
begin
data = 5; amt =i; dir_lr = ROTATE_RIGHT; #10;
end
$stop;
end
endmodule
由于现在移动数据的阶段数是可变的,可以使用生成块在循环中生成不同的必要阶段。