📜  多周期数据路径和流水线数据路径之间的差异(1)

📅  最后修改于: 2023-12-03 14:51:40.535000             🧑  作者: Mango

多周期数据路径和流水线数据路径之间的差异

在计算机组成原理中,数据路径的设计可以采用多周期和流水线两种方式。虽然二者都是完成相同的任务,但是它们在实现方式和性能方面存在显著的差异。下面详细介绍多周期数据路径和流水线数据路径之间的差异。

多周期数据路径

多周期数据路径将指令执行划分为多个时钟周期,每个时钟周期执行不同的功能,如取指、译码、执行、访存和写回等。经典的多周期数据路径通常由以下部分组成:

  • 指令寄存器(IR):存储当前执行的指令
  • 程序计数器(PC):存储下一条指令的地址
  • 寄存器堆:用于存储操作数和结果
  • ALU:执行算术和逻辑操作
  • 存储器:用于数据和指令的存储

多周期数据路径的性能相对较低,因为每个指令执行需要多个时钟周期。此外,在多周期数据路径中,不同指令的时钟周期数量可能是不同的,其执行过程受到了指令类型和数据冲突等因素的影响。

流水线数据路径

流水线数据路径采用流水线的方式将指令执行划分为多个阶段,每个阶段在同一时钟周期内执行不同的指令。经典的流水线数据路径通常由以下级别组成:

  • 取指阶段(IF):从内存中获取指令并将其存储在指令寄存器中
  • 译码阶段(ID):解码指令,确定执行何种操作,并读取操作数
  • 执行阶段(EX):执行指令
  • 访存阶段(MEM):访问内存(有些指令不需要进行该步骤)
  • 写回阶段(WB):写回操作数或结果

与多周期数据路径相比,流水线数据路径的性能更高,因为每个阶段的执行时间比多周期数据路径的时钟周期短。此外,在流水线数据路径中,不同指令的执行时间相同,具有更一致的性能。

总结

虽然多周期数据路径和流水线数据路径实现相同的功能,但它们的设计方式、性能和特点存在明显的差异。多周期数据路径适用于较简单的处理器,其实现简单,容易理解和设计;流水线数据路径用于更复杂的处理器,具有更高的性能和更好的适应性,但其设计和调试都更为困难。

代码片段

// 多周期数据路径实现
module multi_cycle_data_path
(
  input clk,
  input reset,
  input [31:0] inst,
  input [4:0] rs,
  input [4:0] rt,
  input [4:0] rd,
  output reg [31:0] out_data
);

// 时钟周期 1
wire [31:0] pc_next;
wire [31:0] inst_out;
wire [4:0] rs_out;
wire [4:0] rt_out;

assign pc_next = reset ? 32'h00000000 : pc + 4;

instruction_fetcher inst_fetcher(
  .clk(clk),
  .reset(reset),
  .pc(pc),
  .inst(inst_out)
);

instruction_decoder inst_decoder(
  .rs(rs_out),
  .rt(rt_out),
  .inst(inst_out)
);

assign inst_out = inst[pc[9:2]];

assign rs_out = inst[25:21];
assign rt_out = inst[20:16];

// 时钟周期 2
wire [31:0] alu_in1;
wire [31:0] alu_in2;
wire [31:0] alu_out;

assign alu_in1 = register_file[rs_out];
assign alu_in2 = register_file[rt_out];

alu alu_unit(
  .in1(alu_in1),
  .in2(alu_in2),
  .op(inst_out[5:2]),
  .out(alu_out)
);

// 时钟周期 3
wire [31:0] mem_in;
wire [31:0] mem_out;

assign mem_in = alu_out;

memory_access mem_access(
  .clk(clk),
  .write(inst_out[0]),
  .addr(alu_out),
  .in(mem_in),
  .out(mem_out)
);

// 时钟周期 4
wire [4:0] wb_reg;
wire [31:0] wb_data;

assign wb_reg = rd;
assign wb_data = mem_out;

write_back wb(
  .clk(clk),
  .enable(inst_out[1]),
  .data(wb_data),
  .reg(wb_reg),
  .out(out_data)
);

register_file reg(
  .read_a(rs_out),
  .read_b(rt_out),
  .write_data(wb_data)
);

assign register_file[0] = 32'h0;

endmodule

// 流水线数据路径实现
module pipeline_data_path
(
  input clk,
  input reset,
  input [31:0] inst,
  input [4:0] rs,
  input [4:0] rt,
  input [4:0] rd,
  output reg [31:0] out_data
);

// 取指阶段
wire [31:0] pc_next;
wire [31:0] inst_out;

assign pc_next = reset ? 32'h00000000 : pc + 4;

instruction_fetcher inst_fetcher(
  .clk(clk),
  .reset(reset),
  .pc(pc),
  .inst(inst_out)
);

pipeline_register if_id_reg(
  .in(inst_out),
  .out(id_inst),
  .clk(clk)
);

// 译码阶段
wire [31:0] id_inst;
wire [4:0] rs_out;
wire [4:0] rt_out;

instruction_decoder inst_decoder(
  .rs(rs_out),
  .rt(rt_out),
  .inst(id_inst)
);

register_file reg(
  .read_a(rs_out),
  .read_b(rt_out),
  .write_data(wb_data)
);

pipeline_register id_exe_reg(
  .in({rs_out, rt_out, imm_ext}),
  .out(exe_info),
  .clk(clk)
);

// 执行阶段
wire [31:0] exe_op1;
wire [31:0] exe_op2;
wire [31:0] alu_out;
wire [31:0] imm_ext;

assign exe_op1 = register_file[exe_info[0]];
assign exe_op2 = register_file[exe_info[1]];
assign imm_ext = sign_ext(exe_info[2]);

alu alu_unit(
  .in1(exe_op1),
  .in2(exe_op2),
  .op(id_inst[5:2]),
  .out(alu_out)
);

pipeline_register exe_mem_reg(
  .in(alu_out),
  .out(mem_op),
  .clk(clk)
);

// 访存阶段
wire [31:0] mem_op_out;

memory_access mem_access(
  .clk(clk),
  .write(id_inst[0]),
  .addr(alu_out),
  .in(mem_op),
  .out(mem_op_out)
);

pipeline_register mem_wb_reg(
  .in(mem_op_out),
  .out(wb_data),
  .clk(clk)
);

write_back wb(
  .clk(clk),
  .enable(id_inst[1]),
  .data(wb_data),
  .reg(id_inst[4:0])
  .out(out_data)
);

endmodule