我正在练习Verilog HDL编程,我无法解决这个简单的5阶段流水线处理器的问题。 我面临的问题是在LD指令中,它将来自存储器的双字加载到寄存器中。我无法保存每个阶段的输出数据。如何保存下一阶段的输出数据? 你能否在下面的代码中指出我的错误:
module Processor(
input CLK,
input [31:0] Instruction,
input [63:0] Data_In,
output [31:0] Inst_Addr,
output [31:0] Data_Addr,
output [63:0] Data_Out,
output Mem_Read,
output Mem_Write
);
////////////////////////////////////////////
reg[31:0] pc;
reg[31:0] regs[0:31];
reg[31:0] stage1;
reg[31:0] data_addr;
reg[63:0] data_out;
reg[63:0] data_in;
reg[31:0] inst;
reg [5:0] funct;
reg [5:0] opcode;
reg [4:0] rs;
reg [4:0] rt;
reg [4:0] rd;
reg [15:0] imm;
reg [4:0] shamt;
reg [1:0] inst_type;
reg [31:0] rs_value;
reg [31:0] rt_value;
reg [31:0] rd_value;
reg mem_read, mem_write;
initial begin
pc = 0;
end
assign Inst_Addr = pc;
always @ (posedge CLK) begin
/////////////IF//////////////////////////////
pc = pc + 4;
$display("%h Fetched instruction: %h", Inst_Addr, Instruction);
stage1 = Instruction;
//////////////ID//////////////////////////////
opcode[5:0] = stage1[31:26];
case(opcode)
0:
begin /* R-type */
assign rs = stage1[25:21];
assign rt = stage1[20:16];
assign rd = stage1[15:11];
assign shamt = stage1[10:6];
assign inst_type = 2'b00;
end
default:
begin /* I type */
assign rs = stage1[25:21];
assign rt = stage1[20:16];
assign shamt = stage1[10:6];
assign imm = stage1[15:0];
assign inst_type = 2'b01;
end
endcase
assign funct = stage1[5:0];
/////////////EX////////////////////////////////
// Fetching operands
assign rs_value = regs[rs];
assign rt_value = regs[rt];
// Performing operations
assign data_in = Data_In; // LD
case(inst_type)
2'b00: /* R-type */
begin
case(funct)
6'h20: /* DADD */
begin
assign rd_value = rs_value + rt_value;
end
6'h22: /* DSUB */
begin
assign rd_value = rs_value - rt_value;
end
6'h26: /* XOR */
begin
assign rd_value = ~ (rs_value | rt_value);
end
endcase
end
2'b01: /* I-type */
begin
case(opcode)
6'b110111: /* LD */
begin
assign data_addr = rs + imm;
assign rt_value = data_in;
assign mem_read = 1;
assign mem_write = 0;
end
6'b111111: /* SD */
begin
assign data_out = rt_value;
assign data_addr = rs + imm;
assign mem_write = 1;
assign mem_read = 0;
end
6'b011000: /* DADDI */
begin
assign rt_value = rs_value + imm;
end
endcase
end
endcase
//LD, SD,DADD, DSUB, XOR, DADDI, HALT
///////////////WB///////////////////////////////
case(inst_type)
2'b00: /* R-type */
begin
regs[rd] = rd_value;
end
2'b01: /* I-type */
begin
case(opcode)
6'b110111: /* LD */
begin
regs[rt] = rt_value;
end
6'b011000: /* DADDI */
begin
regs[rt] = rt_value;
end
endcase
end
endcase
end
//////////////MEM//////////////////////////////
assign Data_Addr = data_addr;
assign Data_Out = data_out; // SD
assign Mem_Write = mem_write;
assign Mem_Read = mem_read;
////////////////////////////////////////////////
endmodule
答案 0 :(得分:2)
通常,您不会将assign
与寄存器一起使用,而是valid cases explained here。以下不是其中之一:
reg data_in;
always @* begin
assign data_in = Data_In; // LD
end
应该是:
reg data_in;
always @* begin
data_in = Data_In; // LD
end
您的大多数变量赋值都发生在边缘触发的块内(隐含的触发器)。在这种情况下,您应该使用非阻塞分配(<=
)。你最终得到的是:
reg data_in;
always @(posedge clk) begin
data_in <= Data_In; // LD
end
我怀疑设计的某些部分(下方)应放入一个单独的组合部分,以便它们与地址解码器并行工作,而不会受到延迟的影响。
// Fetching operands
assign rs_value = regs[rs];
assign rt_value = regs[rt];
以下是更详细的内容
always @ (posedge CLK) begin
//...
case(opcode)
0: begin /* R-type */
assign rs = stage1[25:21];
assign rt = stage1[20:16];
assign rd = stage1[15:11];
assign shamt = stage1[10:6];
assign inst_type = 2'b00;
end
default: begin /* I type */
assign rs = stage1[25:21];
assign rt = stage1[20:16];
assign shamt = stage1[10:6];
assign imm = stage1[15:0];
assign inst_type = 2'b01;
end
endcase
//...
应该是:
always @ (posedge CLK) begin
//..
case(opcode)
0: begin /* R-type */
rs <= stage1[25:21];
rt <= stage1[20:16];
rd <= stage1[15:11];
shamt <= stage1[10:6];
inst_type <= 2'b00;
end
default: begin /* I type */
rs <= stage1[25:21];
rt <= stage1[20:16];
shamt <= stage1[10:6];
imm <= stage1[15:0];
inst_type <= 2'b01;
end
endcase
//...
虽然可以在边缘触发=
块中混合阻塞(<=
)和非阻塞(always
),但我会避免直到你更好地理解这样编码的含义。我个人从不混合这两种风格,以便代码更容易阅读。
因此,我将分离出在同一周期(组合)中发生的任何代码。这方面的一个例子是将以下内容与时钟进程并行添加。
always @* begin
// Fetching operands
rs_value = regs[rs];
rt_value = regs[rt];
end
always @(posedge CLK) begin
end