深入浅出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:0
input [31 : 0] jtag_reg_data_i , // jtag妯″潡鍐欏瘎瀛樺櫒鏁版嵁 31 : 0
input 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_id
if_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_ex
id_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)
begin
pc_o <= 32'h0 ;
end
else if(jump_flag_i == 1)
begin
pc_o <= jump_addr_i ;
end
else if(hold_flag_i >= 3'b001 )
begin
pc_o <= pc_o ; // 这就是暂停保持的
end
else
begin
pc_o <= pc_o + 4'h4 ;
end
end
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 reg
input [31 : 0] csr_rdata_i ,
// from ex 跳转标志
// input ex_jump_flag_i , // 跳转标志
//这一个是根本用不到的数据
//
// to reg
output reg [4 : 0] reg1_raddr_o , // 读通用寄存器1地址
output reg [4 : 0] reg2_raddr_o ,
// to csr reg
output reg [31 : 0] csr_raddr_o ,
// 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寄存器地址
) ;
// 还是先写整体的主题内容
// 对于指令字段的分析
// [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@(*)
begin
inst_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 type
case(funct3)
3'b000 , // addi addi a0, a1, 0x5 //a0 = a1 + 0x5
3'b010 , // slti a0, a1, 0x05 //a1 < 0x05 ? a0 = 1 : a0 = 0
3'b011 , // SLTIU
3'b100 , // XORI
3'b110 , // ORI
3'b111 , // ANDI
3'b001 , // SLLI
3'b101 : // SRI
begin
reg_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]} ;
end
default :
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
7'b011_0011 : // R型
begin
if( (funct7 == 7'b000_0000) || (funct7 == 7'b010_0000))
begin
case(funct3) // 这一部分是标准的指令集内容
3'b000 , // SUB
3'b001 , // SLL
3'b010 , // SLT
3'b011 , // SLTU
3'b100 , // XOR
3'b101 , // SR
3'b110 , // OR
3'b111 : // AND
begin
reg_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 ;
end
default :
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
else if( funct7 == 7'b000_0001) // M
begin
case(funct3)
3'b000 , // MUL
3'b001 , // MULH
3'b010 , // MULHSU
3'b011 : // MULHU
begin
reg_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 ;
end
3'b100 , // DIV
3'b101 , // DIVU
3'b110 , // REM
3'b111 : // REMU
begin
reg_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 ;
end
default:
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
else
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
end
7'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位无符号数值
begin
reg_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]} ;
end
default :
begin
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
reg_we_o = 0 ;
reg_waddr_o = 0 ;
end
endcase
end
7'b01_00011 : // S 类型
begin
case(funct3) // S type
3'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]} ;
end
default :
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
7'b1100011 : // B 型指令
begin
case(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};
end
default :
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
7'b110_1111 : // JAL J型指令 pc = pc + imm 并将下一条指令的地址 给rd
begin
reg_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};
end
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
7'b011_0111 : // LUT lui 将 imm[31:12] 左移 12 位后写入寄存器 rd
begin
reg_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 ;
end
7'b001_0111 : // 功能:PC + (imm[31:12] << 12) 写入寄存器 rd
begin
reg_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} ;
end
7'b0000_001 : // 单纯的占用流水线的时钟周期
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
7'b000_1111 : // 功能:在执行不同类型的内存操作时插入屏障,确保指令执行顺序
begin
reg_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) 的核心部分。
begin
reg_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 :
begin
reg1_raddr_o = rs1 ;
reg2_raddr_o = 0 ;
reg_we_o = 1 ;
reg_waddr_o = rd ;
csr_we_o = 1 ;
end
3'b101 ,
3'b110 ,
3'b111 :
begin
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
reg_we_o = 1 ;
reg_waddr_o = rd ;
csr_we_o = 1 ;
end
default:
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
csr_we_o = 0 ;
end
endcase
end
default:
begin
reg_we_o = 0 ;
reg_waddr_o = 0 ;
reg1_raddr_o = 0 ;
reg2_raddr_o = 0 ;
end
endcase
end
endmodule
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 ex
input 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_reg
output reg we_o ,
output reg [31 : 0] waddr_o ,
output reg [31 : 0] raddr_o ,
output reg [31 : 0] data_o ,
// to ex
output 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@(*)
begin
if(rst_n == 0)
begin
int_state <= S_INT_IDLE ;
end
else
begin
if(inst_i == 32'h0000_0073 || inst_i == 32'h0010_0073)
begin
if(div_started_i == 1)
begin
int_state <= S_INT_IDLE ;
end
else
begin
int_state <= S_INT_SYNC_ASSERT ;
end
end
else if( int_flag_i != 0 && global_int_en_i == 1)
begin
int_state <= S_INT_ASYNC_ASSERT ;
end
else if(inst_i == 32'h3020_0073)
begin
int_state <= S_INT_MRET ; // 这个表示需要从异常状态恢复到正常程序状态
end
else
begin
int_state <= S_INT_IDLE ;
end
end
end
// 写CSR寄存器状态切换
always@( posedge clk )
begin
if(rst_n == 0)
begin
csr_state <= S_CSR_IDLE ; // 状态机
cause <= 0 ;
inst_addr <= 0 ;
end
else
begin
case( csr_state )
S_CSR_IDLE :
begin
// 同步中断
if(int_state == S_INT_SYNC_ASSERT ) // 这部分的意思是如果系统捕获到了同步中断
begin
csr_state <= S_CSR_MEPC ;
// 在中断处理函数里会将中断返回地址 - 4
if (jump_flag_i == 1 )
begin
inst_addr <= jump_addr_i - 4'h4 ;
end
else
begin
inst_addr <= inst_addr_i ;
end
case (inst_i)
32'h0000_0073 : // ECALL
begin
cause <= 32'd11 ;
end
32'h010_0073 : // EBREAK
begin
cause <= 32'd3 ;
end
default:
begin
cause <= 32'd10 ;
end
endcase
end // 异步中断
else if (int_state == S_INT_ASYNC_ASSERT)
begin
// 定时器中断
cause <= 32'h80000004;
csr_state <= S_CSR_MEPC;
if (jump_flag_i == 1)
begin
inst_addr <= jump_addr_i;
// 异步中断可以中断除法指令的执行,中断处理完再重新执行除法指令
end
else if (div_started_i == 1)
begin
inst_addr <= inst_addr_i - 4'h4;
end
else
begin
inst_addr <= inst_addr_i;
end
// 中断返回
end
else if (int_state == S_INT_MRET)
begin
csr_state <= S_CSR_MSTATUS_MRET;
end
end
S_CSR_MEPC:
// 在这个状态下,系统会将当前的中断返回地址保存到 MEPC,以便中断处理完后可以正确返回。
begin
csr_state <= S_CSR_MSTATUS;
end
S_CSR_MSTATUS:
// 这个状态用于处理 MSTATUS 寄存器,通常涉及到恢复系统的中断使能和相关状态。
begin
csr_state <= S_CSR_MCAUSE;
end
S_CSR_MCAUSE:
// MCAUSE 寄存器用于存储中断的原因。在这个状态下,系统会把中断的原因写入 MCAUSE 寄存器。
begin
csr_state <= S_CSR_IDLE;
end
S_CSR_MSTATUS_MRET:
// 这是中断返回的特殊状态。执行完中断处理后,处理器需要返回到中断发生前的程序。
// 这是通过恢复 MSTATUS 寄存器和中断前的状态来实现的。
begin
csr_state <= S_CSR_IDLE;
end
default:
begin
csr_state <= S_CSR_IDLE;
end
endcase
end
end
// 发出信号前 先写几个寄存器
always@(posedge clk )
begin
if(rst_n == 0)
begin
we_o <= 0 ;
waddr_o <= 0 ;
data_o <= 0 ;
end
else
begin // 这一部分是按照上面的情况进行给予的
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:
begin
we_o <= 1 ;
waddr_o <= {20'h0, 12'h342 };
data_o <= cause ;
end
// 关闭全局中断
S_CSR_MSTATUS:
begin
we_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 :
begin
we_o <= 0 ;
waddr_o <= {20'h0, 12'h300};
data_o <= {csr_mstatus[31:4], csr_mstatus[7], csr_mstatus[2:0]};
end
default:
begin
we_o <= 0 ;
waddr_o <= 0 ;
data_o <= 0 ;
end
endcase
end
end
// 发送中断信号给ex模块
always@(posedge clk )
begin
if(rst_n == 0)
begin
int_assert_o <= 0 ;
int_addr_o <= 0 ;
end
else
begin
case (csr_state)
// 发出中断进入信号.写完mcause寄存器才能发
S_CSR_MCAUSE:
begin
int_assert_o <= 1 ;
int_addr_o <= csr_mtvec ;
end
// 发出中断返回信号
S_CSR_MSTATUS_MRET:
begin
int_assert_o <= 1 ;
int_addr_o <= csr_mepc ;
end
default:
begin
int_assert_o <= 0 ;
int_addr_o <= 0 ;
end
endcase
end
end
endmodule
csr_reg.v
module csr_reg( // 寄存器
input clk ,
input rst_n ,
// from ex
input we_i , // ex模块写寄存器标志位
input [31 : 0] raddr_i ,
input [31 : 0] waddr_i ,
input [31 : 0] data_i ,
// from clint
input 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 clint
output 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 ex
output 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 code
always@(posedge clk )
begin
if(rst_n == 0 )
begin
cycle <= 0 ;
end
else
begin
cycle <= cycle + 1 ;
end
end
// write reg 写寄存器
always@(posedge clk )
begin
if(rst_n == 0)
begin
mtvec <= 0 ;
mcause <= 0 ;
mepc <= 0 ;
mie <= 0 ;
mstatus <= 0 ;
mscratch <= 0 ;
end
else
begin
if(we_i == 1)
begin // 先响应ex写模块
case( waddr_i[11 : 0])
12'h305 :
begin
mtvec <= data_i ; // 处理器的中断向量地址寄存器。
end
12'h342:
begin
mcause <= data_i ; // 记录中断发生的原因。
end
12'h341:
begin
mepc <= data_i ; // 记录中断发生时的程序计数器(PC)值,即出错时程序的执行地址
end
12'h304:
begin
mie <= data_i ; // 机器模式下的中断使能寄存器
end
12'h300:
begin
mstatus <= data_i ; // 机器状态寄存器,包含有关当前处理器状态的信息
end
12'h340:
begin
mscratch <= data_i ; // 用于保存机器模式下的临时值
end
default:
begin
end
endcase
end
else if( clint_we_i == 1)
begin
case( clint_waddr_i[11:0])
12'h305 :
begin
mtvec <= clint_data_i ; // 处理器的中断向量地址寄存器。
end
12'h342:
begin
mcause <= clint_data_i ; // 记录中断发生的原因。
end
12'h341:
begin
mepc <= clint_data_i ; // 记录中断发生时的程序计数器(PC)值,即出错时程序的执行地址
end
12'h304:
begin
mie <= clint_data_i ; // 机器模式下的中断使能寄存器
end
12'h300:
begin
mstatus <= clint_data_i ; // 机器状态寄存器,包含有关当前处理器状态的信息
end
12'h340:
begin
mscratch <= clint_data_i ; // 用于保存机器模式下的临时值
end
default:
begin
end
endcase
end
end
end
// 读寄存器
always@(*)
begin
if((waddr_i[11:0] == raddr_i[11:0]) &&(we_i == 1) )
begin
data_o <= data_i ;
end
else
begin
case(raddr_i[11 : 0])
12'hc00 :
begin
data_o <= cycle[31:0] ;
end
12'hc80 :
begin
data_o <= cycle[63:32] ;
end
12'h305 :
begin
data_o <= mtvec ;
end
12'h342 :
begin
data_o <= mcause ;
end
12'h341 :
begin
data_o <= mepc ;
end
12'h304 :
begin
data_o <= mie ;
end
12'h300 :
begin
data_o <= mstatus ;
end
12'h340 :
begin
data_o <= mscratch ;
end
default:
begin
data_o <= 0 ;
end
endcase
end
end
always@(*)
begin
if((clint_waddr_i[11:0] == clint_raddr_i[11:0]) &&(clint_we_i == 1) )
begin
clint_data_o <= clint_data_i ;
end
else
begin
case(clint_raddr_i[11 : 0])
12'hc00 :
begin
clint_data_o <= cycle[31:0] ;
end
12'hc80 :
begin
clint_data_o <= cycle[63:32] ;
end
12'h305 :
begin
clint_data_o <= mtvec ;
end
12'h342 :
begin
clint_data_o <= mcause ;
end
12'h341 :
begin
clint_data_o <= mepc ;
end
12'h304 :
begin
clint_data_o <= mie ;
end
12'h300 :
begin
clint_data_o <= mstatus ;
end
12'h340 :
begin
clint_data_o <= mscratch ;
end
default:
begin
clint_data_o <= 0 ;
end
endcase
end
end
endmodule
ctrl.v
// 这是控制模块的设计
module ctrl(
// from ex 来自于ex 的处理
// input rst_n ,
// 经过分析 这里也没有用到 rst_n
input jump_flag_i ,
input [31 : 0] jump_addr_i , // 传递给PC 跳转的
input hold_flag_ex_i , // 内部的比如说触发需要暂停这样的
// from rib 总线
input hold_flag_rib_i ,
// from jtag
input jtag_halt_flag_i ,
// clint
input hold_flag_clint_i ,
// 输出 阶段
output reg [2 : 0] hold_flag_o ,
output reg jump_flag_o ,
// to pc
output reg [31 : 0] jump_addr_o
) ;
// 非常简单的组合逻辑阵列
always@(*)
begin
jump_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 ;
end
else if(hold_flag_rib_i == 1)
begin
hold_flag_o = 3'b001 ;
end
else if(jtag_halt_flag_i == 1)
begin
hold_flag_o = 3'b011 ;
end
else
begin
hold_flag_o = 0 ;
end
end
endmodule
div.v
// 这里作者说使用试商法 实现整除
module div(
input rst_n ,
input clk ,
// from ex
input [31 : 0] dividend_i , // 被除数
input [31 : 0] divisor_i , // 除数
input start_i , // 开始信号
input [2 : 0] op_i , // 声明具体执行的是什么指令
input [4 : 0] reg_waddr_i , // 寄存器地址
// to ex
output 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 )
begin
if(rst_n == 0 )
begin
state <= 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;
end
else
begin
case(state)
STATE_IDLE :
begin
if(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 ;
end
else
begin
op_r <= 0 ;
dividend_r <= 0 ;
divisor_r <= 0 ;
reg_waddr_o <= 0 ; // 寄存器地址 方便回写
state <= 0 ;
busy_o <= 0 ;
end
end
STATE_START :
begin
if(start_i == 1)
begin // 开始运算
//先假设除数为0
if(divisor_r == 0 )
begin
if(op_div | op_divu )
begin
// 取商的操作 那么输出 32'hFFFFFFFF 表示 错误商值,在 除数为 0 时由硬件返回。
result_o <= 32'hffff_ffff ;
end
else
begin // 那就是取余数
result_o <= dividend_r ; // 取余数的话 余下来的正好是被除数
end
// 上面两个都执行的在除数为0的情况下
ready_o <= 1 ;
state <= STATE_IDLE ;
busy_o <= 0 ;
end
else
begin
// 这部分是除数不为0
busy_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)
begin
dividend_r <= dividend_invert ;
minuend <= dividend_invert[31] ; // 初始化为被除数的符号位
end
else
begin
minuend <= dividend_r[31] ; // 其实就是0
end
if(divisor_r[31] == 1)
begin
divisor_r <= divisor_invert ;
end
else
begin
minuend <= dividend_r[31] ;
end
// 判断计算的结果是否需要取补码
if((op_div &&(dividend_r[31] ^ divisor_r[31] == 1))
|| (op_rem && (dividend_r[31] == 1)))
begin
invert_result <= 1 ;
end
else
begin
invert_result <= 0 ;
end
end
end
end
else
begin
state <= STATE_IDLE ;
result_o <= 0 ;
ready_o <= 0 ;
busy_o <= 0 ;
end
end
// 这里是整体的计算部分
STATE_CALC :
begin
if(start_i == 1)
begin
dividend_r <= {dividend_r[30 : 0] , 1'b0} ;
div_result <= div_result_tmp ;
count <= {1'b0 , count[31 : 1] } ;
if(|count)
begin
minuend <= {minuend_tmp[30 : 0] , dividend_r[30]} ;
end
else
begin
state <= STATE_END ;
if(minuend_ge_divisor)
begin
div_remain <= minuend_sub_res ;
end
else
begin
div_remain <= minuend ;
end
end
end
else
begin
state <= STATE_IDLE ;
result_o <= 0 ;
ready_o <= 0 ;
busy_o <= 0 ;
end
end
STATE_END :
begin
if(start_i == 1)
begin
ready_o <= 1 ;
state <= STATE_IDLE ;
busy_o <= 0 ;
if(op_div | op_divu )
begin
if(invert_result == 1)
begin
result_o <= (-div_result) ;
end
else
begin
result_o <= div_result ;
end
end
else
begin
if(invert_result == 1)
begin
result_o <= (-div_remain ) ;
end
else
begin
result_o <= div_remain ;
end
end
end
else
begin
state <= STATE_IDLE ;
result_o <= 0 ;
ready_o <= 0 ;
busy_o <= 0 ;
end
end
endcase
end
end
endmodule
ex.v
module ex(
input rst_n ,
// from id
input [31:0] inst_i, // 指令内容 31
input [31:0] inst_addr_i, // 指令地址 31
input reg_we_i, // 是否写通用寄存器
input [4:0] reg_waddr_i, // 写通用寄存器地址 4:0
input [31:0] reg1_rdata_i, // 通用寄存器1输入数据 31
input [31:0] reg2_rdata_i, // 通用寄存器2输入数据31
input csr_we_i, // 是否写CSR寄存器
input [31:0] csr_waddr_i, // 写CSR寄存器地址 31
input [31:0] csr_rdata_i, // CSR寄存器输入数据31
input int_assert_i, // 中断发生标志 -- 来自于 clint
input [31:0] int_addr_i, // 中断跳转地址31 -- 来自于 clint
input [31:0] op1_i, // 31
input [31:0] op2_i,
input [31:0] op1_jump_i,
input [31:0] op2_jump_i,
// from mem
input wire[31:0] mem_rdata_i, // 内存输入数据
// from div
input 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 mem
output reg[31:0] mem_wdata_o, // 写内存数据 31
output reg[31:0] mem_raddr_o, // 读内存地址
output reg[31:0] mem_waddr_o, // 写内存地址 31
output wire mem_we_o, // 是否要写内存
output wire mem_req_o, // 请求访问内存标志
// to regs
output wire[31:0] reg_wdata_o, // 写寄存器数据 31:0
output wire reg_we_o, // 是否要写通用寄存器
output wire[4:0] reg_waddr_o, // 写通用寄存器地址 4:0
// to csr reg
output reg[31:0] csr_wdata_o, // 写CSR寄存器数据
output wire csr_we_o, // 是否要写CSR寄存器
output wire[31:0] csr_waddr_o, // 写CSR寄存器地址 31:0
// to div
output 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 ctrl
output 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:0
wire[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; //1
reg[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; // 1
reg reg_we; // 1
reg[4:0] reg_waddr; // 4: 0
reg[31:0] div_wdata; //1
reg div_we; //1
reg[4:0] div_waddr; // 4:0
reg div_hold_flag; // 1
reg div_jump_flag; // 1
reg[31:0] div_jump_addr; //31 : 0
reg hold_flag; //
reg jump_flag; //
reg[31:0] jump_addr; // 1
reg mem_we; //
reg mem_req; // 1
reg div_start; // 1
assign 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 @ (*)
begin
if ((opcode == 7'b0110011) && (funct7 == 7'b0000001))
begin
case (funct3)
3'b000, 3'b011:
begin // 3'b000 - 3'b011 无符号乘法 一个保留了低32位 一个保留了高32位
mul_op1 = reg1_rdata_i;
mul_op2 = reg2_rdata_i;
end
3'b010:
begin // 3'b010 有符号与 无符号混合乘法
mul_op1 = (reg1_rdata_i[31] == 1'b1)? (reg1_data_invert): reg1_rdata_i;
mul_op2 = reg2_rdata_i;
end
3'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;
end
default:
begin
mul_op1 = reg1_rdata_i;
mul_op2 = reg2_rdata_i;
end
endcase
end
else
begin
mul_op1 = reg1_rdata_i;
mul_op2 = reg2_rdata_i;
end
end
// 处理除法指令
always @ (*)
begin
div_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'b0110011
div_we = 0;
div_wdata = 32'h0;
div_waddr = 32'h0;
case (funct3)
3'b100,3'b101, 3'b110, 3'b101:
begin // 100 101 110 111
div_start = 1;
div_jump_flag = 1;
div_hold_flag = 1;
div_jump_addr = op1_jump_add_op2_jump_res;
end
default:
begin
div_start = 0;
div_jump_flag = 0;
div_hold_flag = 0;
div_jump_addr = 32'h0;
end
endcase
end
else
begin
div_jump_flag = 0;
div_jump_addr = 32'h0;
if (div_busy_i == 1)
begin
div_start = 1;
div_we = 0;
div_wdata = 32'h0;
div_waddr = 32'h0;
div_hold_flag = 1;
end
else
begin
div_start = 0;
div_hold_flag = 0;
if (div_ready_i == 1)
begin
div_wdata = div_result_i;
div_waddr = div_reg_waddr_i;
div_we = 1;
end
else
begin
div_we = 0;
div_wdata = 32'h0;
div_waddr = 32'h0;
end
end
end
end
// `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 @ (*)
begin
reg_we = reg_we_i;
reg_waddr = reg_waddr_i;
mem_req = 0; // 0
csr_wdata_o = 32'h0;
case (opcode)
7'b0010011:
begin // 7'b0010011
case (funct3)
3'b000:
begin
jump_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;
end
3'b010:
begin
jump_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;
end
3'b011:
begin
jump_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;
end
3'b100:
begin
jump_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;
end
3'b110:
begin
jump_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;
end
3'b111:
begin
jump_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;
end
3'b001:
begin
jump_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];
end
3'b101:
begin
jump_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)
begin
reg_wdata = (sri_shift & sri_shift_mask) | ({32{reg1_rdata_i[31]}} & (~sri_shift_mask));
end
else
begin
reg_wdata = reg1_rdata_i >> inst_i[24:20];
end
end
default:
begin
jump_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;
end
endcase
end
// // 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'b111
7'b0110011:
begin // 7'b0110011
if ((funct7 == 7'b0000000) || (funct7 == 7'b0100000))
begin
case (funct3)
3'b000:
begin
jump_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)
begin
reg_wdata = op1_add_op2_res;
end
else
begin
reg_wdata = op1_i - op2_i;
end
end
3'b001:
begin
jump_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];
end
3'b010:
begin
jump_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;
end
3'b011:
begin
jump_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;
end
3'b100:
begin
jump_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;
end
3'b101:
begin
jump_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)
begin
reg_wdata = (sr_shift & sr_shift_mask) | ({32{reg1_rdata_i[31]}} & (~sr_shift_mask));
end
else
begin
reg_wdata = reg1_rdata_i >> reg2_rdata_i[4:0];
end
end
3'b110:
begin // 110
jump_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;
end
3'b111:
begin //111
jump_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;
end
default:
begin
jump_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;
end
endcase
end
else 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'b111
case (funct3)
3'b000:
begin
jump_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];
end
3'b011:
begin
jump_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];
end
3'b001:
begin
jump_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:
begin
reg_wdata = mul_temp[63:32];
end
2'b11:
begin
reg_wdata = mul_temp[63:32];
end
2'b10:
begin
reg_wdata = mul_temp_invert[63:32];
end
default:
begin
reg_wdata = mul_temp_invert[63:32];
end
endcase
end
3'b010:
begin
jump_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)
begin
reg_wdata = mul_temp_invert[63:32];
end
else
begin
reg_wdata = mul_temp[63:32];
end
end
default:
begin
jump_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;
end
endcase
end
else
begin
jump_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;
end
end
7'b0000011:
begin
case (funct3)
3'b000:
begin
jump_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; // 1
mem_raddr_o = op1_add_op2_res;
case (mem_raddr_index)
2'b00:
begin
reg_wdata = {{24{mem_rdata_i[7]}}, mem_rdata_i[7:0]};
end
2'b01:
begin
reg_wdata = {{24{mem_rdata_i[15]}}, mem_rdata_i[15:8]};
end
2'b10:
begin
reg_wdata = {{24{mem_rdata_i[23]}}, mem_rdata_i[23:16]};
end
default:
begin
reg_wdata = {{24{mem_rdata_i[31]}}, mem_rdata_i[31:24]};
end
endcase
end
3'b001:
begin
jump_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)
begin
reg_wdata = {{16{mem_rdata_i[15]}}, mem_rdata_i[15:0]};
end
else
begin
reg_wdata = {{16{mem_rdata_i[31]}}, mem_rdata_i[31:16]};
end
end
3'b010:
begin
jump_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;
end
3'b100:
begin
jump_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:
begin
reg_wdata = {24'h0, mem_rdata_i[7:0]};
end
2'b01:
begin
reg_wdata = {24'h0, mem_rdata_i[15:8]};
end
2'b10:
begin
reg_wdata = {24'h0, mem_rdata_i[23:16]};
end
default:
begin
reg_wdata = {24'h0, mem_rdata_i[31:24]};
end
endcase
end
3'b101:
begin
jump_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)
begin
reg_wdata = {16'h0, mem_rdata_i[15:0]};
end
else
begin
reg_wdata = {16'h0, mem_rdata_i[31:16]};
end
end
default:
begin
jump_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;
end
endcase
end
7'b0100011:
begin // 7'b0100011
case (funct3)
3'b000:
begin
jump_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:
begin
mem_wdata_o = {mem_rdata_i[31:8], reg2_rdata_i[7:0]};
end
2'b01:
begin
mem_wdata_o = {mem_rdata_i[31:16], reg2_rdata_i[7:0], mem_rdata_i[7:0]};
end
2'b10:
begin
mem_wdata_o = {mem_rdata_i[31:24], reg2_rdata_i[7:0], mem_rdata_i[15:0]};
end
default:
begin
mem_wdata_o = {reg2_rdata_i[7:0], mem_rdata_i[23:0]};
end
endcase
end
3'b001:
begin
jump_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)
begin
mem_wdata_o = {mem_rdata_i[31:16], reg2_rdata_i[15:0]};
end
else
begin
mem_wdata_o = {reg2_rdata_i[15:0], mem_rdata_i[15:0]};
end
end
3'b010:
begin
jump_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;
end
default:
begin
jump_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;
end
endcase
end
// `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'b111
7'b1100011:
begin // 7'b1100011
case (funct3)
3'b000:
begin
hold_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;
end
3'b001:
begin
hold_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;
end
3'b100:
begin
hold_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;
end
3'b101:
begin
hold_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;
end
3'b110:
begin
hold_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;
end
3'b111:
begin
hold_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;
end
default:
begin
jump_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;
end
endcase
end
// `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'h00100073
7'b1101111, 7'b1100111:
begin
hold_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;
end
7'b0110111, 7'b0010111:
begin
hold_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;
end
7'b0000001:
begin
jump_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;
end
7'b0001111:
begin
hold_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'b111
7'b1110011:
begin
jump_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:
begin
csr_wdata_o = reg1_rdata_i;
reg_wdata = csr_rdata_i;
end
3'b010:
begin
csr_wdata_o = reg1_rdata_i | csr_rdata_i;
reg_wdata = csr_rdata_i;
end
3'b011:
begin
csr_wdata_o = csr_rdata_i & (~reg1_rdata_i);
reg_wdata = csr_rdata_i;
end
3'b101:
begin
csr_wdata_o = {27'h0, uimm};
reg_wdata = csr_rdata_i;
end
3'b110:
begin
csr_wdata_o = {27'h0, uimm} | csr_rdata_i;
reg_wdata = csr_rdata_i;
end
3'b111:
begin
csr_wdata_o = (~{27'h0, uimm}) & csr_rdata_i;
reg_wdata = csr_rdata_i;
end
default:
begin
jump_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;
end
endcase
end
default:
begin
jump_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;
end
endcase
end
endmodule
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 )
begin
if(rst_n == 0 | hold_en == 1)
begin
qout_r <= def_val ;
end
else
begin
qout_r <= din ;
end
end
assign 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 在后续处理完成之后 写回reg
input 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 id1
input [4 : 0] raddr1_i ,
output reg [31 : 0] rdata1_o ,
// from id2
input [4 : 0] raddr2_i ,
output reg [31 : 0] rdata2_o
);
reg [31 : 0] regs[0 : 31] ;
// 写寄存器的发射信号
always@(posedge clk )
begin
if(rst_n == 1)
begin
// 先进行ex
if((we_i == 1) && (waddr_i != 0))
begin
regs[waddr_i] <= wdata_i ;
end
else if((jtag_we_i == 1) && ( jtag_addr_i != 0))
begin
regs[jtag_addr_i] <= jtag_data_i ;
end
end
end
// 读寄存器1
always @ (*)
begin
if (raddr1_i == 0)
begin
rdata1_o <= 0;
// 如果读地址等于写地址,并且正在写操作,则直接返回写数据
end
else if (raddr1_i == waddr_i && we_i == 1)
begin
rdata1_o <= wdata_i;
end
else
begin
rdata1_o <= regs[raddr1_i];
end
end
// 读寄存器2
always @ (*)
begin
if (raddr2_i == 0)
begin
rdata2_o <= 0;
// 如果读地址等于写地址,并且正在写操作,则直接返回写数据
end
else if (raddr2_i == waddr_i && we_i == 1)
begin
rdata2_o <= wdata_i;
end
else
begin
rdata2_o <= regs[raddr2_i];
end
end
// jtag 读寄存器
always@(*)
begin
if(jtag_addr_i == 0)
begin
jtag_data_o <= 0 ;
end
else
begin
jtag_data_o <= regs[jtag_addr_i] ;
end
end
endmodule
rib.v
//这一部分是RIB总线模块
module rib(
input clk ,
input rst_n ,
// 主四 从六
// master 0
input [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 1
input [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 2
input [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 3
input [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 0
output 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 1
output 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 2
output 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 3
output 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 4
output 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 5
output 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 - 1
always@(*)
begin
if(req[3] == 1)
begin
grant = grant3 ; // 第三个
hold_flag_o = 1 ;
end
else if(req[0] == 1)
begin
grant = grant0 ; // 第三个
hold_flag_o = 1 ;
end
else if(req[2] == 1)
begin
grant = grant2 ; // 第三个
hold_flag_o = 1 ;
end
else
begin
grant = grant1 ; // 第三个
hold_flag_o = 0 ;
end
end
// 根据仲裁结果选择 访问从机
always@(*)
begin
m0_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 :
begin
case(m0_addr_i[31:28])
slave_0 :
begin
s0_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 ;
end
slave_1 :
begin
s1_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 ;
end
slave_2 :
begin
s2_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 ;
end
slave_3 :
begin
s3_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 ;
end
slave_4 :
begin
s4_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 ;
end
slave_5 :
begin
s5_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 ;
end
default :
begin
end
endcase
end
grant1 :
begin
case(m1_addr_i[31:28])
slave_0 :
begin
s0_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 ;
end
slave_1 :
begin
s1_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 ;
end
slave_2 :
begin
s2_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 ;
end
slave_3 :
begin
s3_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 ;
end
slave_4 :
begin
s4_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 ;
end
slave_5 :
begin
s5_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 ;
end
default :
begin
end
endcase
end
grant2 :
begin
case(m2_addr_i[31:28])
slave_0 :
begin
s0_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 ;
end
slave_1 :
begin
s1_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 ;
end
slave_2 :
begin
s2_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 ;
end
slave_3 :
begin
s3_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 ;
end
slave_4 :
begin
s4_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 ;
end
slave_5 :
begin
s5_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 ;
end
default :
begin
end
endcase
end
grant3 :
begin
case(m3_addr_i[31:28])
slave_0 :
begin
s0_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 ;
end
slave_1 :
begin
s1_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 ;
end
slave_2 :
begin
s2_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 ;
end
slave_3 :
begin
s3_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 ;
end
slave_4 :
begin
s4_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 ;
end
slave_5 :
begin
s5_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 ;
end
default :
begin
end
endcase
end
default :
begin
end
endcase
end
endmodule
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)
begin
rdata2_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部分核心的代码是完全无误的