risc-v处理器设计

news/2024/10/22 16:30:12/

RISC-V CPU设计-绪论及单周期CPU设计

  • 绪论
  • 第一章:单周期CPU设计
    • ①:pc_reg
    • ②:IF阶段
    • ③:ID阶段
    • ③ :EX阶段
    • ④:MEM阶段
    • ⑤:WB阶段
    • 结语

绪论

为了更好的学习和理解RISC-V处理器设计,在阅读了计算机组成与设计软硬件接口RISC-V这本书后,我决定自己动手写一个简单的CPU,下面我将分享整个设计的过程,本过程是我个人学习的一个记录,仅供参考,我的代码水平不高,作为一名跨专业考生我只是学习了半年的的verilog,代码质量肯定不是很高,顺便一提,整个CPU基本都是我根据书上的图来搭建的,可能代码写的有些冗余? 因为做完以后我发现有些步骤确实有点多余,但逻辑应该是没问题的,基本的32I指令都能跑通。

我的学习过程是先以单周期CPU开始,在设计完单周期CPU后在进行5阶流水线CPU的设计,我的5阶流水线CPU中解决了数据冒险和控制冒险,其中数据冒险用forwarding来实现,还有一张load—use型数据冒险用stall技术来实现。控制冒险我做的比较简单,就是静态分支预测,我使用的是Nottaken。

整个设计中每行代码都是我自己手打的,我写代码的经验不是很丰富,我自己也感觉我在设计的过程中有些地方做的有亿点点复杂,在今后的设计中我定会吸取教训,变得更加老练。我目前的CPU设计只是简单的一小步,在写完这篇报告后,我就要去学习总线,chache,串口,gpio等一些外围设备了,再之后就是学习一下脚本语言,我在github上看到别人写的程序都是脚本话的,验证起来很方便,而我自己验证的过程很笨很原始,就是把不同种类的指令,在一个反汇编网站上转成16进制机器码,写入txt文件在读取, 这种方法确实费时费力,希望在今后能学好一门脚本语言。

最后,我还是力荐这本书,计算机组成与设计-软/硬件接口,RISC-V版。我在这本书中真正的学会了很多知识,大家可以可以在youtube上找 朱宗贤老师的这们课的讲解,讲的很好。

第一章:单周期CPU设计

单周期的CPU设计很简单,我做这个的过程就是照着书上的几张图片,直接就完成了整个数据通路,控制信号生成的搭建,看懂CPU原理图对我们的设计真的十分重要,照着图,把每一个组件都搭建好,最后连线就可以完成。我在接下来的阐述中也是以这个为前提的。
​​​​​​单周期CPU结构图
来看这张图,揭示了单周期CPU的结构,分别是PC寄存器,指令内存,寄存器堆,ALU单元,数据内存,以及一些控制单元,通过分组我们可以把他对应成为CPU的五个阶段,分别是取指,译码,执行,访存,写回。

在一开始设计代码的时候我最大的问题是不知道什么时候用组合逻辑,什么时候用时序逻辑,这个问题其实在单周期阶段还好,到了流水线CPU阶段,更是一直困扰了我许久,这个其实要说也很简单,但我觉得还是要自己在设计的时候多经历点坑自己理解才算是真正的理解了这种简单CPU的机制。在这里插入图片描述
这是我划分后的结构 ,接下来我们按顺序一个一个完成设计

①:pc_reg

我们看到,pc_reg是用来计算下一个pc的地址,并且在每个时钟上升沿,把下一个PC的地址发出去给IF,那么就引出了个两个变量,next_pc和now_pc

​
module pc_reg(input                   clk,input                   rst_n,
//Unconditonal jump:jal jalrinput                   jump_flag_i,input   [31:0]          jump_addr_i,
//conditional branch  :beq bne blt bginput                   Branch, input   [31:0]          branch_addr_i,output  reg [31:0]      now_pc
);reg [31:0]  next_pc;always @(*)beginif(jump_flag_i) beginnext_pc = jump_addr_i;endelse if(Branch)beginnext_pc = branch_addr_i;endelse beginnext_pc = now_pc+4;end
end always@ (posedge clk or negedge rst_n) beginif(!rst_n)beginnow_pc <= 32'h0;endelse beginnow_pc <= next_pc;endend
endmodule

②:IF阶段

