Created
June 10, 2021 09:07
-
-
Save olofk/6738d7bac9a1ffa59092431382f87033 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| `default_nettype none | |
| module serv_top | |
| #(parameter WITH_CSR = 1, | |
| parameter PRE_REGISTER = 1, | |
| parameter RESET_STRATEGY = "MINI", | |
| parameter RESET_PC = 32'd0) | |
| ( | |
| input wire clk, | |
| input wire i_rst, | |
| //RF Interface | |
| output wire o_rf_rreq, | |
| output wire o_rf_wreq, | |
| input wire i_rf_ready, | |
| output wire [4+WITH_CSR:0] o_wreg0, | |
| output wire [4+WITH_CSR:0] o_wreg1, | |
| output wire o_wen0, | |
| output wire o_wen1, | |
| output wire o_wdata0, | |
| output wire o_wdata1, | |
| output wire [4+WITH_CSR:0] rs1_addr, | |
| output wire [4+WITH_CSR:0] rs2_addr, | |
| input wire rs1, | |
| input wire rs2, | |
| output reg [31:0] o_ibus_adr, | |
| output wire o_ibus_cyc, | |
| input wire [31:0] i_ibus_rdt, | |
| input wire i_ibus_ack, | |
| output wire [31:0] o_dbus_adr, | |
| output reg [31:0] o_dbus_dat, | |
| output wire [3:0] o_dbus_sel, | |
| output wire o_dbus_we , | |
| output wire o_dbus_cyc, | |
| input wire [31:0] i_dbus_rdt, | |
| input wire i_dbus_ack); | |
| wire [4:0] rd_addr; | |
| wire [3:0] immdec_ctrl; | |
| wire [3:0] immdec_en; | |
| wire sh_right; | |
| wire cond_branch; | |
| wire ebreak; | |
| wire branch_op; | |
| wire mem_op; | |
| wire shift_op; | |
| wire slt_op; | |
| wire rd_op; | |
| wire rd_alu_en; | |
| wire ctrl_rd; | |
| wire alu_rd; | |
| wire mem_rd; | |
| wire ctrl_pc_en; | |
| reg jump; | |
| wire imm; | |
| wire pc_rel; | |
| wire init; | |
| wire cnt_en; | |
| wire cnt0to3; | |
| wire cnt12to31; | |
| wire cnt0; | |
| wire cnt1; | |
| wire cnt2; | |
| wire cnt3; | |
| wire cnt7; | |
| reg cnt_done; | |
| wire bufreg_en; | |
| wire bufreg_sh_signed; | |
| wire bufreg_rs1_en; | |
| wire bufreg_imm_en; | |
| wire bufreg_clr_lsb; | |
| wire bufreg_q; | |
| wire alu_sub; | |
| wire [1:0] alu_bool_op; | |
| wire alu_cmp_eq; | |
| wire alu_cmp_sig; | |
| wire alu_cmp; | |
| wire [2:0] alu_rd_sel; | |
| wire rd_en; | |
| wire op_b_source; | |
| wire mem_signed; | |
| wire mem_word; | |
| wire mem_half; | |
| wire [1:0] mem_bytecnt; | |
| wire mem_sh_done; | |
| wire mem_sh_done_r; | |
| wire mem_misalign; | |
| reg [1:0] lsb; | |
| wire op_b = op_b_source ? rs2 : imm; | |
| reg stage_two_req; | |
| reg init_done; | |
| reg [4:2] cnt; | |
| reg [3:0] cnt_r; | |
| reg ibus_cyc; | |
| assign ctrl_pc_en = cnt_en & !init; | |
| assign cnt_en = |cnt_r; | |
| assign mem_bytecnt = cnt[4:3]; | |
| assign cnt0to3 = (cnt[4:2] == 3'd0); | |
| assign cnt12to31 = (cnt[4] | (cnt[3:2] == 2'b11)); | |
| assign cnt0 = (cnt[4:2] == 3'd0) & cnt_r[0]; | |
| assign cnt1 = (cnt[4:2] == 3'd0) & cnt_r[1]; | |
| assign cnt2 = (cnt[4:2] == 3'd0) & cnt_r[2]; | |
| assign cnt3 = (cnt[4:2] == 3'd0) & cnt_r[3]; | |
| assign cnt7 = (cnt[4:2] == 3'd1) & cnt_r[3]; | |
| wire take_branch = branch_op & (!cond_branch | (alu_cmp^bne_or_bge)); | |
| //slt*, branch/jump, shift, load/store | |
| wire two_stage_op = slt_op | mem_op | branch_op | shift_op; | |
| assign o_dbus_cyc = !cnt_en & init_done & mem_op; | |
| //Prepare RF for reads when a new instruction is fetched | |
| // or when stage one caused an exception (rreq implies a write request too) | |
| assign o_rf_rreq = i_ibus_ack | (stage_two_req); | |
| //Prepare RF for writes when everything is ready to enter stage two | |
| // and the first stage didn't cause a misalign exception | |
| assign o_rf_wreq = | |
| ((shift_op & (mem_sh_done | !sh_right) & !cnt_en & init_done) | | |
| (mem_op & i_dbus_ack) | | |
| (stage_two_req & (slt_op | branch_op))); | |
| assign rd_en = rd_op & !init; | |
| assign bufreg_en = (cnt_en & (init | branch_op)) | (shift_op & !stage_two_req & (sh_right | mem_sh_done_r)); | |
| assign o_ibus_cyc = ibus_cyc & !i_rst; | |
| assign init = two_stage_op & !init_done; | |
| always @(posedge clk) begin | |
| if (i_ibus_ack | cnt_done | i_rst) | |
| ibus_cyc <= ctrl_pc_en | i_rst; | |
| if (cnt_done) begin | |
| init_done <= init & !init_done; | |
| jump <= init & take_branch; | |
| end | |
| cnt_done <= (cnt[4:2] == 3'b111) & cnt_r[2]; | |
| //Need a strobe for the first cycle in the IDLE state after INIT | |
| stage_two_req <= cnt_done & init; | |
| cnt <= cnt + {2'd0,cnt_r[3]}; | |
| cnt_r <= {cnt_r[2:0],(cnt_r[3] & !cnt_done) | (i_rf_ready & !cnt_en)}; | |
| if (i_rst) begin | |
| cnt <= 3'd0; | |
| init_done <= 1'b0; | |
| jump <= 1'b0; | |
| cnt_r <= 4'b0000; | |
| end | |
| end | |
| reg [4:0] opcode; | |
| reg [2:0] funct3; | |
| reg op20; | |
| reg op21; | |
| reg op22; | |
| reg op26; | |
| reg imm30; | |
| //opcode | |
| wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); | |
| wire mem_op = !opcode[4] & !opcode[2] & !opcode[0]; | |
| wire branch_op = opcode[4] & !opcode[2]; | |
| //jal,branch = imm | |
| //jalr = rs1+imm | |
| //mem = rs1+imm | |
| //shift = rs1 | |
| wire bufreg_rs1_en = !opcode[4] | (!opcode[1] & opcode[0]); | |
| wire bufreg_imm_en = !opcode[2]; | |
| wire bufreg_clr_lsb = opcode[4] & ((opcode[1:0] == 2'b00) | (opcode[1:0] == 2'b11)); | |
| //Conditional branch | |
| //True for BRANCH | |
| //False for JAL/JALR | |
| wire cond_branch = !opcode[0]; | |
| wire utype = !opcode[4] & opcode[2] & opcode[0]; | |
| wire jal_or_jalr = opcode[4] & opcode[0]; | |
| //PC-relative operations | |
| //True for jal, b* auipc | |
| //False for jalr, lui | |
| wire pc_rel = (opcode[2:0] == 3'b000) | | |
| (opcode[1:0] == 2'b11) | | |
| (opcode[4:3] == 2'b00); | |
| //Write to RD | |
| //True for OP-IMM, AUIPC, OP, LUI, SYSTEM, JALR, JAL, LOAD | |
| //False for STORE, BRANCH, MISC-MEM | |
| wire rd_op = (opcode[2] | | |
| (!opcode[2] & opcode[4] & opcode[0]) | | |
| (!opcode[2] & !opcode[3] & !opcode[0])); | |
| wire sh_right = funct3[2]; | |
| wire bne_or_bge = funct3[0]; | |
| wire shift_op = op_or_opimm & (funct3[1:0] == 2'b01); | |
| wire slt_op = op_or_opimm & (funct3[2:1] == 2'b01); | |
| wire ebreak = op20; | |
| wire bufreg_sh_signed = imm30; | |
| wire alu_sub = funct3[1] | funct3[0] | (opcode[3] & imm30) | opcode[4]; | |
| wire alu_cmp_eq = funct3[2:1] == 2'b00; | |
| wire alu_cmp_sig = ~((funct3[0] & funct3[1]) | (funct3[1] & funct3[2])); | |
| assign o_dbus_we = opcode[3]; | |
| wire mem_signed = ~funct3[2]; | |
| wire mem_word = funct3[1]; | |
| wire mem_half = funct3[0]; | |
| wire [1:0] alu_bool_op = funct3[1:0]; | |
| //True for S (STORE) or B (BRANCH) type instructions | |
| //False for J type instructions | |
| assign immdec_ctrl[0] = opcode[3:0] == 4'b1000; | |
| //True for OP-IMM, LOAD, STORE, JALR (I S) | |
| //False for LUI, AUIPC, JAL (U J) | |
| assign immdec_ctrl[1] = (opcode[1:0] == 2'b00) | (opcode[2:1] == 2'b00); | |
| assign immdec_ctrl[2] = opcode[4] & !opcode[0]; | |
| assign immdec_ctrl[3] = opcode[4]; | |
| assign immdec_en[3] = opcode[4] | opcode[3] | opcode[2] | !opcode[0]; //B I J S U | |
| assign immdec_en[2] = (opcode[4] & opcode[2]) | !opcode[3] | opcode[0]; // I J U | |
| assign immdec_en[1] = (opcode[2:1] == 2'b01) | (opcode[2] & opcode[0]);// J U | |
| assign immdec_en[0] = ~rd_op; //B S | |
| wire [2:0] alu_rd_sel; | |
| assign alu_rd_sel[0] = (funct3 == 3'b000); // Add/sub | |
| assign alu_rd_sel[1] = (funct3[2:1] == 2'b01); //SLT* | |
| assign alu_rd_sel[2] = funct3[2]; //Bool | |
| //0 (OP_B_SOURCE_IMM) when OPIMM | |
| //1 (OP_B_SOURCE_RS2) when BRANCH or OP | |
| wire op_b_source = opcode[3]; | |
| wire rd_alu_en = !opcode[0] & opcode[2] & !opcode[4]; | |
| always @(posedge clk) begin | |
| if (i_ibus_ack) begin | |
| funct3 <= i_ibus_rdt[14:12]; | |
| imm30 <= i_ibus_rdt[30]; | |
| opcode <= i_ibus_rdt[6:2]; | |
| op20 <= i_ibus_rdt[20]; | |
| op21 <= i_ibus_rdt[21]; | |
| op22 <= i_ibus_rdt[22]; | |
| op26 <= i_ibus_rdt[26]; | |
| end | |
| end | |
| /* immdec */ | |
| reg signbit; | |
| reg [8:0] imm19_12_20; | |
| reg imm7; | |
| reg [5:0] imm30_25; | |
| reg [4:0] imm24_20; | |
| reg [4:0] imm11_7; | |
| assign imm = cnt_done ? signbit : immdec_ctrl[0] ? imm11_7[0] : imm24_20[0]; | |
| assign rs1_addr = imm19_12_20[8:4]; | |
| assign rs2_addr = imm24_20; | |
| assign rd_addr = imm11_7; | |
| always @(posedge clk) begin | |
| if (i_ibus_ack) begin | |
| signbit <= i_ibus_rdt[31]; | |
| end | |
| if (i_ibus_ack | (cnt_en & immdec_en[1])) | |
| imm19_12_20 <= i_ibus_ack ? {i_ibus_rdt[19:12],i_ibus_rdt[20]} : {immdec_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]}; | |
| if (i_ibus_ack | (cnt_en)) | |
| imm7 <= i_ibus_ack ? i_ibus_rdt[7] : signbit; | |
| if (i_ibus_ack | (cnt_en & immdec_en[3])) | |
| imm30_25 <= i_ibus_ack ? i_ibus_rdt[30:25] : {immdec_ctrl[2] ? imm7 : immdec_ctrl[1] ? signbit : imm19_12_20[0], imm30_25[5:1]}; | |
| if (i_ibus_ack | (cnt_en & immdec_en[2])) | |
| imm24_20 <= i_ibus_ack ? i_ibus_rdt[24:20] : {imm30_25[0], imm24_20[4:1]}; | |
| if (i_ibus_ack | (cnt_en & immdec_en[0])) | |
| imm11_7 <= i_ibus_ack ? i_ibus_rdt[11:7] : {imm30_25[0], imm11_7[4:1]}; | |
| end | |
| /* Bufreg */ | |
| wire c, q; | |
| reg c_r; | |
| reg [31:2] data; | |
| wire clr_lsb = cnt0 & bufreg_clr_lsb; | |
| assign {c,q} = {1'b0,(rs1 & bufreg_rs1_en)} + {1'b0,(imm & bufreg_imm_en & !clr_lsb)} + c_r; | |
| always @(posedge clk) begin | |
| //Make sure carry is cleared before loading new data | |
| c_r <= c & bufreg_en; | |
| if (bufreg_en) | |
| data <= {init ? q : (data[31] & bufreg_sh_signed), data[31:3]}; | |
| if (init ? (cnt0 | cnt1) : bufreg_en) | |
| lsb <= {init ? q : data[2],lsb[1]}; | |
| end | |
| assign bufreg_q = lsb[0] & bufreg_en; | |
| assign o_dbus_adr = {data, 2'b00}; | |
| /* CTRL */ | |
| wire pc_plus_4; | |
| wire pc_plus_4_cy; | |
| reg pc_plus_4_cy_r; | |
| wire pc_plus_offset; | |
| wire pc_plus_offset_cy; | |
| reg pc_plus_offset_cy_r; | |
| wire pc_plus_offset_aligned; | |
| wire plus_4; | |
| wire pc = o_ibus_adr[0]; | |
| wire new_pc; | |
| wire offset_a; | |
| wire offset_b; | |
| assign plus_4 = cnt2; | |
| assign {pc_plus_4_cy,pc_plus_4} = pc+plus_4+pc_plus_4_cy_r; | |
| assign new_pc = jump ? pc_plus_offset_aligned : pc_plus_4; | |
| assign ctrl_rd = (utype & pc_plus_offset_aligned) | (pc_plus_4 & jal_or_jalr); | |
| assign offset_a = pc_rel & pc; | |
| assign offset_b = utype ? (imm & cnt12to31): bufreg_q; | |
| assign {pc_plus_offset_cy,pc_plus_offset} = offset_a+offset_b+pc_plus_offset_cy_r; | |
| assign pc_plus_offset_aligned = pc_plus_offset & !cnt0; | |
| always @(posedge clk) begin | |
| pc_plus_4_cy_r <= ctrl_pc_en & pc_plus_4_cy; | |
| pc_plus_offset_cy_r <= ctrl_pc_en & pc_plus_offset_cy; | |
| if (ctrl_pc_en | i_rst) | |
| o_ibus_adr <= i_rst ? RESET_PC : {new_pc, o_ibus_adr[31:1]}; | |
| end | |
| /* ALU */ | |
| wire result_add; | |
| reg cmp_r; | |
| wire add_cy; | |
| reg add_cy_r; | |
| //Sign-extended operands | |
| wire rs1_sx = rs1 & alu_cmp_sig; | |
| wire op_b_sx = op_b & alu_cmp_sig; | |
| wire add_b = op_b^alu_sub; | |
| assign {add_cy,result_add} = rs1+add_b+add_cy_r; | |
| wire result_lt = rs1_sx + ~op_b_sx + add_cy; | |
| wire result_eq = !result_add & (cmp_r | cnt0); | |
| assign alu_cmp = alu_cmp_eq ? result_eq : result_lt; | |
| wire result_bool = ((rs1 ^ op_b) & ~ alu_bool_op[0]) | (alu_bool_op[1] & op_b & rs1); | |
| assign alu_rd = bufreg_q | | |
| (alu_rd_sel[0] & result_add) | | |
| (alu_rd_sel[1] & cmp_r & cnt0) | | |
| (alu_rd_sel[2] & result_bool); | |
| always @(posedge clk) begin | |
| add_cy_r <= cnt_en ? add_cy : alu_sub; | |
| if (cnt_en) | |
| cmp_r <= alu_cmp; | |
| end | |
| /* RF IF */ | |
| wire rd_wen = rd_en & (|rd_addr); | |
| wire rd = (ctrl_rd ) | | |
| (alu_rd & rd_alu_en) | | |
| (mem_rd); | |
| assign o_wdata0 = rd; | |
| assign o_wdata1 = 1'b0; | |
| assign o_wreg0 = rd_addr; | |
| assign o_wreg1 = 5'd0; | |
| assign o_wen0 = cnt_en & rd_wen; | |
| assign o_wen1 = 1'b0; | |
| /************************* | |
| Mem IF | |
| *************************/ | |
| reg mem_signbit; | |
| wire byte_valid = | |
| (!lsb[0] & !lsb[1]) | | |
| (!mem_bytecnt[0] & !mem_bytecnt[1]) | | |
| (!mem_bytecnt[1] & !lsb[1]) | | |
| (!mem_bytecnt[1] & !lsb[0]) | | |
| (!mem_bytecnt[0] & !lsb[1]); | |
| wire dat_en = shift_op | (cnt_en & byte_valid); | |
| wire dat_cur = | |
| ((lsb == 2'd3) & o_dbus_dat[24]) | | |
| ((lsb == 2'd2) & o_dbus_dat[16]) | | |
| ((lsb == 2'd1) & o_dbus_dat[8]) | | |
| ((lsb == 2'd0) & o_dbus_dat[0]); | |
| wire dat_valid = | |
| mem_word | | |
| (mem_bytecnt == 2'b00) | | |
| (mem_half & !mem_bytecnt[1]); | |
| assign mem_rd = mem_op & (dat_valid ? dat_cur : mem_signbit & mem_signed); | |
| assign o_dbus_sel[3] = (lsb == 2'b11) | mem_word | (mem_half & lsb[1]); | |
| assign o_dbus_sel[2] = (lsb == 2'b10) | mem_word; | |
| assign o_dbus_sel[1] = (lsb == 2'b01) | mem_word | (mem_half & !lsb[1]); | |
| assign o_dbus_sel[0] = (lsb == 2'b00); | |
| wire [5:0] dat_shamt = (shift_op & !init) ? | |
| //Down counter mode | |
| o_dbus_dat[5:0]-1 : | |
| //Shift reg mode with optional clearing of bit 5 | |
| {o_dbus_dat[6] & !(shift_op & cnt_done),o_dbus_dat[5:1]}; | |
| assign mem_sh_done = dat_shamt[5]; | |
| assign mem_sh_done_r = o_dbus_dat[5]; | |
| always @(posedge clk) begin | |
| if (dat_en | i_dbus_ack) | |
| o_dbus_dat <= i_dbus_ack ? i_dbus_rdt : {op_b, o_dbus_dat[31:7], dat_shamt}; | |
| if (dat_valid) | |
| mem_signbit <= dat_cur; | |
| end | |
| endmodule | |
| `default_nettype wire |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment