图解
先放一张图:
原理就是把32位的数分成4部分,每部分8位。每个周期都只计算一个部分,共四个周期计算出结果。比如a+b:
第一个周期,a的最低8位与b的最低8位相加,得出的结果(包括进位)传递给第一级流水线寄存器,尚还没有计算的a的高24位与b的高24位也原样传递给第一级流水线寄存器;
第二个周期,a的次低8位与b的次低8位相加,得出的结果传递给第二级流水线寄存器(与低位的结果拼接),尚还没有计算的a的高16位与b的高16位也原样传递给第二级流水线寄存器;
第三个周期,a的次高8位与b的次高8位相加,得出的结果传递给第三级流水线寄存器(与低位的结果拼接),尚还没有计算的a的高8位与b的高8位也原样传递给第三级流水线寄存器;
第四个周期,a的最高8位与b的最高8位相加,得出的结果传递给第四级流水线寄存器(与低位的结果拼接),此时已全部计算完成,得到整个输出结果和进位结果。
四级流水线加法器,从低位到高位,每周期计算8位,共需4周期出结果。
流水线暂停仿真图
流水线暂停:简单说就是把要暂停的阶段前面的所有流水线寄存器的使能信号关掉,使时钟跳变时,暂停的阶段前面的所有流水线寄存器中的内容保持(这样数据就不往后传),达到了暂停的效果。而暂停阶段后面的流水线仍在流动。
一个暂停仿真图:
流水线刷新仿真图
流水线的刷新一般和暂停一起使用,当然也可以单独使用。
(1)与暂停一起使用,作用:清空暂停阶段的后续一个流水线寄存器,避免重复无意义的运算。
上文说到,若要暂停某一阶段,则其前面的所有阶段都要一起暂停才行,但是其后面的阶段仍在流动。这会引发什么问题呢?假设要暂停第二阶段(a的次低8位与b的次低8位相加),则其前面的流水线寄存器(第一级流水线寄存器)使能信号置0,在暂停的过程中,每一次时钟上升沿(使用posedge clk)时,流水线的第二阶段都会把第一级流水线寄存器中的数据拿来运算,并在下一时钟上升沿传递给第二级流水线寄存器,因为前面没有新的数据传过来,所以流水线的第二阶段会一直做重复的计算,算完之后往后传递(第二级流水线寄存器),所以我们不能让他的结果继续往后传了(其实让他传也没影响,但我们习惯于把它刷掉,防止占用一些资源),故把第二级流水线寄存器的clear信号置1,每次时钟上升沿,第二级流水线寄存器的值全清0,因此第三阶段取到的值也都是0。
(2)单独使用,作用:刷新指定流水线寄存器,处于该流水线寄存器的值全部清0,即当前运行到此流水线寄存器前一阶段的那条加法的数据被清空(无效数据)了,它最后也就没有结果产生了。
一个单独使用刷新的仿真图:
Verilog源代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/04/19 20:41:31
// Design Name:
// Module Name: stallable_pipeline_adder
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//32位4级流水线加法器
module stallable_pipeline_adder(
input clk,
input rst, //复位
input [3:0] stop, //暂停(注意当暂停某一级时,当前级与之前的级都被暂停,其后的级继续流动
// 0000不暂停,0001暂停第一级,0011暂停第二级,0111暂停第三级,1111暂停第四级)
input [3:0] refresh, //刷新 (刷新哪一级,哪一级的数据就变无效,通过拉低 pipeX_valid信号)
//(0000不刷新,0001刷新第一级,0010刷新第二级,0100刷新第三级,1000刷新第四级)
input validin, //输入是否有效
input [31:0] cin_a, //输入a
input [31:0] cin_b, //输入b
input c_in, //初始进位输入
input out_allow, //是否允许输出
output validout, //当前输出是否有效
output [31:0] sum_out, //流水线结果输出
output c_out //流水线进位输出
);
//各级流水线间临时寄存器(保存未计算的高位输入)
reg [23:0] temp_a_t1,temp_b_t1; //第一、二级间
reg [15:0] temp_a_t2,temp_b_t2; //第二、三级间
reg [7:0] temp_a_t3,temp_b_t3; //第三、四级间
//各级流水线输出寄存器
reg [7:0] sum_out_t1; //第一级
reg [15:0] sum_out_t2; //第二级
reg [23:0] sum_out_t3; //第三级
reg [31:0] sum_out_t4; //第四级
//各级流水线进位输出寄存器
reg c_out_t1, c_out_t2, c_out_t3, c_out_t4;
// 记录每一级当前数据是否有效
reg pipe1_valid;
reg pipe2_valid;
reg pipe3_valid;
reg pipe4_valid;
//pipeline level 1 , calculate 7:0 bits of input, store 31:8 bits of input
wire pipe1_allowin; //表示pipe1能否被上一级刷新
wire pipe1_ready_go; //表示pipe1是否可以用于刷新下一级
wire pipe1_to_pipe2_valid;//表示pipe1能否进入pipe2
assign pipe1_ready_go = !stop[0]; //如果没有暂停,那么就可以Go
// 如果pipe1中的值已经无效,或者这一轮一定会传给下一个,那么就可以进行接收
assign pipe1_allowin = !pipe1_valid || pipe1_ready_go && pipe2_allowin;
// 如果pipe1有效,并且pipe1可以进行传输,那么pipe1_to_pipe2_valid可以进行。
assign pipe1_to_pipe2_valid = pipe1_valid && pipe1_ready_go;
always@(posedge clk)begin
if(rst)begin
pipe1_valid <= 1'b0; //如果需要复位,那么pipe1_valid变为0,表示pipe1中的值不再有效
end
else if(refresh[0])begin
pipe1_valid <= 1'b0; //如果需要刷新,那么pipe1_valid变为0,表示pipe1中的值不再有效
end
//既不复位也不刷新,当pipe1允许输入,若当前输入有效,pipe1_valid为1,值有效,否则0,值无效
else if(pipe1_allowin)begin
pipe1_valid <= validin;
end
//如果输入有效,pipe1能接收,则进行低位运算并保留高位
if(validin && pipe1_allowin) begin
{c_out_t1,sum_out_t1} <= cin_a[7:0] + cin_b[7:0] + c_in;
temp_a_t1 <= cin_a[31:8];
temp_b_t1 <= cin_b[31:8];
end
end
//pipeline level 2 , calculate 15:8 bits , store 31:16 bits
wire pipe2_allowin;
wire pipe2_ready_go;
wire pipe2_to_pipe3_valid;
assign pipe2_ready_go = !stop[1];
assign pipe2_allowin = !pipe2_valid || pipe2_ready_go && pipe3_allowin;
assign pipe2_to_pipe3_valid = pipe2_valid && pipe2_ready_go;
always@(posedge clk)begin
if(rst)begin
pipe2_valid <= 1'b0;
end
else if(refresh[1])begin
pipe2_valid <= 1'b0;
end
else if(pipe2_allowin)begin
pipe2_valid <= pipe1_to_pipe2_valid;
end
if(pipe1_to_pipe2_valid && pipe2_allowin) begin
{c_out_t2,sum_out_t2} <= {{1'b0,temp_a_t1[7:0]} + {1'b0,temp_b_t1[7:0]} + c_out_t1, sum_out_t1};
temp_a_t2<=temp_a_t1[23:8];
temp_b_t2<=temp_b_t1[23:8];
end
end
//pipeline level 3 , calculate 23:16 bits , store 31:24 bits
wire pipe3_allowin;
wire pipe3_ready_go;
wire pipe3_to_pipe4_valid;
assign pipe3_ready_go = !stop[2];
assign pipe3_allowin = !pipe3_valid || pipe3_ready_go && pipe4_allowin;
assign pipe3_to_pipe4_valid = pipe3_valid && pipe3_ready_go;
always@(posedge clk)begin
if(rst)begin
pipe3_valid <= 1'b0;
end
else if(refresh[2])begin
pipe3_valid <= 1'b0;
end
else if(pipe3_allowin)begin
pipe3_valid <= pipe2_to_pipe3_valid;
end
if(pipe2_to_pipe3_valid && pipe3_allowin)begin
{c_out_t3,sum_out_t3} <= {{1'b0,temp_a_t2[7:0]} + {1'b0,temp_b_t2[7:0]} + c_out_t2, sum_out_t2};
temp_a_t3<=temp_a_t2[15:8];
temp_b_t3<=temp_b_t2[15:8];
end
end
//pipeline level 4 , calculate 31:24 bits,and then output
wire pipe4_allowin;
wire pipe4_ready_go;
assign pipe4_ready_go = !stop[3];
assign pipe4_allowin = !pipe4_valid || pipe4_ready_go && out_allow;
always@(posedge clk)begin
if(rst)begin
pipe4_valid <= 1'b0;
end
else if(refresh[3])begin
pipe4_valid <= 1'b0;
end
else if(pipe4_allowin)begin
pipe4_valid <= pipe3_to_pipe4_valid;
end
if(pipe3_to_pipe4_valid && pipe4_allowin)begin
{c_out_t4,sum_out_t4} <= {{1'b0,temp_a_t3[7:0]} + {1'b0,temp_b_t3[7:0]} + c_out_t3, sum_out_t3};
end
end
//output
assign validout = pipe4_valid && pipe4_ready_go;
assign sum_out = sum_out_t4;
assign c_out = c_out_t4;
endmodule
仿真顶层文件代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/04/28 00:06:05
// Design Name:
// Module Name: sim
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sim();
reg clk,rst;
reg [3:0] stop,refresh;
reg validin;
reg [31:0] cin_a,cin_b;
reg c_in;
reg out_allow;
wire validout;
wire [31:0] sum_out;
wire c_out;
stallable_pipeline_adder U1(.clk(clk),.rst(rst),.stop(stop),.refresh(refresh),
.validin(validin), .cin_a(cin_a),.cin_b(cin_b),.c_in(c_in),
.out_allow(out_allow),.validout(validout),.sum_out(sum_out),.c_out(c_out) );
initial begin
clk = 1;
rst = 1;
stop = 0;
refresh = 0;
validin = 1;
cin_a = 0;
cin_b = 0;
c_in = 0;
out_allow = 1;
#10 rst = 0;
#390 stop = 4'b0111; //第10周期(即第400单位时)暂停流水线第二级,持续两周期
#80 stop = 4'b0000;
#360 refresh = 4'b0010; //第20周期刷新流水线第三级
#40 refresh = 4'b0000;
end
always #20 clk = ~clk; //20单位翻转一次,40单位构成一个周期
//每个周期,输入改变
always #40 cin_a = {$random} % 66666; //产生0--666665之间的随机数
always #40 cin_b = {$random} % 66666;
endmodule
计算机组成原理实验一要求,在此记录一下,如有疑问或错误之处,欢迎留言指出!
参考资料:CQU计算机组成原理实验一指导书