IF阶段根据pc_reg所输出的pc值,将其传递给inst_rom,读inst_rom,取得指令

里还是写成两个不同的控制信号好一些。

​
module IF(input   rst_n,//from pc_reg;input   [31:0]  pc_if_i,//from inst_rominput   [31:0]   inst_if_i,//to inst_romoutput  reg [31:0]  pc_if_o,            // as address to access memoutput  reg         mem_en,// to exoutput  reg [31:0]  inst_if_o      //data   from mem
);
always@(*) beginif(!rst_n)beginpc_if_o = 32'h0;inst_if_o = 32'h0;mem_en = 1'b0;endelse beginpc_if_o = pc_if_i;inst_if_o = inst_if_i;mem_en = 1'b1;end
endendmodule​

③:ID阶段

此过程主要是搞清楚,RISC-V指令的译码过程,首先,要知道译码是干什么,我理解的译码,就是通过翻译输入的机器指令,来生成后续阶段的控制信号的。我觉得是单周期CPU设计过程中最关键的过程。
​​在这里插入图片描述

可以看到共有R-type, I-type, S-type, SB-type, U-type, UJ-type t六种类型的指令,指令的类型相同,说明指令的每个阶段操作类似,但功能不一定类似,生成的控制信号也就不一定一样。为了更好的不同的ALU控制和控制信号,我把这六种指令又细分了一下,也就是根据Opcode,进一步划分,划分结果如下
在这里插入图片描述
在译码阶段,我们主要干四件事,1、:控制信号生成 2:读寄存器堆3:立即数扩展。4:ALU控制信号(让ALU做不同的事)
1:控制信号的生成,通过risc-v的汇编指令的opcode,也就是instruction[6:0],我们基本知道这个指令是用来干什么的,具体我们可以看图3,也就是说根据opcode,我们就能生成控制信号

