【【深入浅出TinyRisc-v】】
深入浅出TinyRisc-v
本代码参考于 https://gitee.com/liangkangnan/tinyriscv 自己理解之后又重新写了一遍
tinyriscv.v
// 涓嬮潰鏄鏁翠釜top妯″潡鐨勪功鍐?
module tinyriscv(input clk ,input rst_n ,//澶栬鐨勪俊鎭鍙?input [31 : 0] rib_ex_data_i , // 浠庡璁捐鍙栫殑鏁版嵁output [31 : 0] rib_ex_addr_o , // 璇诲啓澶栬鐨勫湴鍧?output [31 : 0] rib_ex_data_o , // 鍐欏叆澶栬鐨勬暟鎹?output rib_ex_req_o ,output rib_ex_we_o ,// 鍙栨寚浠? 浠? ROM涓?input [31 : 0] rib_pc_data_i ,output [31 : 0] rib_pc_addr_o , // 鍙栨寚鍦板潃// jtag浜や簰鐨勬帴鍙?input [4 : 0] jtag_reg_addr_i , // jtag妯″潡璇汇?佸啓瀵勫瓨鍣ㄧ殑鍦板潃 4:0input [31 : 0] jtag_reg_data_i , // jtag妯″潡鍐欏瘎瀛樺櫒鏁版嵁 31 : 0input jtag_reg_we_i , // jtag妯″潡鍐欏瘎瀛樺櫒鏍囧織output [31 : 0] jtag_reg_data_o , // jtag妯″潡璇诲彇鍒扮殑瀵勫瓨鍣ㄦ暟鎹?input rib_hold_flag_i , // 鎬荤嚎鏆傚仠鏍囧織input jtag_halt_flag_i , // jtag鏆傚仠鏍囧織input jtag_reset_flag_i , // jtag澶嶄綅PC鏍囧織input [7 : 0] int_i // 涓柇淇″彿 7:0);// -------------------------------------------------------- //// internal signal and deffine //// ----------------------------------------------------- //wire [31 : 0] pc_pc_o ;assign rib_pc_addr_o = pc_pc_o ; // 杩欐槸鍙栨寚浠ゅ湴鍧?wire [2 : 0] ctrl_hold_flag_o ;wire ctrl_jump_flag_o ;wire [31 : 0] ctrl_jump_addr_o ;wire [7 : 0] if_int_flag_o ;wire [31 : 0] if_inst_o ;wire [31 : 0] if_inst_addr_o ;wire [31 : 0] regs_rdata1_o ;wire [31 : 0] regs_rdata2_o ;wire [4 : 0] id_reg1_raddr_o ;wire [4 : 0] id_reg2_raddr_o ;wire [31 : 0] id_inst_o ;wire [31 : 0] id_inst_addr_o ;wire [31 : 0] id_reg1_rdata_o ;wire [31 : 0] id_reg2_rdata_o ;wire id_reg_we_o ;wire [4 : 0] id_reg_waddr_o ;wire [31 : 0] id_csr_raddr_o ;wire id_csr_we_o ;wire [31 : 0] id_csr_rdata_o ;wire [31 : 0] id_csr_waddr_o ;wire [31 : 0] id_op1_o ;wire [31 : 0] id_op2_o ;wire [31 : 0] id_op1_jump_o ;wire [31 : 0] id_op2_jump_o ;// id_ex妯″潡杈撳嚭淇″彿wire [31 : 0] ie_inst_o ;wire [31 : 0] ie_inst_addr_o ;wire ie_reg_we_o ;wire [4 : 0] ie_reg_waddr_o ;wire [31 : 0] ie_reg1_rdata_o ;wire [31 : 0] ie_reg2_rdata_o ;wire ie_csr_we_o ;wire [31 : 0] ie_csr_waddr_o ;wire [31 : 0] ie_csr_rdata_o ;wire [31 : 0] ie_op1_o ;wire [31 : 0] ie_op2_o ;wire [31 : 0] ie_op1_jump_o ;wire [31 : 0] ie_op2_jump_o ;// ex妯″潡杈撳嚭淇″彿wire [31 : 0] ex_mem_wdata_o ;wire [31 : 0] ex_mem_raddr_o ;wire [31 : 0] ex_mem_waddr_o ;wire ex_mem_we_o ;wire ex_mem_req_o ;wire [31 : 0] ex_reg_wdata_o ;wire ex_reg_we_o ;wire [4 : 0] ex_reg_waddr_o ;wire ex_hold_flag_o ;wire ex_jump_flag_o ;wire [31 : 0] ex_jump_addr_o ;wire ex_div_start_o ;wire [31 : 0] ex_div_dividend_o ;wire [31 : 0] ex_div_divisor_o ;wire [2 : 0] ex_div_op_o ;wire [4 : 0] ex_div_reg_waddr_o ;wire [31 : 0] ex_csr_wdata_o ;wire ex_csr_we_o ;wire [31 : 0] ex_csr_waddr_o ;wire [31 : 0] csr_data_o ;wire [31 : 0] csr_clint_data_o ;wire csr_global_int_en_o ;wire [31 : 0] csr_clint_csr_mtvec ;wire [31 : 0] csr_clint_csr_mepc ;wire [31 : 0] csr_clint_csr_mstatus;wire [31 : 0] div_result_o ;wire div_ready_o ;wire div_busy_o ;wire [4 : 0] div_reg_waddr_o ;wire clint_we_o ;wire [31 : 0] clint_waddr_o ;wire [31 : 0] clint_raddr_o ;wire [31 : 0] clint_data_o ;wire [31 : 0] clint_int_addr_o ;wire clint_int_assert_o ;wire clint_hold_flag_o ;assign rib_ex_addr_o = (ex_mem_we_o == 1)? ex_mem_waddr_o: ex_mem_raddr_o;assign rib_ex_data_o = ex_mem_wdata_o ;assign rib_ex_req_o = ex_mem_req_o ;assign rib_ex_we_o = ex_mem_we_o ;// ----------------------------------------------------- //// 渚嬪寲 浠嬬粛 //// ----------------------------------------------------- //pc_reg u_pc_reg(.clk ( clk ),.rst_n ( rst_n ),.jtag_reset_flag_i(jtag_reset_flag_i),.pc_o(pc_pc_o),.hold_flag_i(ctrl_hold_flag_o),.jump_flag_i(ctrl_jump_flag_o),.jump_addr_i(ctrl_jump_addr_o));// 鎸夐『搴忎笅涓?涓槸 if_idif_id u_if_id(.clk ( clk ),.rst_n ( rst_n ),.inst_i(rib_pc_data_i),.inst_addr_i(pc_pc_o),.int_flag_i(int_i),.int_flag_o(if_int_flag_o),.hold_flag_i(ctrl_hold_flag_o),.inst_o(if_inst_o),.inst_addr_o(if_inst_addr_o));//鎺ヤ笅鏉ユ槸 id妯″潡id u_id(.rst_n ( rst_n ),.inst_i(if_inst_o),.inst_addr_i(if_inst_addr_o),.reg1_rdata_i(regs_rdata1_o),.reg2_rdata_i(regs_rdata2_o),.reg1_raddr_o(id_reg1_raddr_o),.reg2_raddr_o(id_reg2_raddr_o),.inst_o(id_inst_o),.inst_addr_o(id_inst_addr_o),.reg1_rdata_o(id_reg1_rdata_o),.reg2_rdata_o(id_reg2_rdata_o),.reg_we_o(id_reg_we_o),.reg_waddr_o(id_reg_waddr_o),.op1_o(id_op1_o),.op2_o(id_op2_o),.op1_jump_o(id_op1_jump_o),.op2_jump_o(id_op2_jump_o),.csr_rdata_i(csr_data_o),.csr_raddr_o(id_csr_raddr_o),.csr_we_o(id_csr_we_o),.csr_rdata_o(id_csr_rdata_o),.csr_waddr_o(id_csr_waddr_o));// 杩欓噷闈㈠苟娌℃湁鍗忓晢ex_jump_flag// 鐢ㄧ殑鏄? id_exid_ex u_id_ex(.clk(clk ),.rst_n(rst_n ),.inst_i(id_inst_o),.inst_addr_i(id_inst_addr_o),.reg_we_i(id_reg_we_o),.reg_waddr_i(id_reg_waddr_o),.reg1_rdata_i(id_reg1_rdata_o),.reg2_rdata_i(id_reg2_rdata_o),.hold_flag_i(ctrl_hold_flag_o),.inst_o(ie_inst_o),.inst_addr_o(ie_inst_addr_o),.reg_we_o(ie_reg_we_o),.reg_waddr_o(ie_reg_waddr_o),.reg1_rdata_o(ie_reg1_rdata_o),.reg2_rdata_o(ie_reg2_rdata_o),.op1_i(id_op1_o),.op2_i(id_op2_o),.op1_jump_i(id_op1_jump_o),.op2_jump_i(id_op2_jump_o),.op1_o(ie_op1_o),.op2_o(ie_op2_o),.op1_jump_o(ie_op1_jump_o),.op2_jump_o(ie_op2_jump_o),.csr_we_i(id_csr_we_o),.csr_waddr_i(id_csr_waddr_o),.csr_rdata_i(id_csr_rdata_o),.csr_we_o(ie_csr_we_o),.csr_waddr_o(ie_csr_waddr_o),.csr_rdata_o(ie_csr_rdata_o));// ex妯″潡ex u_ex(.rst_n(rst_n ),.inst_i(ie_inst_o),.inst_addr_i(ie_inst_addr_o),.reg_we_i(ie_reg_we_o),.reg_waddr_i(ie_reg_waddr_o),.reg1_rdata_i(ie_reg1_rdata_o),.reg2_rdata_i(ie_reg2_rdata_o),.op1_i(ie_op1_o),.op2_i(ie_op2_o),.op1_jump_i(ie_op1_jump_o),.op2_jump_i(ie_op2_jump_o),.mem_rdata_i(rib_ex_data_i),.mem_wdata_o(ex_mem_wdata_o),.mem_raddr_o(ex_mem_raddr_o),.mem_waddr_o(ex_mem_waddr_o),.mem_we_o(ex_mem_we_o),.mem_req_o(ex_mem_req_o),.reg_wdata_o(ex_reg_wdata_o),.reg_we_o(ex_reg_we_o),.reg_waddr_o(ex_reg_waddr_o),.hold_flag_o(ex_hold_flag_o),.jump_flag_o(ex_jump_flag_o),.jump_addr_o(ex_jump_addr_o),.int_assert_i(clint_int_assert_o),.int_addr_i(clint_int_addr_o),.div_ready_i(div_ready_o),.div_result_i(div_result_o),.div_busy_i(div_busy_o),.div_reg_waddr_i(div_reg_waddr_o),.div_start_o(ex_div_start_o),.div_dividend_o(ex_div_dividend_o),.div_divisor_o(ex_div_divisor_o),.div_op_o(ex_div_op_o),.div_reg_waddr_o(ex_div_reg_waddr_o),.csr_we_i(ie_csr_we_o),.csr_waddr_i(ie_csr_waddr_o),.csr_rdata_i(ie_csr_rdata_o),.csr_wdata_o(ex_csr_wdata_o),.csr_we_o(ex_csr_we_o),.csr_waddr_o(ex_csr_waddr_o));ctrl u_ctrl(.jump_flag_i(ex_jump_flag_o),.jump_addr_i(ex_jump_addr_o),.hold_flag_ex_i(ex_hold_flag_o),.hold_flag_rib_i(rib_hold_flag_i),.hold_flag_o(ctrl_hold_flag_o),.hold_flag_clint_i(clint_hold_flag_o),.jump_flag_o(ctrl_jump_flag_o),.jump_addr_o(ctrl_jump_addr_o),.jtag_halt_flag_i(jtag_halt_flag_i));regs u_regs(.clk(clk),.rst_n(rst_n),.we_i(ex_reg_we_o),.waddr_i(ex_reg_waddr_o),.wdata_i(ex_reg_wdata_o),.raddr1_i(id_reg1_raddr_o),.rdata1_o(regs_rdata1_o),.raddr2_i(id_reg2_raddr_o),.rdata2_o(regs_rdata2_o),.jtag_we_i(jtag_reg_we_i),.jtag_addr_i(jtag_reg_addr_i),.jtag_data_i(jtag_reg_data_i),.jtag_data_o(jtag_reg_data_o));csr_reg u_csr_reg(.clk(clk),.rst_n(rst_n),.we_i(ex_csr_we_o),.raddr_i(id_csr_raddr_o),.waddr_i(ex_csr_waddr_o),.data_i(ex_csr_wdata_o),.data_o(csr_data_o),.global_int_en_o(csr_global_int_en_o),.clint_we_i(clint_we_o),.clint_raddr_i(clint_raddr_o),.clint_waddr_i(clint_waddr_o),.clint_data_i(clint_data_o),.clint_data_o(csr_clint_data_o),.clint_csr_mtvec(csr_clint_csr_mtvec),.clint_csr_mepc(csr_clint_csr_mepc),.clint_csr_mstatus(csr_clint_csr_mstatus));div u_div(.clk(clk),.rst_n(rst_n),.dividend_i(ex_div_dividend_o),.divisor_i(ex_div_divisor_o),.start_i(ex_div_start_o),.op_i(ex_div_op_o),.reg_waddr_i(ex_div_reg_waddr_o),.result_o(div_result_o),.ready_o(div_ready_o),.busy_o(div_busy_o),.reg_waddr_o(div_reg_waddr_o));clint u_clint(.clk(clk),.rst_n(rst_n),.int_flag_i(if_int_flag_o),.inst_i(id_inst_o),.inst_addr_i(id_inst_addr_o),.jump_flag_i(ex_jump_flag_o),.jump_addr_i(ex_jump_addr_o),.div_started_i(ex_div_start_o),.csr_mtvec(csr_clint_csr_mtvec),.csr_mepc(csr_clint_csr_mepc),.csr_mstatus(csr_clint_csr_mstatus),.we_o(clint_we_o),.waddr_o(clint_waddr_o),.raddr_o(clint_raddr_o),.data_o(clint_data_o),.hold_flag_o(clint_hold_flag_o),.global_int_en_i(csr_global_int_en_o),.int_addr_o(clint_int_addr_o),.int_assert_o(clint_int_assert_o));endmodule
pc_reg.v
// PC寄存器模块
module pc_reg(input wire clk ,input wire rst_n , // 低电平复位input wire jump_flag_i , // 跳转标志input wire [31:0] jump_addr_i , // 跳转地址input wire [2 :0] hold_flag_i , // 流水线暂停标志input wire jtag_reset_flag_i , // 复位标志output reg [31:0] pc_o);always@(posedge clk )begin// 复位if(rst_n == 0 || jtag_reset_flag_i == 1)beginpc_o <= 32'h0 ;endelse if(jump_flag_i == 1)beginpc_o <= jump_addr_i ;endelse if(hold_flag_i >= 3'b001 )beginpc_o <= pc_o ; // 这就是暂停保持的endelsebeginpc_o <= pc_o + 4'h4 ;endend
endmodule
if_id.v
// 将指令向译码模块传递
module if_id(input wire clk ,input wire rst_n ,input wire [31 : 0] inst_i ,output wire [31 : 0] inst_o ,input wire [31 : 0] inst_addr_i , output wire [31 : 0] inst_addr_o , input wire [2 : 0] hold_flag_i ,input wire [7 : 0] int_flag_i , // 外设中断输入信号 output wire [7 : 0] int_flag_o
);wire hold_en = (hold_flag_i > 3'b010) ; // 这里的和前面一样的控制信息 hold_IF wire [31 : 0] inst ;
wire [31 : 0] inst_addr ;
wire [7 : 0] int_flag ; gen_pipe_dff#(.DW ( 32 )
)inst_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 32'h00000001 ), // 其实我不懂默认的为什么要设置这个指令内容 1 我就设置成0 .din ( inst_i ),.qout ( inst )
);
assign inst_o = inst ; gen_pipe_dff#(.DW ( 32 )
)inst_addr_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 32'h0 ), // 这里是 指令地址0 .din ( inst_addr_i ),.qout ( inst_addr )
);
assign inst_addr_o = inst_addr ; gen_pipe_dff#(.DW ( 8 )
)int_flag_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 8'h0 ),.din ( int_flag_i ), // 外设中断输入信号.qout ( int_flag )
);
assign int_flag_o = int_flag ; endmodule
id.v
// 译码阶段
module id(input rst_n ,//input [31 : 0] inst_i , // 指令内容input [31 : 0] inst_addr_i ,// from reg 我在这里其实会等reg把指令传递回来input [31 : 0] reg1_rdata_i ,input [31 : 0] reg2_rdata_i ,// from csr reginput [31 : 0] csr_rdata_i ,// from ex 跳转标志// input ex_jump_flag_i , // 跳转标志//这一个是根本用不到的数据 // // to regoutput reg [4 : 0] reg1_raddr_o , // 读通用寄存器1地址output reg [4 : 0] reg2_raddr_o ,// to csr regoutput reg [31 : 0] csr_raddr_o ,// to exoutput reg [31 : 0] op1_o ,output reg [31 : 0] op2_o ,output reg [31 : 0] op1_jump_o ,output reg [31 : 0] op2_jump_o ,output reg [31 : 0] inst_o ,output reg [31 : 0] inst_addr_o , // 指令地址output reg [31 : 0] reg1_rdata_o , // 通用寄存器1数据output reg [31 : 0] reg2_rdata_o , // 通用寄存器2数据output reg reg_we_o , // 写通用寄存器标志output reg [4 : 0] reg_waddr_o , // 写通用寄存器地址output reg csr_we_o , // 写CSR寄存器标志output reg [31 : 0] csr_rdata_o , // CSR寄存器数据output reg [31 : 0] csr_waddr_o // 写CSR寄存器地址) ;// 还是先写整体的主题内容// 对于指令字段的分析// [31:25] [24:20] [19:15] [14:12] [11:7] [6:0]wire [6 : 0] opcode ;wire [4 : 0] rd ;wire [2 : 0] funct3 ;wire [4 : 0] rs1 ;wire [4 : 0] rs2 ;wire [6 : 0] funct7 ;assign opcode = inst_i[6 : 0] ;assign rd = inst_i[11 : 7] ;assign funct3 = inst_i[14 : 12] ;assign rs1 = inst_i[19 : 15] ;assign rs2 = inst_i[24 : 20] ;assign funct7 = inst_i[31 : 25] ;always@(*)begininst_o = inst_i ;inst_addr_o = inst_addr_i ;reg1_rdata_o = reg1_rdata_i ;reg2_rdata_o = reg2_rdata_i ;csr_rdata_o = csr_rdata_i ;csr_raddr_o = 0 ;csr_waddr_o = 0 ;csr_we_o = 0 ;op1_o = 0 ;op2_o = 0 ;op1_jump_o = 0 ;op2_jump_o = 0 ;case(opcode)7'b001_0011 :begin // this is I typecase(funct3)3'b000 , // addi addi a0, a1, 0x5 //a0 = a1 + 0x53'b010 , // slti a0, a1, 0x05 //a1 < 0x05 ? a0 = 1 : a0 = 03'b011 , // SLTIU3'b100 , // XORI3'b110 , // ORI3'b111 , // ANDI3'b001 , // SLLI3'b101 : // SRIbeginreg_we_o = 1 ;reg_waddr_o = rd ; // 这里的做法是写地址给出reg1_raddr_o = rs1 ;reg2_raddr_o = 0 ;op1_o = reg1_rdata_i ;op2_o = {{20{inst_i[31]}}, inst_i[31:20]} ;enddefault :beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseend7'b011_0011 : // R型beginif( (funct7 == 7'b000_0000) || (funct7 == 7'b010_0000))begincase(funct3) // 这一部分是标准的指令集内容3'b000 , // SUB3'b001 , // SLL3'b010 , // SLT3'b011 , // SLTU3'b100 , // XOR3'b101 , // SR3'b110 , // OR3'b111 : // ANDbeginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = rs1 ;reg2_raddr_o = rs2 ;op1_o = reg1_rdata_i ;op2_o = reg2_rdata_i ;enddefault :beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseendelse if( funct7 == 7'b000_0001) // Mbegincase(funct3)3'b000 , // MUL3'b001 , // MULH3'b010 , // MULHSU3'b011 : // MULHUbeginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = rs1 ;reg2_raddr_o = rs2 ;op1_o = reg1_rdata_i ;op2_o = reg2_rdata_i ;end3'b100 , // DIV3'b101 , // DIVU3'b110 , // REM3'b111 : // REMUbeginreg_we_o = 0 ; // 因为除法的计算时间比较长,所以会立即禁止写回reg_waddr_o = rd ;reg1_raddr_o = rs1 ;reg2_raddr_o = rs2 ;op1_o = reg1_rdata_i ;op2_o = reg2_rdata_i ;op1_jump_o = inst_addr_i ; // 记录 当前指令的地址op2_jump_o = 32'h4 ;enddefault:beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseendelsebeginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endend7'b000_0011 :begin // 其实这里是对上面的I型指令的一部分扩展case(funct3)3'b000 , // lb x10, 0(x1)//将x1的值加上0,将这个值作为地址, 取出这个地址所对应的内存中的值, 将这个值赋值给x10(取出的是8位数值)3'b001 , // lh x10, 0(x1) //从内存中取出16位数值3'b010 , // lw x10, 0(x1) //从内存中取出32位数值3'b100 , // lbu x10, 0(x1) //从内存中取出8位无符号数值3'b101 : // lhu x10, 0(x1) //从内存中取出16位无符号数值beginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = rs1 ;reg2_raddr_o = 0 ;op1_o = reg1_rdata_i ;op2_o = {{20{inst_i[31]}},inst_i[31:20]} ;enddefault :beginreg1_raddr_o = 0 ;reg2_raddr_o = 0 ;reg_we_o = 0 ;reg_waddr_o = 0 ;endendcaseend7'b01_00011 : // S 类型begincase(funct3) // S type3'b000 , // sb x10, 0(x1)//x1的值加上0,将这个值作为地址, 将x10的值存储到上述地址所对应的内存中去 (只会将x10的值的低8位写入)3'b001 , // sh x10, 0(x1) //只会将x10的值的低16位写入3'b010 :// sw x10, 0(x1) //只会将x10的值的低32位写入begin reg_we_o = 0 ; // 并不需要写入寄存器 是直接存入内存的reg_waddr_o = 0 ;reg1_raddr_o = rs1 ;reg2_raddr_o = rs2 ;op1_o = reg1_rdata_i ;op2_o = {{20{inst_i[31]}}, inst_i[31:25], inst_i[11:7]} ;enddefault :beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseend7'b1100011 : // B 型指令begincase(funct3)3'b000 , //beq a1,a2,Label //if(a1==a2){goto Label;}3'b001 , //bne a1,a2,Label //if(a1!=a2){goto Label;}3'b100 , //blt a1,a2,Label //if(a1< a2){goto Label;}3'b101 , //BGE: Branch if Greater or Equal (大于等于时跳转,有符号比较)3'b110 , //BLTU: Branch if Less Than Unsigned (小于时跳转,无符号比较)3'b111 :begin // BGEU: Branch if Greater or Equal Unsigned (大于等于时跳转,无符号比较)reg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = rs1 ;reg2_raddr_o = rs2 ;op1_o = reg1_rdata_i ;op2_o = reg2_rdata_i ;op1_jump_o = inst_addr_i ;op2_jump_o = {{20{inst_i[31]}}, inst_i[7], inst_i[30:25], inst_i[11:8], 1'b0};enddefault :beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseend7'b110_1111 : // JAL J型指令 pc = pc + imm 并将下一条指令的地址 给rdbeginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;op1_o = inst_addr_i ;op2_o = 32'h4 ;op1_jump_o = inst_addr_i ;op2_jump_o = {{12{inst_i[31]}}, inst_i[19:12], inst_i[20], inst_i[30:21], 1'b0};end7'b1100_111 : // JALR 功能:PC = (rs1 + imm) & ~1,并将下一条指令地址存入 rd。beginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = rs1 ;reg2_raddr_o = 0 ;op1_o = inst_addr_i ;op2_o = 32'h4 ;op1_jump_o = reg1_rdata_i ;op2_jump_o = {{20{inst_i[31]}}, inst_i[31:20]};end7'b011_0111 : // LUT lui 将 imm[31:12] 左移 12 位后写入寄存器 rdbeginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;op1_o = {inst_i[31:12], 12'b0} ;op2_o = 0 ;end7'b001_0111 : // 功能:PC + (imm[31:12] << 12) 写入寄存器 rdbeginreg_we_o = 1 ;reg_waddr_o = rd ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;op1_o = inst_addr_i ;op2_o = {inst_i[31:12], 12'b0} ;end7'b0000_001 : // 单纯的占用流水线的时钟周期beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;end7'b000_1111 : // 功能:在执行不同类型的内存操作时插入屏障,确保指令执行顺序beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;op1_jump_o = inst_addr_i ;op2_jump_o = 32'h4 ;end//下面是最后一部分CSR的编写7'b111_0011 : // CSR 指令属于 RISC-V 的标准基础指令集的扩展部分// 是的一共六个 特权指令集(Privileged ISA) 的核心部分。beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;csr_raddr_o = {20'h0, inst_i[31:20]};csr_waddr_o = {20'h0, inst_i[31:20]};case (funct3)3'b001 ,3'b010 ,3'b011 :beginreg1_raddr_o = rs1 ;reg2_raddr_o = 0 ;reg_we_o = 1 ;reg_waddr_o = rd ;csr_we_o = 1 ;end3'b101 ,3'b110 ,3'b111 :beginreg1_raddr_o = 0 ;reg2_raddr_o = 0 ;reg_we_o = 1 ;reg_waddr_o = rd ;csr_we_o = 1 ;enddefault:beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;csr_we_o = 0 ;endendcaseenddefault:beginreg_we_o = 0 ;reg_waddr_o = 0 ;reg1_raddr_o = 0 ;reg2_raddr_o = 0 ;endendcaseendendmodule
clint.v
// 核心中断管理、仲裁
module clint(input clk ,input rst_n ,// 其实很简单的区分input [7 : 0] int_flag_i , // 这个信号是 来自于core的// 这一部分是中断的指令内容input [31 : 0] inst_i , // 这里确定的是指令的内容// clint 模块需要知道当前执行的指令,以便检查是否是一个需要处理的指令(例如,ECALL 或 EBREAK)。// 如果是同步中断(如系统调用),需要根据指令类型决定如何处理中断。input [31 : 0] inst_addr_i ,// form exinput jump_flag_i , //// 中断处理需要知道当前是否有跳转指令正在执行。如果有跳转指令发生,中断处理时要注意将跳转地址保存或调整。input [31 : 0] jump_addr_i ,input div_started_i , // 除法指令// 如果正在进行除法操作 可能要推迟同步中断的处理//input [2 : 0] hold_flag_i , // 流水线暂停标志// 标注一下这里的 hold_flag_i 并没有用到// 下面是正常的交互 csr_reg// input [31 : 0] data_i , 这是一个没用的值input [31 : 0] csr_mtvec ,// 中断向量表地址input [31 : 0] csr_mepc ,// 中断返回地址input [31 : 0] csr_mstatus ,// 全局中断状态寄存器input global_int_en_i , // 全局中断使能output hold_flag_o , // 全局流水线暂停// to csr_regoutput reg we_o ,output reg [31 : 0] waddr_o ,output reg [31 : 0] raddr_o ,output reg [31 : 0] data_o ,// to exoutput reg [31 : 0] int_addr_o ,output reg int_assert_o);// ------------------------------------------------------------- //// next is define and parameter //// ------------------------------------------------------------- //// 定义中断的状态机localparam S_INT_IDLE = 4'b0001;localparam S_INT_SYNC_ASSERT = 4'b0010;localparam S_INT_ASYNC_ASSERT = 4'b0100;localparam S_INT_MRET = 4'b1000;// 写CSR寄存器状态定义localparam S_CSR_IDLE = 5'b00001;localparam S_CSR_MSTATUS = 5'b00010;localparam S_CSR_MEPC = 5'b00100;localparam S_CSR_MSTATUS_MRET = 5'b01000;localparam S_CSR_MCAUSE = 5'b10000;reg[3 : 0] int_state ;reg[4 : 0] csr_state ;reg[31 : 0] inst_addr ;reg[31 : 0] cause ;assign hold_flag_o = ((int_state != S_INT_IDLE) | (csr_state != S_CSR_IDLE))? 1 : 0 ;// 中断仲裁阶段always@(*)beginif(rst_n == 0)beginint_state <= S_INT_IDLE ;endelsebeginif(inst_i == 32'h0000_0073 || inst_i == 32'h0010_0073)beginif(div_started_i == 1)beginint_state <= S_INT_IDLE ;endelsebeginint_state <= S_INT_SYNC_ASSERT ;endendelse if( int_flag_i != 0 && global_int_en_i == 1)beginint_state <= S_INT_ASYNC_ASSERT ;endelse if(inst_i == 32'h3020_0073)beginint_state <= S_INT_MRET ; // 这个表示需要从异常状态恢复到正常程序状态endelsebeginint_state <= S_INT_IDLE ;endendend// 写CSR寄存器状态切换always@( posedge clk )beginif(rst_n == 0)begincsr_state <= S_CSR_IDLE ; // 状态机cause <= 0 ;inst_addr <= 0 ;endelsebegincase( csr_state )S_CSR_IDLE :begin// 同步中断if(int_state == S_INT_SYNC_ASSERT ) // 这部分的意思是如果系统捕获到了同步中断begincsr_state <= S_CSR_MEPC ;// 在中断处理函数里会将中断返回地址 - 4if (jump_flag_i == 1 )begininst_addr <= jump_addr_i - 4'h4 ;endelsebegininst_addr <= inst_addr_i ;endcase (inst_i)32'h0000_0073 : // ECALLbegincause <= 32'd11 ;end32'h010_0073 : // EBREAKbegincause <= 32'd3 ;enddefault:begincause <= 32'd10 ;endendcaseend // 异步中断else if (int_state == S_INT_ASYNC_ASSERT)begin// 定时器中断cause <= 32'h80000004;csr_state <= S_CSR_MEPC;if (jump_flag_i == 1)begininst_addr <= jump_addr_i;// 异步中断可以中断除法指令的执行,中断处理完再重新执行除法指令endelse if (div_started_i == 1)begininst_addr <= inst_addr_i - 4'h4;endelsebegininst_addr <= inst_addr_i;end// 中断返回endelse if (int_state == S_INT_MRET)begincsr_state <= S_CSR_MSTATUS_MRET;endendS_CSR_MEPC:// 在这个状态下,系统会将当前的中断返回地址保存到 MEPC,以便中断处理完后可以正确返回。begincsr_state <= S_CSR_MSTATUS;endS_CSR_MSTATUS:// 这个状态用于处理 MSTATUS 寄存器,通常涉及到恢复系统的中断使能和相关状态。begincsr_state <= S_CSR_MCAUSE;endS_CSR_MCAUSE:// MCAUSE 寄存器用于存储中断的原因。在这个状态下,系统会把中断的原因写入 MCAUSE 寄存器。begincsr_state <= S_CSR_IDLE;endS_CSR_MSTATUS_MRET:// 这是中断返回的特殊状态。执行完中断处理后,处理器需要返回到中断发生前的程序。// 这是通过恢复 MSTATUS 寄存器和中断前的状态来实现的。begincsr_state <= S_CSR_IDLE;enddefault:begincsr_state <= S_CSR_IDLE;endendcaseendend// 发出信号前 先写几个寄存器always@(posedge clk )beginif(rst_n == 0)beginwe_o <= 0 ;waddr_o <= 0 ;data_o <= 0 ;endelsebegin // 这一部分是按照上面的情况进行给予的case(csr_state )S_CSR_MEPC :begin// 在这个状态下,系统会将当前的中断返回地址保存到 MEPC,以便中断处理完后可以正确返回。we_o <= 1 ;waddr_o <= {20'h0 , 12'h341} ;data_o <= inst_addr ;end// 写中断产生的原因S_CSR_MCAUSE:beginwe_o <= 1 ;waddr_o <= {20'h0, 12'h342 };data_o <= cause ;end// 关闭全局中断S_CSR_MSTATUS:beginwe_o <= 1 ;waddr_o <= {20'h0, 12'h300};data_o <= {csr_mstatus[31:4], 1'b0, csr_mstatus[2:0]};end// 中断返回S_CSR_MSTATUS_MRET :beginwe_o <= 0 ;waddr_o <= {20'h0, 12'h300};data_o <= {csr_mstatus[31:4], csr_mstatus[7], csr_mstatus[2:0]};enddefault:beginwe_o <= 0 ;waddr_o <= 0 ;data_o <= 0 ;endendcaseendend// 发送中断信号给ex模块always@(posedge clk )beginif(rst_n == 0)beginint_assert_o <= 0 ;int_addr_o <= 0 ;endelsebegincase (csr_state)// 发出中断进入信号.写完mcause寄存器才能发S_CSR_MCAUSE:beginint_assert_o <= 1 ;int_addr_o <= csr_mtvec ;end// 发出中断返回信号S_CSR_MSTATUS_MRET:beginint_assert_o <= 1 ;int_addr_o <= csr_mepc ;enddefault:beginint_assert_o <= 0 ;int_addr_o <= 0 ;endendcaseendendendmodule
csr_reg.v
module csr_reg( // 寄存器input clk ,input rst_n ,// from exinput we_i , // ex模块写寄存器标志位input [31 : 0] raddr_i ,input [31 : 0] waddr_i ,input [31 : 0] data_i ,// from clintinput clint_we_i ,input [31 : 0] clint_raddr_i ,input [31 : 0] clint_waddr_i ,input [31 : 0] clint_data_i ,output global_int_en_o ,// to clintoutput reg [31 : 0] clint_data_o ,output [31 : 0] clint_csr_mtvec ,output [31 : 0] clint_csr_mepc ,output [31 : 0] clint_csr_mstatus ,// to exoutput reg [31 : 0] data_o) ;// --------------------------------------------- //// next is parameter and define //// --------------------------------------------- //reg [63 : 0] cycle ;reg [31 : 0] mtvec ;reg [31 : 0] mcause ;reg [31 : 0] mepc ;reg [31 : 0] mie ;reg [31 : 0] mstatus ;reg [31 : 0] mscratch ;assign global_int_en_o = (mstatus[3] == 1'b1)? 1: 0 ;assign clint_csr_mtvec = mtvec ;assign clint_csr_mepc = mepc ;assign clint_csr_mstatus = mstatus ;// next is main codealways@(posedge clk )beginif(rst_n == 0 )begincycle <= 0 ;endelsebegincycle <= cycle + 1 ;endend// write reg 写寄存器always@(posedge clk )beginif(rst_n == 0)beginmtvec <= 0 ;mcause <= 0 ;mepc <= 0 ;mie <= 0 ;mstatus <= 0 ;mscratch <= 0 ;endelsebeginif(we_i == 1)begin // 先响应ex写模块case( waddr_i[11 : 0])12'h305 :beginmtvec <= data_i ; // 处理器的中断向量地址寄存器。end12'h342:beginmcause <= data_i ; // 记录中断发生的原因。end12'h341:beginmepc <= data_i ; // 记录中断发生时的程序计数器(PC)值,即出错时程序的执行地址end12'h304:beginmie <= data_i ; // 机器模式下的中断使能寄存器end12'h300:beginmstatus <= data_i ; // 机器状态寄存器,包含有关当前处理器状态的信息end12'h340:beginmscratch <= data_i ; // 用于保存机器模式下的临时值enddefault:beginendendcaseendelse if( clint_we_i == 1)begincase( clint_waddr_i[11:0])12'h305 :beginmtvec <= clint_data_i ; // 处理器的中断向量地址寄存器。end12'h342:beginmcause <= clint_data_i ; // 记录中断发生的原因。end12'h341:beginmepc <= clint_data_i ; // 记录中断发生时的程序计数器(PC)值,即出错时程序的执行地址end12'h304:beginmie <= clint_data_i ; // 机器模式下的中断使能寄存器end12'h300:beginmstatus <= clint_data_i ; // 机器状态寄存器,包含有关当前处理器状态的信息end12'h340:beginmscratch <= clint_data_i ; // 用于保存机器模式下的临时值enddefault:beginendendcaseendendend// 读寄存器always@(*)beginif((waddr_i[11:0] == raddr_i[11:0]) &&(we_i == 1) )begindata_o <= data_i ;endelsebegincase(raddr_i[11 : 0])12'hc00 :begindata_o <= cycle[31:0] ;end12'hc80 :begindata_o <= cycle[63:32] ;end12'h305 :begindata_o <= mtvec ;end12'h342 :begindata_o <= mcause ;end12'h341 :begindata_o <= mepc ;end12'h304 :begindata_o <= mie ;end12'h300 :begindata_o <= mstatus ;end12'h340 :begindata_o <= mscratch ;enddefault:begindata_o <= 0 ;endendcaseendendalways@(*)beginif((clint_waddr_i[11:0] == clint_raddr_i[11:0]) &&(clint_we_i == 1) )beginclint_data_o <= clint_data_i ;endelsebegincase(clint_raddr_i[11 : 0])12'hc00 :beginclint_data_o <= cycle[31:0] ;end12'hc80 :beginclint_data_o <= cycle[63:32] ;end12'h305 :beginclint_data_o <= mtvec ;end12'h342 :beginclint_data_o <= mcause ;end12'h341 :beginclint_data_o <= mepc ;end12'h304 :beginclint_data_o <= mie ;end12'h300 :beginclint_data_o <= mstatus ;end12'h340 :beginclint_data_o <= mscratch ;enddefault:beginclint_data_o <= 0 ;endendcaseendendendmodule
ctrl.v
// 这是控制模块的设计
module ctrl(// from ex 来自于ex 的处理// input rst_n ,// 经过分析 这里也没有用到 rst_ninput jump_flag_i ,input [31 : 0] jump_addr_i , // 传递给PC 跳转的input hold_flag_ex_i , // 内部的比如说触发需要暂停这样的// from rib 总线input hold_flag_rib_i ,// from jtaginput jtag_halt_flag_i ,// clintinput hold_flag_clint_i ,// 输出 阶段output reg [2 : 0] hold_flag_o ,output reg jump_flag_o ,// to pcoutput reg [31 : 0] jump_addr_o) ;// 非常简单的组合逻辑阵列always@(*)beginjump_addr_o = jump_addr_i ;jump_flag_o = jump_flag_i ;// 默认不暂停hold_flag_o = 0 ;// 按照优先级程度处理不同模块if(jump_flag_i == 1 || hold_flag_ex_i == 1 || hold_flag_clint_i === 1)begin// 暂停整条流水线hold_flag_o = 3'b011 ;endelse if(hold_flag_rib_i == 1)beginhold_flag_o = 3'b001 ;endelse if(jtag_halt_flag_i == 1)beginhold_flag_o = 3'b011 ;endelsebeginhold_flag_o = 0 ;endend
endmodule
div.v
// 这里作者说使用试商法 实现整除
module div(input rst_n ,input clk ,// from exinput [31 : 0] dividend_i , // 被除数input [31 : 0] divisor_i , // 除数input start_i , // 开始信号input [2 : 0] op_i , // 声明具体执行的是什么指令input [4 : 0] reg_waddr_i , // 寄存器地址// to exoutput reg [31 : 0] result_o , // 除法结果output reg ready_o ,output reg busy_o ,output reg [4 : 0] reg_waddr_o // 存放的寄存器地址);// 状态定义localparam STATE_IDLE = 4'b0001 ;localparam STATE_START = 4'b0010 ;localparam STATE_CALC = 4'b0100 ;localparam STATE_END = 4'b1000 ;// ----------------------------------------------------- //// parameter and define //// ------------------------------------------------ //reg [2 : 0] op_r ;reg [31 : 0] dividend_r ;reg [31 : 0] divisor_r ;reg [3 : 0] state ;wire op_div = (op_r == 3'b100 ); // 100 有符号整数除法wire op_divu = (op_r == 3'b101 ); // 101 无符号整数除法wire op_rem = (op_r == 3'b110 ); // 110 有符号整数取余wire op_remu = (op_r == 3'b111 ); // 111 无符号整数取余reg [31 : 0] count ;reg [31 : 0] div_result ; // 暂存的商值reg [31 : 0] div_remain ; // 暂存的余数值reg [31 : 0] minuend ; // 表示当前的被减数reg invert_result ;wire [31 : 0] dividend_invert = (-dividend_r ) ;wire [31 : 0] divisor_invert = (-divisor_r) ;wire minuend_ge_divisor = minuend >= divisor_r ; // 比较被减数 和 除数wire [31 : 0] div_result_tmp = minuend_ge_divisor ?({div_result[30 : 0] ,1'b1}) : ({div_result[30 : 0] , 1'b0}) ;wire [31 : 0] minuend_sub_res = minuend - divisor_r ;wire [31 : 0] minuend_tmp = minuend_ge_divisor ? minuend_sub_res[30 : 0] : minuend[30 : 0] ;// ----------------------------------------------- //// next is main code //// ------------------------------------------------- //// 状态机的更新always@(posedge clk )beginif(rst_n == 0 )beginstate <= STATE_IDLE ;ready_o <= 0 ;result_o <= 0;div_result <= 0;div_remain <= 0;op_r <= 3'h0;reg_waddr_o <= 0;dividend_r <= 0;divisor_r <=0;minuend <= 0;invert_result <= 1'b0;busy_o <= 0;count <= 0;endelsebegincase(state)STATE_IDLE :beginif(start_i == 1)begin// 表示开始运算op_r <= op_i ;dividend_r <= dividend_i ;divisor_r <= divisor_i ;reg_waddr_o <= reg_waddr_i ; // 寄存器地址 方便回写state <= STATE_START ;busy_o <= 1 ;endelsebeginop_r <= 0 ;dividend_r <= 0 ;divisor_r <= 0 ;reg_waddr_o <= 0 ; // 寄存器地址 方便回写state <= 0 ;busy_o <= 0 ;endendSTATE_START :beginif(start_i == 1)begin // 开始运算//先假设除数为0if(divisor_r == 0 )beginif(op_div | op_divu )begin// 取商的操作 那么输出 32'hFFFFFFFF 表示 错误商值,在 除数为 0 时由硬件返回。result_o <= 32'hffff_ffff ;endelsebegin // 那就是取余数result_o <= dividend_r ; // 取余数的话 余下来的正好是被除数end// 上面两个都执行的在除数为0的情况下ready_o <= 1 ;state <= STATE_IDLE ;busy_o <= 0 ;endelsebegin// 这部分是除数不为0busy_o <= 1 ;state <= STATE_CALC ;count <= 32'h4000_0000 ;div_result <= 0 ;div_remain <= 0 ;// 先进行有符号的运算if(op_div | op_rem) // 有符号运算begin// 被除数求补码if(dividend_r[31] == 1)begindividend_r <= dividend_invert ;minuend <= dividend_invert[31] ; // 初始化为被除数的符号位endelsebeginminuend <= dividend_r[31] ; // 其实就是0endif(divisor_r[31] == 1)begindivisor_r <= divisor_invert ;endelsebeginminuend <= dividend_r[31] ;end// 判断计算的结果是否需要取补码if((op_div &&(dividend_r[31] ^ divisor_r[31] == 1))|| (op_rem && (dividend_r[31] == 1)))begininvert_result <= 1 ;endelsebegininvert_result <= 0 ;endendendendelsebeginstate <= STATE_IDLE ;result_o <= 0 ;ready_o <= 0 ;busy_o <= 0 ;endend// 这里是整体的计算部分STATE_CALC :beginif(start_i == 1)begindividend_r <= {dividend_r[30 : 0] , 1'b0} ;div_result <= div_result_tmp ;count <= {1'b0 , count[31 : 1] } ;if(|count)beginminuend <= {minuend_tmp[30 : 0] , dividend_r[30]} ;endelsebeginstate <= STATE_END ;if(minuend_ge_divisor)begindiv_remain <= minuend_sub_res ;endelsebegindiv_remain <= minuend ;endendendelsebeginstate <= STATE_IDLE ;result_o <= 0 ;ready_o <= 0 ;busy_o <= 0 ;endendSTATE_END :beginif(start_i == 1)beginready_o <= 1 ;state <= STATE_IDLE ;busy_o <= 0 ;if(op_div | op_divu )beginif(invert_result == 1)beginresult_o <= (-div_result) ;endelsebeginresult_o <= div_result ;endendelsebeginif(invert_result == 1)beginresult_o <= (-div_remain ) ;endelsebeginresult_o <= div_remain ;endendendelsebeginstate <= STATE_IDLE ;result_o <= 0 ;ready_o <= 0 ;busy_o <= 0 ;endendendcaseendend
endmodule
ex.v
module ex(input rst_n ,// from idinput [31:0] inst_i, // 指令内容 31input [31:0] inst_addr_i, // 指令地址 31input reg_we_i, // 是否写通用寄存器input [4:0] reg_waddr_i, // 写通用寄存器地址 4:0input [31:0] reg1_rdata_i, // 通用寄存器1输入数据 31input [31:0] reg2_rdata_i, // 通用寄存器2输入数据31input csr_we_i, // 是否写CSR寄存器input [31:0] csr_waddr_i, // 写CSR寄存器地址 31input [31:0] csr_rdata_i, // CSR寄存器输入数据31input int_assert_i, // 中断发生标志 -- 来自于 clintinput [31:0] int_addr_i, // 中断跳转地址31 -- 来自于 clintinput [31:0] op1_i, // 31input [31:0] op2_i,input [31:0] op1_jump_i,input [31:0] op2_jump_i,// from meminput wire[31:0] mem_rdata_i, // 内存输入数据// from divinput wire div_ready_i, // 除法运算完成标志input wire[31:0] div_result_i, // 除法运算结果input wire div_busy_i, // 除法运算忙标志input wire[4:0] div_reg_waddr_i,// 除法运算结束后要写的寄存器地址 4:0// to memoutput reg[31:0] mem_wdata_o, // 写内存数据 31output reg[31:0] mem_raddr_o, // 读内存地址output reg[31:0] mem_waddr_o, // 写内存地址 31output wire mem_we_o, // 是否要写内存output wire mem_req_o, // 请求访问内存标志// to regsoutput wire[31:0] reg_wdata_o, // 写寄存器数据 31:0output wire reg_we_o, // 是否要写通用寄存器output wire[4:0] reg_waddr_o, // 写通用寄存器地址 4:0// to csr regoutput reg[31:0] csr_wdata_o, // 写CSR寄存器数据output wire csr_we_o, // 是否要写CSR寄存器output wire[31:0] csr_waddr_o, // 写CSR寄存器地址 31:0// to divoutput wire div_start_o, // 开始除法运算标志output reg[31:0] div_dividend_o, // 被除数output reg[31:0] div_divisor_o, // 除数output reg[2:0] div_op_o, // 具体是哪一条除法指令output reg[4:0] div_reg_waddr_o,// 除法运算结束后要写的寄存器地址4:0// to ctrloutput wire hold_flag_o, // 是否暂停标志output wire jump_flag_o, // 是否跳转标志output wire[31:0] jump_addr_o // 跳转目的地址 31 : 0);wire[1:0] mem_raddr_index; //wire[1:0] mem_waddr_index; //wire[63:0] mul_temp; // 63:0wire[63:0] mul_temp_invert; //wire[31:0] sr_shift; //wire[31:0] sri_shift; //wire[31:0] sr_shift_mask; //wire[31:0] sri_shift_mask; //wire[31:0] op1_add_op2_res; //wire[31:0] op1_jump_add_op2_jump_res; //wire[31:0] reg1_data_invert; // 我合在一起写了wire[31:0] reg2_data_invert; //wire op1_ge_op2_signed; //wire op1_ge_op2_unsigned; //wire op1_eq_op2; //reg[31:0] mul_op1; //1reg[31:0] mul_op2; //wire[6:0] opcode; //wire[2:0] funct3; //wire[6:0] funct7; //wire[4:0] rd; //wire[4:0] uimm; //reg[31:0] reg_wdata; // 1reg reg_we; // 1reg[4:0] reg_waddr; // 4: 0reg[31:0] div_wdata; //1reg div_we; //1reg[4:0] div_waddr; // 4:0reg div_hold_flag; // 1reg div_jump_flag; // 1reg[31:0] div_jump_addr; //31 : 0reg hold_flag; //reg jump_flag; //reg[31:0] jump_addr; // 1reg mem_we; //reg mem_req; // 1reg div_start; // 1assign opcode = inst_i[6:0];assign funct3 = inst_i[14:12];assign funct7 = inst_i[31:25];assign rd = inst_i[11:7];assign uimm = inst_i[19:15];assign sr_shift = reg1_rdata_i >> reg2_rdata_i[4:0]; //assign sri_shift = reg1_rdata_i >> inst_i[24:20]; //assign sr_shift_mask = 32'hffffffff >> reg2_rdata_i[4:0]; //assign sri_shift_mask = 32'hffffffff >> inst_i[24:20]; //assign op1_add_op2_res = op1_i + op2_i; //assign op1_jump_add_op2_jump_res = op1_jump_i + op2_jump_i; //assign reg1_data_invert = ~reg1_rdata_i + 1; //assign reg2_data_invert = ~reg2_rdata_i + 1; //// 有符号数比较assign op1_ge_op2_signed = $signed(op1_i) >= $signed(op2_i); //// 无符号数比较assign op1_ge_op2_unsigned = op1_i >= op2_i; //assign op1_eq_op2 = (op1_i == op2_i); //assign mul_temp = mul_op1 * mul_op2; //assign mul_temp_invert = ~mul_temp + 1; //assign mem_raddr_index = (reg1_rdata_i + {{20{inst_i[31]}}, inst_i[31:20]}) & 2'b11; // //assign mem_waddr_index = (reg1_rdata_i + {{20{inst_i[31]}}, inst_i[31:25], inst_i[11:7]}) & 2'b11; // //assign div_start_o = (int_assert_i == 1)? 0: div_start; //assign reg_wdata_o = reg_wdata | div_wdata; //// 响应中断时不写通用寄存器assign reg_we_o = (int_assert_i == 1)? 0: (reg_we || div_we);assign reg_waddr_o = reg_waddr | div_waddr;// 响应中断时不写内存assign mem_we_o = (int_assert_i == 1)? 0: mem_we;// 响应中断时不向总线请求访问内存assign mem_req_o = (int_assert_i == 1)? 0: mem_req;assign hold_flag_o = hold_flag || div_hold_flag;assign jump_flag_o = jump_flag || div_jump_flag || ((int_assert_i == 1)? 1: 0);assign jump_addr_o = (int_assert_i == 1)? int_addr_i: (jump_addr | div_jump_addr);// 响应中断时不写CSR寄存器assign csr_we_o = (int_assert_i == 1)? 0: csr_we_i;assign csr_waddr_o = csr_waddr_i;// 处理乘法指令always @ (*)beginif ((opcode == 7'b0110011) && (funct7 == 7'b0000001))begincase (funct3)3'b000, 3'b011:begin // 3'b000 - 3'b011 无符号乘法 一个保留了低32位 一个保留了高32位mul_op1 = reg1_rdata_i;mul_op2 = reg2_rdata_i;end3'b010:begin // 3'b010 有符号与 无符号混合乘法mul_op1 = (reg1_rdata_i[31] == 1'b1)? (reg1_data_invert): reg1_rdata_i;mul_op2 = reg2_rdata_i;end3'b001:begin //3'b001 有符号乘法mul_op1 = (reg1_rdata_i[31] == 1'b1)? (reg1_data_invert): reg1_rdata_i;mul_op2 = (reg2_rdata_i[31] == 1'b1)? (reg2_data_invert): reg2_rdata_i;enddefault:beginmul_op1 = reg1_rdata_i;mul_op2 = reg2_rdata_i;endendcaseendelsebeginmul_op1 = reg1_rdata_i;mul_op2 = reg2_rdata_i;endend// 处理除法指令always @ (*)begindiv_dividend_o = reg1_rdata_i;div_divisor_o = reg2_rdata_i;div_op_o = funct3;div_reg_waddr_o = reg_waddr_i;if ((opcode == 7'b0110011) && (funct7 == 7'b0000001))begin // 7'b0110011div_we = 0;div_wdata = 32'h0;div_waddr = 32'h0;case (funct3)3'b100,3'b101, 3'b110, 3'b101:begin // 100 101 110 111div_start = 1;div_jump_flag = 1;div_hold_flag = 1;div_jump_addr = op1_jump_add_op2_jump_res;enddefault:begindiv_start = 0;div_jump_flag = 0;div_hold_flag = 0;div_jump_addr = 32'h0;endendcaseendelsebegindiv_jump_flag = 0;div_jump_addr = 32'h0;if (div_busy_i == 1)begindiv_start = 1;div_we = 0;div_wdata = 32'h0;div_waddr = 32'h0;div_hold_flag = 1;endelsebegindiv_start = 0;div_hold_flag = 0;if (div_ready_i == 1)begindiv_wdata = div_result_i;div_waddr = div_reg_waddr_i;div_we = 1;endelsebegindiv_we = 0;div_wdata = 32'h0;div_waddr = 32'h0;endendendend// `define INST_ADDI 3'b000// `define INST_SLTI 3'b010// `define INST_SLTIU 3'b011// `define INST_XORI 3'b100// `define INST_ORI 3'b110// `define INST_ANDI 3'b111// `define INST_SLLI 3'b001// `define INST_SRI 3'b101// 执行always @ (*)beginreg_we = reg_we_i;reg_waddr = reg_waddr_i;mem_req = 0; // 0csr_wdata_o = 32'h0;case (opcode)7'b0010011:begin // 7'b0010011case (funct3)3'b000:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_add_op2_res;end3'b010:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = {32{(~op1_ge_op2_signed)}} & 32'h1;end3'b011:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = {32{(~op1_ge_op2_unsigned)}} & 32'h1;end3'b100:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i ^ op2_i;end3'b110:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i | op2_i;end3'b111:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i & op2_i;end3'b001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = reg1_rdata_i << inst_i[24:20];end3'b101:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;if (inst_i[30] == 1'b1)beginreg_wdata = (sri_shift & sri_shift_mask) | ({32{reg1_rdata_i[31]}} & (~sri_shift_mask));endelsebeginreg_wdata = reg1_rdata_i >> inst_i[24:20];endenddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseend// // R type inst// `define INST_ADD_SUB 3'b000// `define INST_SLL 3'b001// `define INST_SLT 3'b010// `define INST_SLTU 3'b011// `define INST_XOR 3'b100// `define INST_SR 3'b101// `define INST_OR 3'b110// `define INST_AND 3'b1117'b0110011:begin // 7'b0110011if ((funct7 == 7'b0000000) || (funct7 == 7'b0100000))begincase (funct3)3'b000:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;if (inst_i[30] == 1'b0)beginreg_wdata = op1_add_op2_res;endelsebeginreg_wdata = op1_i - op2_i;endend3'b001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i << op2_i[4:0];end3'b010:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = {32{(~op1_ge_op2_signed)}} & 32'h1;end3'b011:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = {32{(~op1_ge_op2_unsigned)}} & 32'h1;end3'b100:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i ^ op2_i;end3'b101:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;if (inst_i[30] == 1'b1)beginreg_wdata = (sr_shift & sr_shift_mask) | ({32{reg1_rdata_i[31]}} & (~sr_shift_mask));endelsebeginreg_wdata = reg1_rdata_i >> reg2_rdata_i[4:0];endend3'b110:begin // 110jump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i | op2_i;end3'b111:begin //111jump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = op1_i & op2_i;enddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseendelse if (funct7 == 7'b0000001)begin// M type inst//`define INST_MUL 3'b000//`define INST_MULH 3'b001//`define INST_MULHSU 3'b010//`define INST_MULHU 3'b011//`define INST_DIV 3'b100//`define INST_DIVU 3'b101//`define INST_REM 3'b110//`define INST_REMU 3'b111case (funct3)3'b000:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = mul_temp[31:0];end3'b011:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = mul_temp[63:32];end3'b001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;case ({reg1_rdata_i[31], reg2_rdata_i[31]})2'b00:beginreg_wdata = mul_temp[63:32];end2'b11:beginreg_wdata = mul_temp[63:32];end2'b10:beginreg_wdata = mul_temp_invert[63:32];enddefault:beginreg_wdata = mul_temp_invert[63:32];endendcaseend3'b010:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;if (reg1_rdata_i[31] == 1'b1)beginreg_wdata = mul_temp_invert[63:32];endelsebeginreg_wdata = mul_temp[63:32];endenddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseendelsebeginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endend7'b0000011:begincase (funct3)3'b000:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;mem_req = 1; // 1mem_raddr_o = op1_add_op2_res;case (mem_raddr_index)2'b00:beginreg_wdata = {{24{mem_rdata_i[7]}}, mem_rdata_i[7:0]};end2'b01:beginreg_wdata = {{24{mem_rdata_i[15]}}, mem_rdata_i[15:8]};end2'b10:beginreg_wdata = {{24{mem_rdata_i[23]}}, mem_rdata_i[23:16]};enddefault:beginreg_wdata = {{24{mem_rdata_i[31]}}, mem_rdata_i[31:24]};endendcaseend3'b001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;mem_req = 1;mem_raddr_o = op1_add_op2_res;if (mem_raddr_index == 2'b0)beginreg_wdata = {{16{mem_rdata_i[15]}}, mem_rdata_i[15:0]};endelsebeginreg_wdata = {{16{mem_rdata_i[31]}}, mem_rdata_i[31:16]};endend3'b010:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;mem_req = 1;mem_raddr_o = op1_add_op2_res;reg_wdata = mem_rdata_i;end3'b100:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;mem_req = 1;mem_raddr_o = op1_add_op2_res;case (mem_raddr_index)2'b00:beginreg_wdata = {24'h0, mem_rdata_i[7:0]};end2'b01:beginreg_wdata = {24'h0, mem_rdata_i[15:8]};end2'b10:beginreg_wdata = {24'h0, mem_rdata_i[23:16]};enddefault:beginreg_wdata = {24'h0, mem_rdata_i[31:24]};endendcaseend3'b101:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;mem_req = 1;mem_raddr_o = op1_add_op2_res;if (mem_raddr_index == 2'b0)beginreg_wdata = {16'h0, mem_rdata_i[15:0]};endelsebeginreg_wdata = {16'h0, mem_rdata_i[31:16]};endenddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseend7'b0100011:begin // 7'b0100011case (funct3)3'b000:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;reg_wdata = 32'h0;mem_we = 1;mem_req = 1;mem_waddr_o = op1_add_op2_res;mem_raddr_o = op1_add_op2_res;case (mem_waddr_index)2'b00:beginmem_wdata_o = {mem_rdata_i[31:8], reg2_rdata_i[7:0]};end2'b01:beginmem_wdata_o = {mem_rdata_i[31:16], reg2_rdata_i[7:0], mem_rdata_i[7:0]};end2'b10:beginmem_wdata_o = {mem_rdata_i[31:24], reg2_rdata_i[7:0], mem_rdata_i[15:0]};enddefault:beginmem_wdata_o = {reg2_rdata_i[7:0], mem_rdata_i[23:0]};endendcaseend3'b001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;reg_wdata = 32'h0;mem_we = 1;mem_req = 1;mem_waddr_o = op1_add_op2_res;mem_raddr_o = op1_add_op2_res;if (mem_waddr_index == 2'b00)beginmem_wdata_o = {mem_rdata_i[31:16], reg2_rdata_i[15:0]};endelsebeginmem_wdata_o = {reg2_rdata_i[15:0], mem_rdata_i[15:0]};endend3'b010:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;reg_wdata = 32'h0;mem_we = 1;mem_req = 1;mem_waddr_o = op1_add_op2_res;mem_raddr_o = op1_add_op2_res;mem_wdata_o = reg2_rdata_i;enddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseend// `define INST_BEQ 3'b000//`define INST_BNE 3'b001//`define INST_BLT 3'b100//`define INST_BGE 3'b101//`define INST_BLTU 3'b110//`define INST_BGEU 3'b1117'b1100011:begin // 7'b1100011case (funct3)3'b000:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = op1_eq_op2 & 1;jump_addr = {32{op1_eq_op2}} & op1_jump_add_op2_jump_res;end3'b001:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = (~op1_eq_op2) & 1;jump_addr = {32{(~op1_eq_op2)}} & op1_jump_add_op2_jump_res;end3'b100:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = (~op1_ge_op2_signed) & 1;jump_addr = {32{(~op1_ge_op2_signed)}} & op1_jump_add_op2_jump_res;end3'b101:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = (op1_ge_op2_signed) & 1;jump_addr = {32{(op1_ge_op2_signed)}} & op1_jump_add_op2_jump_res;end3'b110:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = (~op1_ge_op2_unsigned) & 1;jump_addr = {32{(~op1_ge_op2_unsigned)}} & op1_jump_add_op2_jump_res;end3'b111:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = (op1_ge_op2_unsigned) & 1;jump_addr = {32{(op1_ge_op2_unsigned)}} & op1_jump_add_op2_jump_res;enddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseend// `define INST_JAL 7'b1101111// `define INST_JALR 7'b1100111//// `define INST_LUI 7'b0110111// `define INST_AUIPC 7'b0010111// `define INST_NOP 32'h00000001// `define INST_NOP_OP 7'b0000001// `define INST_MRET 32'h30200073// `define INST_RET 32'h00008067//// `define INST_FENCE 7'b0001111// `define INST_ECALL 32'h73// `define INST_EBREAK 32'h001000737'b1101111, 7'b1100111:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;jump_flag = 1;jump_addr = op1_jump_add_op2_jump_res;reg_wdata = op1_add_op2_res;end7'b0110111, 7'b0010111:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;jump_addr = 32'h0;jump_flag = 0;reg_wdata = op1_add_op2_res;end7'b0000001:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;end7'b0001111:beginhold_flag = 0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;jump_flag = 1;jump_addr = op1_jump_add_op2_jump_res;end// // CSR inst// `define INST_CSR 7'b1110011// `define INST_CSRRW 3'b001// `define INST_CSRRS 3'b010// `define INST_CSRRC 3'b011// `define INST_CSRRWI 3'b101// `define INST_CSRRSI 3'b110// `define INST_CSRRCI 3'b1117'b1110011:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;case (funct3)3'b001:begincsr_wdata_o = reg1_rdata_i;reg_wdata = csr_rdata_i;end3'b010:begincsr_wdata_o = reg1_rdata_i | csr_rdata_i;reg_wdata = csr_rdata_i;end3'b011:begincsr_wdata_o = csr_rdata_i & (~reg1_rdata_i);reg_wdata = csr_rdata_i;end3'b101:begincsr_wdata_o = {27'h0, uimm};reg_wdata = csr_rdata_i;end3'b110:begincsr_wdata_o = {27'h0, uimm} | csr_rdata_i;reg_wdata = csr_rdata_i;end3'b111:begincsr_wdata_o = (~{27'h0, uimm}) & csr_rdata_i;reg_wdata = csr_rdata_i;enddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseenddefault:beginjump_flag = 0;hold_flag = 0;jump_addr = 32'h0;mem_wdata_o = 32'h0;mem_raddr_o = 32'h0;mem_waddr_o = 32'h0;mem_we = 0;reg_wdata = 32'h0;endendcaseendendmodule
gen_diff.v
module gen_pipe_dff#(parameter DW = 32)(input wire clk ,input wire rst_n ,input wire hold_en ,input wire [DW-1 : 0] def_val ,input wire [DW-1 : 0] din ,output wire [DW-1 : 0] qout);reg [DW - 1 : 0] qout_r ;always@(posedge clk )beginif(rst_n == 0 | hold_en == 1)beginqout_r <= def_val ;endelsebeginqout_r <= din ;endendassign qout = qout_r ;
endmodule
id_ex.v
module id_ex(input rst_n ,input clk ,//来自于id_ex的完全输入input [31 : 0] inst_i ,input [31 : 0] inst_addr_i ,input reg_we_i ,input [4 : 0] reg_waddr_i ,input [31 : 0] reg1_rdata_i ,input [31 : 0] reg2_rdata_i ,input csr_we_i ,input [31 : 0] csr_waddr_i ,input [31 : 0] csr_rdata_i ,input [31 : 0] op1_i ,input [31 : 0] op2_i ,input [31 : 0] op1_jump_i ,input [31 : 0] op2_jump_i ,// 暂停设计input [2 : 0] hold_flag_i , // 流水线暂停标志output [31 : 0] inst_o ,output [31 : 0] inst_addr_o ,output reg_we_o ,output [4 : 0] reg_waddr_o ,output [31 : 0] reg1_rdata_o ,output [31 : 0] reg2_rdata_o ,output csr_we_o ,output [31 : 0] csr_waddr_o ,output [31 : 0] csr_rdata_o ,output [31 : 0] op1_o ,output [31 : 0] op2_o ,output [31 : 0] op1_jump_o ,output [31 : 0] op2_jump_o);wire hold_en = (hold_flag_i >= 3'b011) ;// ---- 果然这样的组合要写13个// -------------------------------------wire [31 : 0] inst ;gen_pipe_dff#(.DW ( 32 ))inst_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 32'h00000001 ), // 它其实是32'h0000_0001 我就是写成0.din ( inst_i ),.qout ( inst ));assign inst_o = inst ;// ----------------------------------// -------------------------------------wire [31 : 0] inst_addr ;gen_pipe_dff#(.DW ( 32 ))inst_addr_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( inst_addr_i ),.qout ( inst_addr ));assign inst_addr_o = inst_addr ;// ----------------------------------// -------------------------------------wire reg_we ;gen_pipe_dff#(.DW ( 1 ))reg_we_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 1'h0 ),.din ( reg_we_i ),.qout ( reg_we ));assign reg_we_o = reg_we ;// ----------------------------------// -------------------------------------wire [4 : 0] reg_waddr ;gen_pipe_dff#(.DW ( 5 ))reg_waddr_dff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 5'h0 ),.din ( reg_waddr_i ),.qout ( reg_waddr ));assign reg_waddr_o = reg_waddr ;// ----------------------------------// -------------------------------------wire [31 : 0] reg1_rdata ;gen_pipe_dff#(.DW ( 32 ))reg1_rdata_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 32'h0 ),.din ( reg1_rdata_i ),.qout ( reg1_rdata ));assign reg1_rdata_o = reg1_rdata ;// ----------------------------------// -------------------------------------wire [31 : 0] reg2_rdata ;gen_pipe_dff#(.DW ( 32 ))reg2_rdata_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( reg2_rdata_i ),.qout ( reg2_rdata ));assign reg2_rdata_o = reg2_rdata ;// ----------------------------------// -------------------------------------wire csr_we ;gen_pipe_dff#(.DW ( 1 ))csr_we_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 1'h0 ),.din ( csr_we_i ),.qout ( csr_we ));assign csr_we_o = csr_we ;// ----------------------------------// -------------------------------------wire [31 : 0] csr_waddr ;gen_pipe_dff#(.DW ( 32 ))csr_waddr_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( csr_waddr_i ),.qout ( csr_waddr ));assign csr_waddr_o = csr_waddr ;// ----------------------------------// -------------------------------------wire [31 : 0] csr_rdata ;gen_pipe_dff#(.DW ( 32 ))csr_rdata_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( csr_rdata_i ),.qout ( csr_rdata ));assign csr_rdata_o = csr_rdata ;// ----------------------------------// -------------------------------------wire [31 : 0] op1 ;gen_pipe_dff#(.DW ( 32 ))op1_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( op1_i ),.qout ( op1 ));assign op1_o = op1 ;// ----------------------------------// -------------------------------------wire [31 : 0] op2 ;gen_pipe_dff#(.DW ( 32 ))op2_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( op2_i ),.qout ( op2 ));assign op2_o = op2 ;// ----------------------------------// -------------------------------------wire [31 : 0] op1_jump;gen_pipe_dff#(.DW ( 32 ))op1_jump_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( op1_jump_i ),.qout ( op1_jump ));assign op1_jump_o = op1_jump ;// ----------------------------------// -------------------------------------wire [31 : 0] op2_jump ;gen_pipe_dff#(.DW ( 32 ))op2_jump_ff(.clk ( clk ),.rst_n ( rst_n ),.hold_en ( hold_en ),.def_val ( 0 ),.din ( op2_jump_i ),.qout ( op2_jump ));assign op2_jump_o = op2_jump ;// ----------------------------------
endmodule
regs.v
module regs(input clk ,input rst_n ,// from ex 在后续处理完成之后 写回reginput we_i , // 使能 | 地址 | 标志input [4 : 0] waddr_i ,input [31 : 0] wdata_i ,// from jtag 用于调试input jtag_we_i ,input [4 : 0] jtag_addr_i ,input [31 : 0] jtag_data_i ,output reg [31 : 0] jtag_data_o ,// from id1input [4 : 0] raddr1_i ,output reg [31 : 0] rdata1_o ,// from id2input [4 : 0] raddr2_i ,output reg [31 : 0] rdata2_o);reg [31 : 0] regs[0 : 31] ;// 写寄存器的发射信号always@(posedge clk )beginif(rst_n == 1)begin// 先进行exif((we_i == 1) && (waddr_i != 0))beginregs[waddr_i] <= wdata_i ;endelse if((jtag_we_i == 1) && ( jtag_addr_i != 0))beginregs[jtag_addr_i] <= jtag_data_i ;endendend// 读寄存器1always @ (*)beginif (raddr1_i == 0)beginrdata1_o <= 0;// 如果读地址等于写地址,并且正在写操作,则直接返回写数据endelse if (raddr1_i == waddr_i && we_i == 1)beginrdata1_o <= wdata_i;endelsebeginrdata1_o <= regs[raddr1_i];endend// 读寄存器2always @ (*)beginif (raddr2_i == 0)beginrdata2_o <= 0;// 如果读地址等于写地址,并且正在写操作,则直接返回写数据endelse if (raddr2_i == waddr_i && we_i == 1)beginrdata2_o <= wdata_i;endelsebeginrdata2_o <= regs[raddr2_i];endend// jtag 读寄存器always@(*)beginif(jtag_addr_i == 0)beginjtag_data_o <= 0 ;endelsebeginjtag_data_o <= regs[jtag_addr_i] ;endendendmodule
rib.v
//这一部分是RIB总线模块
module rib(input clk ,input rst_n ,// 主四 从六// master 0input [31 : 0] m0_addr_i ,input [31 : 0] m0_data_i ,output reg [31 : 0] m0_data_o ,input m0_req_i ,input m0_we_i ,// master 1input [31 : 0] m1_addr_i ,input [31 : 0] m1_data_i ,output reg [31 : 0] m1_data_o ,input m1_req_i ,input m1_we_i ,// master 2input [31 : 0] m2_addr_i ,input [31 : 0] m2_data_i ,output reg [31 : 0] m2_data_o ,input m2_req_i ,input m2_we_i ,// master 3input [31 : 0] m3_addr_i ,input [31 : 0] m3_data_i ,output reg [31 : 0] m3_data_o ,input m3_req_i ,input m3_we_i ,// slave 0output reg [31 : 0] s0_addr_o ,output reg [31 : 0] s0_data_o ,input [31 : 0] s0_data_i ,output reg s0_we_o ,// slave 1output reg [31 : 0] s1_addr_o ,output reg [31 : 0] s1_data_o ,input [31 : 0] s1_data_i ,output reg s1_we_o ,// slave 2output reg [31 : 0] s2_addr_o ,output reg [31 : 0] s2_data_o ,input [31 : 0] s2_data_i ,output reg s2_we_o ,// slave 3output reg [31 : 0] s3_addr_o ,output reg [31 : 0] s3_data_o ,input [31 : 0] s3_data_i ,output reg s3_we_o ,// slave 4output reg [31 : 0] s4_addr_o ,output reg [31 : 0] s4_data_o ,input [31 : 0] s4_data_i ,output reg s4_we_o ,// slave 5output reg [31 : 0] s5_addr_o ,output reg [31 : 0] s5_data_o ,input [31 : 0] s5_data_i ,output reg s5_we_o ,output reg hold_flag_o);// 访问地址的最高4位确定要访问的parameter [3 : 0] slave_0 = 4'b0000 ;parameter [3 : 0] slave_1 = 4'b0001 ;parameter [3 : 0] slave_2 = 4'b0010 ;parameter [3 : 0] slave_3 = 4'b0011 ;parameter [3 : 0] slave_4 = 4'b0100 ;parameter [3 : 0] slave_5 = 4'b0101 ;parameter [1 : 0] grant0 = 2'h0 ;parameter [1 : 0] grant1 = 2'h1 ;parameter [1 : 0] grant2 = 2'h2 ;parameter [1 : 0] grant3 = 2'h3 ;wire [3 : 0] req ;reg [1 : 0] grant ;// 主设备请求信号assign req = {m3_req_i , m2_req_i , m1_req_i , m0_req_i } ;// 接下来配置的是仲裁优先级// 优先级顺序按照3-0-2 - 1always@(*)beginif(req[3] == 1)begingrant = grant3 ; // 第三个hold_flag_o = 1 ;endelse if(req[0] == 1)begingrant = grant0 ; // 第三个hold_flag_o = 1 ;endelse if(req[2] == 1)begingrant = grant2 ; // 第三个hold_flag_o = 1 ;endelse begingrant = grant1 ; // 第三个hold_flag_o = 0 ;endend// 根据仲裁结果选择 访问从机always@(*)beginm0_data_o = 0 ;m1_data_o = 0 ;m2_data_o = 0 ;m3_data_o = 0 ;// 从机s0_addr_o = 0 ;s0_data_o = 0 ;s0_we_o = 0 ;s1_addr_o = 0 ;s1_data_o = 0 ;s1_we_o = 0 ;s2_addr_o = 0 ;s2_data_o = 0 ;s2_we_o = 0 ;s3_addr_o = 0 ;s3_data_o = 0 ;s3_we_o = 0 ;s4_addr_o = 0 ;s4_data_o = 0 ;s4_we_o = 0 ;s5_addr_o = 0 ;s5_data_o = 0 ;s5_we_o = 0 ;case(grant)grant0 :begincase(m0_addr_i[31:28])slave_0 :begins0_we_o = m0_we_i ;s0_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s0_data_o = m0_data_i ;m0_data_o = s0_data_i ;endslave_1 :begins1_we_o = m0_we_i ;s1_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s1_data_o = m0_data_i ;m0_data_o = s1_data_i ;endslave_2 :begins2_we_o = m0_we_i ;s2_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s2_data_o = m0_data_i ;m0_data_o = s2_data_i ;endslave_3 :begins3_we_o = m0_we_i ;s3_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s3_data_o = m0_data_i ;m0_data_o = s3_data_i ;endslave_4 :begins4_we_o = m0_we_i ;s4_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s4_data_o = m0_data_i ;m0_data_o = s4_data_i ;endslave_5 :begins5_we_o = m0_we_i ;s5_addr_o = {{4'h0} , {m0_addr_i[27 : 0]}} ;s5_data_o = m0_data_i ;m0_data_o = s5_data_i ;enddefault :beginendendcaseendgrant1 :begincase(m1_addr_i[31:28])slave_0 :begins0_we_o = m1_we_i ;s0_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s0_data_o = m1_data_i ;m1_data_o = s0_data_i ;endslave_1 :begins1_we_o = m1_we_i ;s1_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s1_data_o = m1_data_i ;m1_data_o = s1_data_i ;endslave_2 :begins2_we_o = m1_we_i ;s2_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s2_data_o = m1_data_i ;m1_data_o = s2_data_i ;endslave_3 :begins3_we_o = m1_we_i ;s3_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s3_data_o = m1_data_i ;m1_data_o = s3_data_i ;endslave_4 :begins4_we_o = m1_we_i ;s4_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s4_data_o = m1_data_i ;m1_data_o = s4_data_i ;endslave_5 :begins5_we_o = m1_we_i ;s5_addr_o = {{4'h0} , {m1_addr_i[27 : 0]}} ;s5_data_o = m1_data_i ;m1_data_o = s5_data_i ;enddefault :beginendendcaseendgrant2 :begincase(m2_addr_i[31:28])slave_0 :begins0_we_o = m2_we_i ;s0_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s0_data_o = m2_data_i ;m2_data_o = s0_data_i ;endslave_1 :begins1_we_o = m2_we_i ;s1_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s1_data_o = m2_data_i ;m2_data_o = s1_data_i ;endslave_2 :begins2_we_o = m2_we_i ;s2_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s2_data_o = m2_data_i ;m2_data_o = s2_data_i ;endslave_3 :begins3_we_o = m2_we_i ;s3_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s3_data_o = m2_data_i ;m2_data_o = s3_data_i ;endslave_4 :begins4_we_o = m2_we_i ;s4_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s4_data_o = m2_data_i ;m2_data_o = s4_data_i ;endslave_5 :begins5_we_o = m2_we_i ;s5_addr_o = {{4'h0} , {m2_addr_i[27 : 0]}} ;s5_data_o = m2_data_i ;m2_data_o = s5_data_i ;enddefault :beginendendcaseendgrant3 :begincase(m3_addr_i[31:28])slave_0 :begins0_we_o = m3_we_i ;s0_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s0_data_o = m3_data_i ;m3_data_o = s0_data_i ;endslave_1 :begins1_we_o = m3_we_i ;s1_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s1_data_o = m3_data_i ;m3_data_o = s1_data_i ;endslave_2 :begins2_we_o = m3_we_i ;s2_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s2_data_o = m3_data_i ;m3_data_o = s2_data_i ;endslave_3 :begins3_we_o = m3_we_i ;s3_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s3_data_o = m3_data_i ;m3_data_o = s3_data_i ;endslave_4 :begins4_we_o = m3_we_i ;s4_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s4_data_o = m3_data_i ;m3_data_o = s4_data_i ;endslave_5 :begins5_we_o = m3_we_i ;s5_addr_o = {{4'h0} , {m3_addr_i[27 : 0]}} ;s5_data_o = m3_data_i ;m3_data_o = s5_data_i ;enddefault :beginendendcaseenddefault :beginendendcaseendendmodule
readme.md
本设计创建于2024年11月7日 预计完成时间2025年1月1日
支持RV32IM指令集 所以指令集的扩展是来自于RV32IM
一开始书写的是pc_reg 用于地址的把控跳转
if_id更像是一个中介模块 用于传递ROM的信息给下一级id的
gen_pipe_dff 来自于 gen_diff.v
2024年11月14日 continue
- CSR是特权寄存器 其中的CSR是特权指令集中的一部分
- 在这一个操作中虽然我暂时不知道这个的内容,但是我知道地址 我是阻塞赋值,所以我在前面把rs1给到reg1_raddr_o 过一会就会把reg1_rdada_i的值拿回来
- | opcode (7) | rd (5) | funct3 (3) | rs1 (5) | rs2 (5) | funct7 (7) |
4.指令 功能描述
CSRRW 将寄存器 rs1 的值写入 CSR,同时将 CSR 原值写入目标寄存器 rd。
CSRRS 读取 CSR 原值并将其写入 rd,同时将 CSR 的指定位(由 rs1 指定)置 1。
CSRRC 读取 CSR 原值并将其写入 rd,同时将 CSR 的指定位清 0。
CSRRWI 与 CSRRW 类似,但立即数(imm)代替 rs1 作为写入 CSR 的值。
CSRRSI 与 CSRRS 类似,但立即数(imm)代替 rs1 作为位掩码。
CSRRCI 与 CSRRC 类似,但立即数(imm)代替 rs1 作为位掩码。
2024年11月20日 continue 开始写的是
2024年11月28日 continue
好久没看了 先去对整体的各个模块复习一下 -我觉得 这周能写的挺多的 28日周四
在pc_reg.v 中的
else if(hold_flag_i >= 3’b001 )
begin
pc_o <= pc_o ; // 这就是暂停保持的
这一段的代码 是为了后续流水线设置的方便
有人会问为什么设置了3bit 暂停而不是设置为1bit 因为后续的暂停标志位需要被if_id 和 id_ex使用
后续防线设置的阈值比001高 这样后面停PC就会被停下来 一级控制着一级 这就是三级流水线的定义
if_id 是流水线的暂存
reg是通用寄存器 reg模块里面的 两个寄存器意思指 一条指令里有可能会同时读取两个寄存器的值
regs.v 中的代码
else if (raddr2_i == waddr_i && we_i == 1)
beginrdata2_o <= wdata_i;
end
原因在于可能会有 译码阶段可能会需要用到上一条指令的结果
add x1 , x2 , x3
add x4 , x1 , x5
id.v是单纯的译码阶段的工作
— 11.28目前工作是 id_ex.v
这个模块的作用跟我想的一样 是作为过渡作用的 和 if_id 是用来传递存储到ex的信息的
// to ex
output reg [31 : 0] op1_o ,
output reg [31 : 0] op2_o ,
output reg [31 : 0] op1_jump_o ,
output reg [31 : 0] op2_jump_o ,
output reg [31 : 0] inst_o ,
output reg [31 : 0] inst_addr_o , // 指令地址
output reg [31 : 0] reg1_rdata_o , // 通用寄存器1数据
output reg [31 : 0] reg2_rdata_o , // 通用寄存器2数据
output reg reg_we_o , // 写通用寄存器标志
output reg [4 : 0] reg_waddr_o , // 写通用寄存器地址
output reg csr_we_o , // 写CSR寄存器标志
output reg [31 : 0] csr_rdata_o , // CSR寄存器数据
output reg [31 : 0] csr_waddr_o // 写CSR寄存器地址
就是这个
clint 的作用是 核心本地中断模块,对输入的中断请求信号进行总裁,产生最终的中断信号
2024年 11月29日
在编写 clint.v 模块
外设产生的中断信号与处理器的CSR寄存器紧密结合,共同构成了中断管理和中断响应机制。通过 CSR 寄存器的操作,系统能够记录中断的原因、保存当前的执行状态,并根据需要使能或屏蔽中断。这一过程确保了中断能够按预期进行处理,且在处理完中断后,处理器能够从正确的位置恢复并继续执行。
在你的系统架构中,clint 模块就是用来处理这些中断请求,并与 CSR 寄存器交互的核心组件,而 CSR 寄存器则承担了中断的状态管理和控制任务。
所以 clint是专门处理中断控制与管理的模块,负责与CSR寄存器相关联
为什么这个设计放在了第二阶段 ?
因为 中断是一个外部事件,通常是异步的,即它的发生时刻并不一定与指令的执行同步。在处理器流水线中,中断需要在适当的时机被捕获和响应,而选择在 ID 阶段进行中断的处理有助于确保中断能够及时响应,同时避免干扰指令的正常执行
问答? 还是有点疑惑的地方 就是说clint处理两种不同的中断嘛 一种是指令本身提供的中断 还有一种是外设提供的嘛?指令的中断从id解析出来之后反正都会传递给csr_reg 然后再给到clint 而 还有一种是外设的中断 这种需要clint写完传递给 csr_reg嘛 ???
–>
因此,clint 在处理中断时,确实需要通过 csr_reg 读取和更新中断相关的寄存器,如 mtvec(中断向量)、mepc(中断返回地址)和 mcause(中断原因)。而外设中断(来自 int_flag_i)也是通过 clint 来处理和管理,最终影响 csr_reg 中的寄存器,指导处理器如何跳转到中断处理程序。
所以,整体上你的理解是对的,clint 处理的是两类中断:指令中断和外设中断,并且它依赖于 csr_reg 来管理中断状态的保存和恢复。
理解之后 我决定先编写 csr_reg.v
问题 : ? 所以说ex是一个唯一的处理阶段的模块,处理了中断的信息,中断需要操作都经由ex模块来完成,clint是一个中断仲裁分析模块,会把外部的外设 指令的信息接收处理了 还给csr_reg 寄存器,最后由ex模块接收到详细处理嘛
–>
ex 模块是唯一的处理阶段模块,负责执行指令并处理与中断、异常相关的操作。
clint 是外部中断的控制器,它接收外设中断信号并将其信息传递给 csr_reg,然后 ex 模块在执行时根据这些信息进行中断处理。
csr_reg 存储与中断、异常相关的控制状态信息,而 ex 模块会根据这些信息执行实际的中断处理逻辑。
问题 : 为什么在写csr寄存器的时候会优先响应ex写模块 ?
–>
中断和异常处理可能会影响执行:当处理器需要处理中断或异常时,它会修改一些控制寄存器。这个修改必须在当前指令执行完后进行。也就是说,EX阶段的修改优先级更高,因为它关系到当前指令是否能顺利执行。比如,如果发生中断时,必须知道从哪里跳转到异常处理函数(mepc),但这是在指令执行完毕之后才处理的。
问题 : 所以这里是有变换的先后要求的嘛 先是保证除法的正常进行 然后是同步的指令 然后是core的外部中断吗 ?
–>
首先,需要确保除法指令等较长周期操作的完成,因此在除法未完成时,不会处理中断。
其次,优先处理中同步中断(如 ECALL、EBREAK),这些通常与程序的控制流相关,可能是系统调用或异常。
然后,处理外部的异步中断(来自硬件或外部设备),这些通常在同步中断后处理。
最后,处理 MRET 指令,恢复到机器模式继续执行。
问题 : 所以跳转中断处理是一个设定的特殊的地址 特殊的指令 包括特殊的 信息,PC是会不断自增的 jump_addr_i 的值在ex阶段会被确定成需要跳转的中断处理地址 ,但是 PC是会不断自增+4 的 ,所以回退一个位置,这样在处理的时候jump的地址就会是需要跳转的地址了 ?
—>
—>
这里的东西 我先按下不表 等我看到后面分析的时候自然而然懂了
我的理解是这样的
GPT 也觉得我的没问题
jump_addr_i - 4 是为了确保跳转到中断处理程序时,从发生中断的正确位置继续执行。
跳转的目标地址 jump_addr_i 是由中断向量表决定的,通常是中断处理程序的入口。
在 EX 阶段,处理器决定了跳转地址,而跳转到中断处理程序时需要回退 4 字节,因为 PC 是自动自增的。
2024年11月30日 -> 2024年12月1日 对前模块进行小总结
稍微带入一点整体的思想观念
注意 在id模块 – J型指令中 有这么一段代码
7’b1100_111 : // JALR 功能:PC = (rs1 + imm) & ~1,并将下一条指令地址存入 rd。
begin
reg_we_o = 1 ;
reg_waddr_o = rd ;
reg1_raddr_o = rs1 ;
reg2_raddr_o = 0 ;
op1_o = inst_addr_i ;
op2_o = 32’h4 ;
op1_jump_o = reg1_rdata_i ;
op2_jump_o = {{20{inst_i[31]}}, inst_i[31:20]};
end
这里所说的 op1_o 和 op2_o 是操作数
用这两个数值 来计算出 rd的值 inst_addr_i + 4 是下一条指令的地址
op1_jump_o 与 op2_jump_o操作 的得到 下一条指令的内容
---注明 读寄存器 不需要判断信号 直接读就可以了 问题 : 为什么已经传递了op1 op1_jump_o 等信息 还要把 寄存器的数据 reg1_rdata_o给ex模块?--> 回答: 将寄存器的数据 reg1_rdata_o 和 reg2_rdata_o 也传递给 EX 模块,是为了提供灵活性,允许在 EX 阶段根据指令的具体需求直接使用原始的寄存器数据。这种设计既考虑了性能优化,又兼顾了流水线设计的实际需求。如果流水线遇到特殊情况(例如复杂指令),EX 阶段可以直接使用寄存器的原始数据,而无需回退到 ID 阶段重新解码。
OK OK 我对中断充满了很多的疑问 我准备等 ex.v 完全结束之后开始了解这中断变换的含义
2024年12月1日 开始写执行模块
2024年 12月2日 开始编写执行模块
2024年 12月4日 备受折磨的12月 开始继续 ex模块的书写
2024年12月9日 继续进行ex.v模块的编写
问题? 没听懂字节对齐的含义 因为reg1_rdata_i + {{20{inst_i[31]}}, inst_i[31:20]} 这不是一个32位的数嘛 这是内存的地址嘛 ? 是不是因为内存的数据都是32位访问的 所以地址是4字节4字节变换的,所以传入的非4的倍数 就是定位到内存的哪一个具体的八位嘛 ?
回答 : 是的,你的理解正在接近正确!
参数的含义
jump_flag 跳转信号
hold_flag 跳转信号
jump_addr 跳转信号
mem_wdata_o 内存
mem_raddr_o
mem_waddr_o
mem_we
reg_wdata 寄存器数据信号
2024年12月11日 继续ex.v的编写
对于模块的第755行 其中 写成 jump_addr = {32{op1_eq_op2}} & op1_jump_add_op2_jump_res; 是为了一个保护作用
当不触发时,跳转地址设置到了保护的0
0 可以作为一个特殊寄存器的存在专门用来存储复位的信息情况
观察后面写的几个程序 才发现前面id分解的时候 全部拆开写的重要性
— 2024年12.11 21:43 结束到 CSR寄存器
希望明天上午把ex里面的 CSR寄存器搞完 和前面关系搞清楚
最后再检查一遍 下班
2024年12月12日 继续ex.v的CSR编写
问题 : 我现在有一个问题 就是 除法指令也都是写入reg寄存器的嘛 因为这里的复位的时候给reg_wdata 和 div_wdata都给0 所以本质上这两个数据再一条指令处理的时候不会赋值 ?
回答 : 关键: 在一个周期内,这两个信号 只能有一个有效数据源,不会出现两个同时非零的情况
现在开始讲述一下之前没懂的几个问题
解析
在clint 的第83行 if(inst_i == 32’h0000_0073 || inst_i == 32’h0010_0073)
这两个指令是直接从ROM中读出来的 系统指令
ECALL 和 EBACK
2024年12月14日 经过那么长时间的学习 终于把这个问题给搞清楚
我的理解 :
所以 我来说一下 我的理解 ,现在说同步中断的概念,首先假设输入的上一条是跳转指令,首先在系统内部会有关于clint 包括csr_reg寄存的读写的信息的,这是系统的概念,这一部分的执行,完全依靠于ex模块的读取和写入,和其他的具体具体跳转没有任何关系,接下来假设上一条的是跳转指令,因为是三级流水线的概念,所以clint的输入来自于id,其实是 ex阶段的下一个指令,所以当输入ecall指令时候,会立即读取csr_reg 寄存器中的信息,这些信息是由一开始就是我上面说的那段话传递进去的,接下来的会在这个阶段上把地址mevpc通过ex阶段完全发送给pc ,这时候并不会暂停流水线,会根据这个指令完成设计,接下来是当指令完成之后会重新回到ret保存的地址,因为Pc会自动+4 ,对于原来的指令Inst+4 就把当前的这个中断指令跳过了,对于上一条是jump的指令,那么这里-4也很合理了
GPT : 你的理解是很清晰的!让我总结并补充一下你说的内容,确保概念清晰
吃饭去了 接下来速速的完成其他模块的编写
2024年12月14日 这次编写的是 同为ex阶段的ctrl模块
ctrl 在了解清楚另外几个模块之后
现在编写div模块 就是 ex后面的除法单元
这边介绍一下试熵法的原理和使用规范
试熵法的基本步骤:**
- 初始化:**
被除数(Dividend)和除数(Divisor)被加载到寄存器中。
商(Quotient)和余数(Remainder)寄存器清零 - 试商(Trial Division):**
从被除数中提取一个对应除数长度的高位部分,称为“被减数”(Minuend)。
假设当前位的商为1,并尝试执行 减法运算:
( \text{Minuend} - \text{Divisor} \ - 判断结果:**
如果减法结果为非负,则 试商成功,当前位的商值为1。
如果减法结果为负,则 试商失败,恢复之前的被减数,当前位的商值为0 - 商寄存器更新:**
将当前商位移入商寄存器,余数寄存器存储减法的结果(如果减法成功) - 重复步骤 2 - 4:**
对被除数的下一个最高位重复上述过程,直到处理完成 - 最终结果:**
商寄存器中保存最终商值。
余数寄存器中保存剩余的余数。
示例:
假设我们要计算 ( 1011_2 \div 10_2 )(即 11 除以 2)。
步骤:
- 初始被除数:
1011,除数:10。 - 提取最高位,得到
10,尝试减去10,成功,商位为1。 - 移位提取下一位
1,组合成01,无法减去10,商位为0。 - 提取最后一位
1,组合成11,减去10成功,商位为1。
最终结果:
- 商:
101_2(即5) - 余数:
01_2(即1)
试商的除法 就是现实版的长除法的使用
自己拿笔推一下就出来了
2024年12月15日 上面的设计基本完成了
剩下top顶层 还有 rib总线的书写
现在是17:14先去吃饭
回来完成 rib总线的部分处理
完成之后 全部内容 检查一遍开始撰写top
2024年12月16日 下面该到了 tinyriscv的顶层模块的设计了
现在是北京时间2024年12月16日 完成了整个的主模块 risc-v的撰写
还剩下粗略15天 用来完成其他部分的书写
先去完成 外设的部分的设计
2024年12月16日
下面先进行 6个外设的设计
ROM
RAM
timer
uart_tx
gpio
spi
啊啊啊啊回归了
2024年12月22日 经过了长时间的蛰伏该到了
不会12月底来不及完成吧
先抓紧了
问题 : ok 我不懂的就是这个图上为什么说 ROM的大小是256MB,给他分配了 0x0000_0000到0x1000_0000的呢 ?
物理 ROM 实际大小可能远小于 256MB,比如你的设计中实际只有 16KB (4096 × 4B) 的 ROM。
剩余未用的空间是为了方便扩展或地址解码简化而划分出来的。
问题 : 那我有一个问题 就是说如何 准确完成CPU的处理,再解析C语言的时候就规定了你的 举例0x1000_0000到0x2000_0000是GPIO这样子嘛 ?
这个问题我想起来了,在C语言的define中 确实有这样的定义 ,
C 语言中,通过指针直接访问这些映射地址。例如 GPIO 寄存器的定义如下:
c
复制代码
#define GPIO_BASE 0x40000000 // GPIO 基地址
#define GPIO_DIR (volatile unsigned int)(GPIO_BASE + 0x00) // 方向寄存器
#define GPIO_OUT (volatile unsigned int)(GPIO_BASE + 0x04) // 输出寄存器
#define GPIO_IN (volatile unsigned int)(GPIO_BASE + 0x08) // 输入寄存器
示例操作:
c
复制代码
void gpio_init() {
GPIO_DIR = 0xFF; // 将所有 GPIO 设置为输出
}
void gpio_set(uint32_t value) {
GPIO_OUT = value; // 设置 GPIO 输出值
}
uint32_t gpio_read() {
return GPIO_IN; // 读取 GPIO 输入值
}
解析:
寄存器地址计算: 通过基地址 + 偏移量定义具体寄存器。
volatile 关键字: 确保每次访问直接读写硬件地址,而不是缓存值。
疑惑解除 开始继续工作
timer模块的作用
基于输入时钟信号进行计数,当计数值达到预设值时触发一个 中断信号,实现定时器功能。
飞快 现在在 使用 uart 端口
uart 有点长 所以先弄 gpio 吧 弄 GPIO的时候 感觉 死去的记忆在攻击我了
现在写完了 gpio 再写 uart
2024年12月23日 继续完成uart外设的编写
问题 : 是不是这个意思 下降沿呢 是和外面交流接收的 ,再在这里设计上升沿 是为了给下面uart传入寄存器做准备的 ?
是的,你的理解非常正确!
完成uart模块
2024年 12月23日 ----- spi.v 继续做另一个外设的设计
2024年 12月25日 ----- 继续spi
问题 : 那么其实说到底,同步进行输入输出的 并不是同一批数据喽 ?
回答 : 没错!
问题 : 那么我有一个问题 ,就是这里没有握手 或者数据对齐的功能 而且这个spi是不断的传输的,那么万一传输错误了 错就错了 是这个意思嘛 ?
回答 : 是的,你理解得完全正确!
CPOL 和 CPHA 控制具体行为
模式 CPOL CPHA 时钟空闲状态 采样边沿 输出边沿
模式 0 0 0 低电平 上升沿 (clock=1) 下降沿 (clock=0)
模式 1 0 1 低电平 下降沿 (clock=0) 上升沿 (clock=1)
模式 2 1 0 高电平 下降沿 (clock=0) 上升沿 (clock=1)
模式 3 1 1 高电平 上升沿 (clock=1) 下降沿 (clock=0)
示例分析:CPOL = 0, CPHA = 0(模式 0)
**时钟空闲状态:**低电平。
**数据输出:**下降沿 (clock=0) 更新 MOSI。
**数据采样:**上升沿 (clock=1) 从 MISO 读取数据
2024年12月25日 21:31 目前完成的进度是外设全部书写完毕
明天开始对uart模块进行创作
我把整个jtag删去了 因为我手头没有对JTAG的调试工具
把这个完成应该就差不多了
over 加油
2024年12月26日 开始编写其中使用的 UART_DEBUG 下载单元
状态机的第一步是往 UART的ctrl 地址 写入 3也就 11 表示
启用了TX使能 和 RX 使能
问题 : 那么对于下载而言的话 ,这个写入UART 的我看就是一个输出信号 是专门用来配置连接到FPGA上的UART串口模块吗 ?
回答 : 这段代码中的 UART 配置和数据传输信号 是用来控制连接到 FPGA 上的 UART 串口模块 的。
问题 : 这个地址status_reg是什么作用的 ,为什么写完首位就要清除接收溢出标志,这个状态寄存器有什么作用 ?
回答 : 第一步:
将地址 mem_addr_o 设置为 UART_STATUS_REG,准备访问状态寄存器。
第二步:
写入数据 32’h0,清除所有错误标志,包括溢出标志。
第三步:
写使能 mem_we_o 设置为 1,确保写入数据有效。
第四步:
状态机进入 S_WAIT_BYTE,开始等待 UART 模块接收新的字节数据。
位位置 标志名称 描述
0 TX_EMPTY_FLAG 发送 FIFO 空标志,表示发送缓冲区为空,可以写入新数据。
1 RX_FULL_FLAG 接收 FIFO 满标志,表示接收缓冲区已满,需要读取数据以避免溢出。
2 RX_OVER_FLAG 接收溢出标志,表示在读取数据之前 UART 收到更多数据,导致数据丢失。
3 PARITY_ERROR_FLAG 奇偶校验错误标志,接收数据时检测到校验错误。
4 FRAME_ERROR_FLAG 帧错误标志,接收数据的起始位或停止位异常。
5 BREAK_DETECT_FLAG 中断检测标志,检测到线路上出现中断信号。
其他位 保留位 未来扩展或保留位,默认忽略。
-> 问题 : ok 我明白了我们这个uart其实是跟外部的连接到FPGA端口的这个连接器的uart模块,我们读取的也就是这里的uart寄存器的信息,一旦上位机发起写入信号,那么其实这个的uart的寄存器的某些位就会发生变换,所以其实我们读取的是这个变化信息 ?
回答 ;
没错,你理解得很准确!
2024年12月26日 差不多可以说是结束了 现在把顶层写好找个tb验证一下
2024年12月27日 我还是怂了 我现在要把这个的JTAG端口加上去了 不然真的没法调试了
我没看 但是把jtag_dm.v这个模块先抄上去
这里面用到了 full_handshake_rx.v
– full_handshake_rx.v
– 现在加上 fuull_handshake_tx.v
– 加上了 jtag_top.v
–.–.–.–.—.----
再做一个合集把 剩下的几个module 写进去
2024年12月27日
准备把代码进行替换验证了
第一次
if_id 原代码 修改了
这个官方的 tb里 我把 最后的rst换成了rst_n
id 原代码也修改了
tinyrisc.v修改
tinyriscvsoc.v 修改
第二次
修改了id_ex
第三次
修改 tinyriscv.v
2024年12月28日 基本完成功能
其他的几个外设 我懒得去验证了 到时候想用直接拿过来
对于RTL部分核心的代码是完全无误的
2024.11.7 —> 2024.12.28
相关文章:
【【深入浅出TinyRisc-v】】
深入浅出TinyRisc-v 本代码参考于 https://gitee.com/liangkangnan/tinyriscv 自己理解之后又重新写了一遍 tinyriscv.v // 涓嬮潰鏄鏁翠釜top妯″潡鐨勪功鍐? module tinyriscv(input clk ,input rst_n …...
常见的限流算法
常见的限流算法 限流的定义固定窗口算法滑动窗口算法漏桶算法(推荐)令牌桶算法(推荐)限流粒度本地限流(单机限流)分布式限流(多机限流)分布式限流的实现 限流的定义 限流,也称流量控制。是指系统…...
【Leetcode 每日一题】3159. 查询数组中元素的出现位置
问题背景 给你一个整数数组 n u m s nums nums,一个整数数组 q u e r i e s queries queries 和一个整数 x x x。 对于每个查询 q u e r i e s [ i ] queries[i] queries[i],你需要找到 n u m s nums nums 中第 q u e r i e s [ i ] queries[i] q…...
xadmin后台首页增加一个导入数据按钮
xadmin后台首页增加一个导入数据按钮 效果 流程 1、在添加小组件中添加一个html页面 2、写入html代码 3、在urls.py添加导入数据路由 4、在views.py中添加响应函数html代码 <!DOCTYPE html> <html lang...
行为树详解(5)——事件驱动
【分析】 如果行为树的节点很多,那么会存在要经过很多节点才会走到动作节点的情况。显然,性能上不如状态机。 每帧都需要重新遍历一系列节点才会走到动作节点,而实际上很多条件节点在数帧内不会有变化,这是造成性能问题的重要原…...
3.若依前端项目拉取、部署、访问
因为默认RuoYi-Vue是使用的Vue2,所以需要另外去下载vue3来部署。 拉取代码 git clone https://gitee.com/ys-gitee/RuoYi-Vue3.git 安装node才能执行npm相关的命令 执行命令npm install 如果npm install比较慢的话,需要添加上国内镜像 npm install --registrhttp…...
Debian操作系统相对于Ubuntu有什么优势吗?
更高的稳定性:Debian 以其出色的稳定性闻名,得益于严格的软件包测试和发布流程。其稳定版经过长时间测试与验证,确保了系统的高度稳定,更适合对稳定性要求极高的长期运行服务器环境。而 Ubuntu 虽有稳定版本,但更新周期…...
【漏洞复现】CVE-2015-3337 Arbitrary File Reading
漏洞信息 NVD - CVE-2015-3337 Directory traversal vulnerability in Elasticsearch before 1.4.5 and 1.5.x before 1.5.2, when a site plugin is enabled, allows remote attackers to read arbitrary files via unspecified vectors. 在安装了具有“site”功能的插件以…...
win10、win11-鼠标右键还原、暂停更新
系统优化 win 10jihuo win 11jihuo鼠标右键还原暂停更新 update 2024.12.28win 10 jihuo winx,打开powershell管理员,输入以下命令,选择1并等待 irm https://get.activated.win | iex参考:https://www.bilibili.com/video/BV1TN411M72J/?sp…...
FFmpeg来从HTTP拉取流并实时推流到RTMP服务器
当使用FFmpeg来从HTTP拉取流并实时推流到RTMP服务器时,你可以使用以下命令: ffmpeg -i http://输入流地址 -c:v copy -c:a copy -f flv rtmp://RTMP服务器地址/应用名称/流名称 这是一个基本的命令示例,其中: - -i http://输入流地…...
Quo Vadis, Anomaly Detection? LLMs and VLMs in the Spotlight 论文阅读
文章信息: 原文链接:https://arxiv.org/abs/2412.18298 Abstract 视频异常检测(VAD)通过整合大语言模型(LLMs)和视觉语言模型(VLMs)取得了显著进展,解决了动态开放世界…...
Rust : tokio中select!
关于tokio的select宏,有不少的用途。包括超时和竞态选择等。 关于select宏需要关注,相关的异步条件,会同时执行,只是当有一个最早完成时,会执行“抛弃”和“对应”策略。 说明:对本文以下素材的来源表示感…...
【hackmyvm】hacked靶机wp
tags: HMVrootkitDiamorphine Type: wp 1. 基本信息^toc 文章目录 1. 基本信息^toc2. 信息收集2.1. 端口扫描2.2. 目录扫描2.3. 获取参数 3. 提权 靶机链接 https://hackmyvm.eu/machines/machine.php?vmHacked 作者 sml 难度 ⭐️⭐️⭐️⭐️️ 2. 信息收集 2.1. 端口扫描…...
MaixBit k210学习记录
开发背景:Window系统主机,在主机上安装了虚拟机(VirtualBoxUbuntu23.04) 目标实现:在虚拟机(Ubuntu)中,实现对Maix bit(k210)开发板的开发 虚拟机的安装参考…...
Wordperss漏洞 DeDeCMS漏洞
Wordperss漏洞 环境搭建 #执⾏命令 cd /vulhub/wordpress/pwnscriptum docker-compose up -d #靶场地址 http://8.155.7.173:8080/wp-admin/ 注册账号 登录 漏洞一:后台修改模板拿WebShell 步骤一:思路是修改其WP的模板写入⼀句话木马后门并访问其文件…...
如何构建有效的AI Agents:从复杂到简约——深度解读Claude实践总结《Building effective agents》(上)
在人工智能技术日新月异的今天,大语言模型(LLM)已经成为技术创新的热点。 然而,在追逐技术前沿的热潮中,我们是否忽视了工程设计的本质? 作为全球人工智能领域的领军企业之一,Anthropic以其在AI安全和伦理方面的深入…...
git status 耗时
某个git库每次status一下就是半小时起步,gc后还是没有效果,后来排查记录发现某笔记录提交几百G的冗余文件,虽然revert了,但是还是存在库中,遂如下清理: # 查找大文件 git verify-pack -v .git/objects/pac…...
C++进阶重点知识(一)|智能指针|右值|lambda|STL|正则表达式
目录 1智能指针1.shared_ptr1.1 shared_ptr的基本用法使用shared_ptr要注意的问题运用 2.unique_ptr独占的智能指针示例:管理动态内存 3.weak_ptr弱引用的智能指针weak_ptr的基本用法lock 的作用:weak_ptr返回this指针weak_ptr解决循环引用问题weak_ptr使…...
OSCP打靶大冒险之Solidstate:多端口获取信息,shell逃逸,计划任务提权
声明! 学习资源来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...
八股(One Day one)
最近老是看到一些面试的视频,对于视频内部面试所提到的八股文,感觉是知道是什么,但是要说的话,却又不知道该怎么说(要不咋称之为八股文呢),所以就想到写一篇八股文总结的博客,以便进…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...
