Skip to content

Instantly share code, notes, and snippets.

@olofk
Created June 10, 2021 09:07
Show Gist options
  • Select an option

  • Save olofk/6738d7bac9a1ffa59092431382f87033 to your computer and use it in GitHub Desktop.

Select an option

Save olofk/6738d7bac9a1ffa59092431382f87033 to your computer and use it in GitHub Desktop.
`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