// control
assign MemRead_id_o  = (instr_id_i[6:0]   == `INST_TYPE_IL);
assign MemtoReg_id_o = (instr_id_i[6:0]   == `INST_TYPE_IL);
assign ALUOp    = ((instr_id_i[6:0]  == `INST_TYPE_IL) || (instr_id_i[6:0] == `INST_TYPE_S) || (instr_id_i[6:0] == `INST_TYPE_JALR)||(instr_id_i[6:0] == `INST_TYPE_U)) ? 2'b00:(instr_id_i[6:0] == `INST_TYPE_SB) ? 2'b01 :(instr_id_i[6:0] == `INST_TYPE_R)||(instr_id_i[6:0] == `INST_TYPE_II) ? 2'b10 : 2'b11;
assign MemWrite_id_o = (instr_id_i[6:0] == `INST_TYPE_S);
assign ALUSrc = (instr_id_i[6:0] == `INST_TYPE_R) || (instr_id_i[6:0] == `INST_TYPE_SB);
assign RegWrite_id_o = (instr_id_i[6:0] == `INST_TYPE_R) || (instr_id_i[6:0] == `INST_TYPE_IL)|| (instr_id_i[6:0] == `INST_TYPE_U)||(instr_id_i[6:0] == `INST_TYPE_II)||(instr_id_i[6:0] == `INST_TYPE_JALR)||(instr_id_i[6:0]== `INST_TYPE_JAL); 

2:读寄存器堆:当寄存器地址是0是,读得的值为0,读操作一定是组合逻辑,写操作一定是时序逻辑


//reg read
assign rd_data1 = (reg_addr1==5'd0)?  0:regs[reg_addr1];
assign rd_data2 = (reg_addr2==5'd0)?  0:regs[reg_addr2];endmodule

3:立即数扩展:不同的指令类型,立即数的类型也不同
在这里插入图片描述

// immgen
assign R_type = (instr_id_i[6:0] == `INST_TYPE_R);
assign I_type = (instr_id_i[6:0] == `INST_TYPE_II) ||(instr_id_i[6:0] == `INST_TYPE_IL)||(instr_id_i[6:0] == `INST_TYPE_JALR) ;
assign S_type = (instr_id_i[6:0] == `INST_TYPE_S);
assign SB_type= (instr_id_i[6:0] == `INST_TYPE_SB);
assign U_type = (instr_id_i[6:0] == `INST_TYPE_U);
assign UJ_type= (instr_id_i[6:0] == `INST_TYPE_JAL);assign I_imme = {{20{instr_id_i[31]}},instr_id_i[31:20]};
assign S_imme = {{20{instr_id_i[31]}},instr_id_i[31:25],instr_id_i[11:7]};
assign SB_imme= {{20{instr_id_i[31]}},instr_id_i[7],instr_id_i[30:25],instr_id_i[11:8],1'b0};
assign U_imme = {instr_id_i[31:12],12'b0};
assign UJ_imme= {{12{instr_id_i[31]}},instr_id_i[19:12],instr_id_i[20],instr_id_i[30:21],1'b0};assign imme = I_type?   I_imme  :S_type?   S_imme  :SB_type?  SB_imme :U_type?   U_imme  :UJ_type?  UJ_imme :32'd0;

4、ALU控制信号
根据不同的instruction[6:0],在控制信号中可以生成不同的ALUOp,而不同的ALUop可以初步确定是如何使用ALU的,在根据进一步的fun3,fun7,就能确定ALUCtrl,从而传递到ex阶段去,让ALU执行不同的功能

                                                                           
//ALUCtrl
always@(*) beginif(!rst_n)beginALUCtrl_id_o = 4'b0000;endelse begincase(ALUOp)2'b00:ALUCtrl_id_o = 4'b1000; // load or store , so use add 2'b01:begin    //condition branch case(fun3_id_o)3'b000:ALUCtrl_id_o = 4'b0001;//beq,  branch if equal3'b001:ALUCtrl_id_o = 4'b0010;//bne,  branch if not equal3'b100:ALUCtrl_id_o = 4'b0011;//blt,  branch if less than3'b101:ALUCtrl_id_o= 4'b0100;//bge, branch if greater than3'b110:ALUCtrl_id_o= 4'b0101;//bltu3'b111:ALUCtrl_id_o= 4'b0110;//bgeudefault : ALUCtrl_id_o= 4'b0000; // do nothingendcaseend2'b10:begin//R_type  or II_typecase(fun3_id_o)3'b000:beginif(instr_id_i[6:0] == `INST_TYPE_R)beginALUCtrl_id_o = (fun7[5] == 0)? 4'b1000:4'b1001;   //fun7==0 so add ,fun7=1 so sub endelse begin// II_TYPEALUCtrl_id_o= 4'b1000;  //addiendend3'b001:ALUCtrl_id_o= 4'b1010;     // sll  slli3'b100:ALUCtrl_id_o= 4'b1011;   //   xor  xori3'b101:beginif(fun7[5])ALUCtrl_id_o = 4'b1100;  //  sra  sraielseALUCtrl_id_o = 4'b1101;   // srl  srliend3'b110:ALUCtrl_id_o = 4'b1110;  // or   ori3'b111:ALUCtrl_id_o = 4'b1111;  //and  andidefault : ALUCtrl_id_o = 4'b0000; // do nothingendcaseenddefault : ALUCtrl_id_o = 4'b0000; // do nothingendcaseend
end

整个ID阶段代码如下

`include "defines.v"
module ID(input   rst_n,input   [31:0]  pc_id_i,input   [31:0]  instr_id_i,//from  regsfileinput   [31:0]  rd_data1_id_i,input   [31:0]  rd_data2_id_i,//to pc_regoutput          jump_flag_id_o,output  [31:0]  jump_addr_id_o,output  [31:0]  branch_addr_id_o,//to    regsfile  output  [4:0]   reg_addr1_id_o,   //rs1output  [4:0]   reg_addr2_id_o,  //rs2//to  exoutput  [6:0]       opcode_id_o,output  [31:0]      op1_id_o,output  [31:0]      op2_id_o,output  [2:0]       fun3_id_o,output  [4:0]       rdregaddr_id_o,//rdoutput  reg [3:0]   ALUCtrl_id_o,output              MemWrite_id_o,output              MemRead_id_o,output              MemtoReg_id_o,output              RegWrite_id_o);
wire R_type; // AND SUB SLI XOR SRL SRA OR AND         ALU
wire I_type;//addi slli xori srli srai ori andi  jarl
wire S_type;// sb sd sh sw
wire SB_type; //  condition branch
wire U_type; //upper inmmediate format
wire UJ_type;  // uncondition branchwire    [31:0] R_imme;
wire    [31:0] I_imme;
wire    [31:0] S_imme;
wire    [31:0] SB_imme;
wire    [31:0] U_imme;
wire    [31:0] UJ_imme;wire    [6:0]   fun7;
wire    [1:0]   ALUOp;wire    [31:0]  imme;assign reg_addr1_id_o = ((R_type == 1'b1)||(I_type == 1'b1)||(S_type == 1'b1)||(SB_type ==1'b1))?  instr_id_i[19:15]:5'b0;  //rs1
assign reg_addr2_id_o = ((R_type == 1'b1)||(S_type == 1'b1)||(SB_type == 1'b1))?  instr_id_i[24:20]:1'b0;    //rs2
assign fun3_id_o      = instr_id_i[14:12];
assign fun7           = instr_id_i[31:25];
assign rdregaddr_id_o   = instr_id_i[11:7];  //rd
assign opcode_id_o        = instr_id_i[6:0];
// immgen
assign R_type = (instr_id_i[6:0] == `INST_TYPE_R);
assign I_type = (instr_id_i[6:0] == `INST_TYPE_II) ||(instr_id_i[6:0] == `INST_TYPE_IL)||(instr_id_i[6:0] == `INST_TYPE_JALR) ;
assign S_type = (instr_id_i[6:0] == `INST_TYPE_S);
assign SB_type= (instr_id_i[6:0] == `INST_TYPE_SB);
assign U_type = (instr_id_i[6:0] == `INST_TYPE_U);
assign UJ_type= (instr_id_i[6:0] == `INST_TYPE_JAL);assign I_imme = {{20{instr_id_i[31]}},instr_id_i[31:20]};
assign S_imme = {{20{instr_id_i[31]}},instr_id_i[31:25],instr_id_i[11:7]};
assign SB_imme= {{20{instr_id_i[31]}},instr_id_i[7],instr_id_i[30:25],instr_id_i[11:8],1'b0};
assign U_imme = {instr_id_i[31:12],12'b0};
assign UJ_imme= {{12{instr_id_i[31]}},instr_id_i[19:12],instr_id_i[20],instr_id_i[30:21],1'b0};assign imme = I_type?   I_imme  :S_type?   S_imme  :SB_type?  SB_imme :U_type?   U_imme  :UJ_type?  UJ_imme :32'd0;// control
assign MemRead_id_o  = (instr_id_i[6:0]   == `INST_TYPE_IL);
assign MemtoReg_id_o = (instr_id_i[6:0]   == `INST_TYPE_IL);
assign ALUOp    = ((instr_id_i[6:0]  == `INST_TYPE_IL) || (instr_id_i[6:0] == `INST_TYPE_S) || (instr_id_i[6:0] == `INST_TYPE_JALR)||(instr_id_i[6:0] == `INST_TYPE_U)) ? 2'b00:(instr_id_i[6:0] == `INST_TYPE_SB) ? 2'b01 :(instr_id_i[6:0] == `INST_TYPE_R)||(instr_id_i[6:0] == `INST_TYPE_II) ? 2'b10 : 2'b11;
assign MemWrite_id_o = (instr_id_i[6:0] == `INST_TYPE_S);
assign ALUSrc = (instr_id_i[6:0] == `INST_TYPE_R) || (instr_id_i[6:0] == `INST_TYPE_SB);
assign RegWrite_id_o = (instr_id_i[6:0] == `INST_TYPE_R) || (instr_id_i[6:0] == `INST_TYPE_IL)|| (instr_id_i[6:0] == `INST_TYPE_U)||(instr_id_i[6:0] == `INST_TYPE_II)||(instr_id_i[6:0] == `INST_TYPE_JALR)||(instr_id_i[6:0]== `INST_TYPE_JAL); assign op1_id_o = (jump_flag_id_o==1)?          pc_id_i:rd_data1_id_i;
assign op2_id_o = (jump_flag_id_o==1)?            32'h4:(ALUSrc==1)? rd_data2_id_i : imme;
// unconditional jump , so jump .
assign jump_flag_id_o = (instr_id_i[6:0]   ==  `INST_TYPE_JALR)|(instr_id_i[6:0]     ==  `INST_TYPE_JAL);
assign jump_addr_id_o = (instr_id_i[6:0]   ==  `INST_TYPE_JALR)? op1_id_o + imme:(instr_id_i[6:0]     ==  `INST_TYPE_JAL)? pc_id_i + imme :32'h0;
assign branch_addr_id_o = (instr_id_i[6:0]     ==  `INST_TYPE_SB)? pc_id_i + imme :32'h0;//ALUCtrl
always@(*) beginif(!rst_n)beginALUCtrl_id_o = 4'b0000;endelse begincase(ALUOp)2'b00:ALUCtrl_id_o = 4'b1000; // load or store , so use add 2'b01:begin    //condition branch case(fun3_id_o)3'b000:ALUCtrl_id_o = 4'b0001;//beq,  branch if equal3'b001:ALUCtrl_id_o = 4'b0010;//bne,  branch if not equal3'b100:ALUCtrl_id_o = 4'b0011;//blt,  branch if less than3'b101:ALUCtrl_id_o= 4'b0100;//bge, branch if greater than3'b110:ALUCtrl_id_o= 4'b0101;//bltu3'b111:ALUCtrl_id_o= 4'b0110;//bgeudefault : ALUCtrl_id_o= 4'b0000; // do nothingendcaseend2'b10:begin//R_type  or II_typecase(fun3_id_o)3'b000:beginif(instr_id_i[6:0] == `INST_TYPE_R)beginALUCtrl_id_o = (fun7[5] == 0)? 4'b1000:4'b1001;   //fun7==0 so add ,fun7=1 so sub endelse begin// II_TYPEALUCtrl_id_o= 4'b1000;  //addiendend3'b001:ALUCtrl_id_o= 4'b1010;     // sll  slli3'b100:ALUCtrl_id_o= 4'b1011;   //   xor  xori3'b101:beginif(fun7[5])ALUCtrl_id_o = 4'b1100;  //  sra  sraielseALUCtrl_id_o = 4'b1101;   // srl  srliend3'b110:ALUCtrl_id_o = 4'b1110;  // or   ori3'b111:ALUCtrl_id_o = 4'b1111;  //and  andidefault : ALUCtrl_id_o = 4'b0000; // do nothingendcaseenddefault : ALUCtrl_id_o = 4'b0000; // do nothingendcaseend
end
endmodule 

这是寄存器堆代码(包含写回)


module regsfile(input           clk,input           rst_n,
//from  idinput   [4:0]   reg_addr1,  //rs1input   [4:0]   reg_addr2,  //rs2input   RegWrite,input   [4:0]   rd,input   [31:0]  writeregdata,output  [31:0]  rd_data1,output  [31:0]  rd_data2
//from mem
);
//to id
reg [31:0] regs[31:0];
//write back
always@(posedge clk or rst_n) beginif(~rst_n) beginregs[0]  <= 32'b0;regs[1]  <= 32'b0;regs[2]  <= 32'b0;endelse if(RegWrite) beginregs[rd] <= writeregdata;endelse beginregs[rd] <= regs[rd];end
end
//reg read
assign rd_data1 = (reg_addr1==5'd0)?  0:regs[reg_addr1];
assign rd_data2 = (reg_addr2==5'd0)?  0:regs[reg_addr2];endmodule

③ :EX阶段

EX阶段主要进行ALU的运算,根据不同的ALUCtrl,让ALU进行不同类型的计算


module EX(input   [31:0]  op1_ex_i,input   [31:0]  op2_ex_i,input   [2:0]   fun3_ex_i,input   [3:0]   ALUCtrl_ex_i,input   [31:0]  rd_data2_ex_i,input   MemWrite_ex_i,output   reg    Branch,// conditional branch predict: 1 meanns successed, 0 means failedoutput   reg    [31:0]  writememdata_ex_o,output   reg    [31:0]  ALUresult_ex_o  // as address  if load or store);
wire [31:0] ALUA;
wire [31:0] ALUB;
wire [31:0] storedata;
assign ALUA = op1_ex_i;
assign ALUB = op2_ex_i;
assign storedata =rd_data2_ex_i;always @(*) beginif(MemWrite_ex_i)begincase(fun3_ex_i)3'b000:writememdata_ex_o = {24'b0,storedata[7:0]};//sb3'b001:writememdata_ex_o = {16'b0,storedata[15:0]};//sh3'b010:writememdata_ex_o =  storedata ;        //swdefault:writememdata_ex_o= writememdata_ex_o;endcaseendelse beginwritememdata_ex_o =32'h0;end
endalways @ (*) beginALUresult_ex_o = 32'h0;Branch = 1'b0;case(ALUCtrl_ex_i)4'b0001:begin    //beqALUresult_ex_o = 32'h0;Branch=(ALUA == ALUB);end4'b0010:begin     //bnqALUresult_ex_o = 32'h0;Branch=(ALUA != ALUB);end4'b0011:begin      //bltALUresult_ex_o = 32'h0;Branch=(ALUA < ALUB);end4'b0100:begin      //bgeALUresult_ex_o = 32'h0;Branch=(ALUA > ALUB);end        4'b0000:ALUresult_ex_o = ALUA + ALUB;  // load  store jalr  utype4'b1000:ALUresult_ex_o = ALUA + ALUB;      //add   addi4'b1001:ALUresult_ex_o = ALUA - ALUB;       //sub4'b1010:ALUresult_ex_o = ALUA << ALUB;     //left shift4'b1011:ALUresult_ex_o = ALUA ^ ALUB;     //xor xori//4'b1100:ALUresult_ex_o = ALUA >> ALUB;   //sra srai4'b1101:ALUresult_ex_o = ALUA >> ALUB;  //srl  srli 4'b1110:ALUresult_ex_o = ALUA | ALUB;   //or  ori 4'b1111:ALUresult_ex_o = ALUA & ALUB;   //and  andidefault : beginALUresult_ex_o = 32'h0;Branch= 1'b0;endendcaseend
endmodule

可以看到我在这里对要存入内存的数据进行了处理(比如说SW,SH,SB,都是对一个数进行不同的处理后在存入内存,其实这个也可以写在译码阶段,也可以写在MEM阶段,但是这里我写在了EX阶段。
其实一开始我是写在MEM阶段,因为我觉得既然是存数据,就在MEM阶段处理就好了,这在单周期中没有影响,在流水线阶段由于要使用forwarding结束,有可能你要存的数据产生了数据冒险,也就是还没有写入,在设计中我尽量都让forwarding信号来到EX阶段进行处理,所以也就把这个放到了EX阶段。

④:MEM阶段

MEM阶段将从ID传来的读写内存控制信号传给data_ram,并将读到的数据根据指令lb lw lh 的不同来进行处理。

module MEM(// from idinput   MemWrite_mem_i,input   MemRead_mem_i,input   [31:0]  ALUresult_mem_i,          //as address , if load or storeinput   [31:0]  writememdata_mem_i,input   [2:0]   fun3_mem_i,// from data_memoryinput   [31:0]  readdata32_mem_i,// to data_memoryoutput   MemWrite_mem_o,output   MemRead_mem_o,output   [31:0]  ALUresult_mem_o,          //as address , if load or storeoutput   [31:0]  writememdata_mem_o,//to wboutput  reg     [31:0] readdata);
//readmemdata after process
always@(*)begin if(MemRead_mem_i) begincase(fun3_mem_i)3'b000:readdata = {{24{readdata32_mem_i[7]}},readdata32_mem_i[7:0]};//lb3'b001:readdata = {{16{readdata32_mem_i[15]}},readdata32_mem_i[15:0]};//lh3'b010:readdata = readdata32_mem_i;//lw3'b100:readdata = {24'b0,readdata32_mem_i[7:0]};//lbu3'b101:readdata = {16'b0,readdata32_mem_i[15:0]};//lhuendcaseend
end
assign MemWrite_mem_o = MemWrite_mem_i;
assign MemRead_mem_o  = MemRead_mem_i;
assign ALUresult_mem_o= ALUresult_mem_i;
assign writememdata_mem_o=writememdata_mem_i;endmodule

⑤:WB阶段

首先要有一个选择器,如果指令需要写回寄存器的话,来确定是从内存读出的值写回寄存器还是通过ALU运算的结果写回寄存器

module muxwb(input MemtoReg,input [31:0] readdata,input [31:0] ALUresult,output [31:0] writeregdata
);assign writeregdata = MemtoReg ? readdata : ALUresult;endmodule

在regsfile文件中写入寄存器,这个模块同样被用在ID阶段,但那时是用来读寄存器

module regsfile(input           clk,input           rst_n,
//from  idinput   [4:0]   reg_addr1,  //rs1input   [4:0]   reg_addr2,  //rs2input   RegWrite,input   [4:0]   rd,input   [31:0]  writeregdata,output  [31:0]  rd_data1,output  [31:0]  rd_data2
//from mem
);
//to id
reg [31:0] regs[31:0];
//write back
always@(posedge clk or rst_n) beginif(~rst_n) beginregs[0]  <= 32'b0;regs[1]  <= 32'b0;regs[2]  <= 32'b0;endelse if(RegWrite) beginregs[rd] <= writeregdata;endelse beginregs[rd] <= regs[rd];end
end
//reg read
assign rd_data1 = (reg_addr1==5'd0)?  0:regs[reg_addr1];
assign rd_data2 = (reg_addr2==5'd0)?  0:regs[reg_addr2];endmodule

结语

本次单周期的设计基本结束,我把从pc_reg,if,rom,id,regsfile,ex,mem,ram
,muxwb,wb的都展示在上面了,再次声明,仅供个人学习记录,我目前还是个新手,代码质量可能一般,请见谅。
后续我会更新testbench,以及五级流水线的设计及验证。


http://www.ppmy.cn/news/429760.html

相关文章

RISC-V的前世今生

RISC-V是什么&#xff1f; RISC全名Reduced Instruction Set Computer &#xff0c;即精简指令集计算机。V是罗马字母&#xff0c;代表第五代RISC(精简指令集计算机)&#xff0c;可读作RISC-FIVE。 RISC-V指令集&#xff0c;类似于INTEL的X86 ,ARM指令集&#xff0c;是一个被CP…

RISC-V 能打 50 年!不必期待 RISC-VI —— 对话 RISC-V CTO Mark Himelstein

《程序员》于 2000 年创刊&#xff0c;其理念为「技术改变世界&#xff0c;创新驱动中国」。2021 年&#xff0c;《程序员》2.0 全新起航&#xff0c;首期以「开发者的黄金十年」为主题&#xff0c;以音视频、图文专栏等丰富的多媒体形式为载体&#xff0c;立足当下&#xff0c…

Vpro 相机操作类

在网站上看到这个&#xff0c;保存下来&#xff0c;以后用到了&#xff0c;再看一下。谢谢原创的分享&#xff01; #region 获得相机信息方法 /// <summary> /// 公有静态方法&#xff0c;查找单个相机。例如“Basler” /// </summary> public static ICogFrameG…

盘点国产RISC-V内核的单片机

RISC-V就不必多说了&#xff0c;它是一个基于精简指令集的开源指令集架构。与主流的主流的架构为x86与ARM架构不同&#xff0c;其特点就是完全开源。今天跟大家一起盘点一下国产RISC-V内核的单片机。 1.GD32VF103系列 兆易创新作为国产MCU比较大的一个厂家&#xff0c;其早就推…

RISC-V与主流处理器设计

RISC-V与主流处理器设计 文章目录 RISC-V与主流处理器设计前言 一、RISC-V最基本的ISA1.什么是RISC-V2.RISC-V的发展目标3.RISC-V的ISA设计4.RV32I&#xff1a;RISC-V 基础整数指令集4.1RV32I 指令格式4.2RV32I 寄存器4.3RV32I 整数计算4.4RV32I 的Load 和 Store4.5RV32I 条件分…

VisionPro软件介绍

V i s i o n P r o 软 件 介 绍 VisionPro软件介绍 VisionPro软件介绍 一 VisionPro的四种开发模式 1.Quickbuild视觉向导生成的操作界面 Quickbuild视觉 向导生成的操作界面 2 Quickbuild视觉修改的操作界面 使用QuickBuild互动开发视觉、输入和输出和工作控件使用应用…

12代酷睿不再“挤牙膏”,能为英特尔vPro远程办公带来什么?

文/智能相对论 作者/隐南 第12代酷睿Alder Lake终于要发售了。 被消费者贴上“牙膏厂”标签多年的英特尔这次终于觉醒&#xff0c;在架构上做出了重大改变&#xff0c;性能上也有大幅的提升。 对于15年来依托酷睿成长起来的商用办公平台vPro&#xff0c;12代酷睿的升级能为…

【Red Hat7.9安装Oracle11g--调用图形化界面的几种方式】

【Red Hat7.9安装Oracle11g--调用图形化界面的几种方式】 &#x1f53b; 一、续上一篇[【Red Hat 7.9---详细安装Oracle 11g---图形化界面方式】](https://blog.csdn.net/qq_41840843/article/details/131198718?spm1001.2014.3001.5501)⛳ 1.1 前言⛳ 1.2 方式一、使用Xmanag…