1
0
mirror of https://github.com/olofk/serv.git synced 2026-01-13 07:09:33 +00:00
olofk.serv/rtl/serv_state.v
2021-01-18 22:38:07 +01:00

205 lines
7.3 KiB
Verilog

module serv_state
#(parameter RESET_STRATEGY = "MINI",
parameter [0:0] WITH_CSR = 1)
(
input wire i_clk,
input wire i_rst,
input wire i_new_irq,
output reg o_pending_irq,
input wire i_dbus_ack,
output wire o_ibus_cyc,
input wire i_ibus_ack,
output wire o_rf_rreq,
output wire o_rf_wreq,
input wire i_rf_ready,
output wire o_rf_rd_en,
input wire i_cond_branch,
input wire i_bne_or_bge,
input wire i_alu_cmp,
input wire i_branch_op,
input wire i_mem_op,
input wire i_shift_op,
input wire i_slt_op,
input wire i_e_op,
input wire i_rd_op,
output wire o_init,
output wire o_cnt_en,
output wire o_cnt0,
output wire o_cnt0to3,
output wire o_cnt12to31,
output wire o_cnt1,
output wire o_cnt2,
output wire o_cnt3,
output wire o_cnt7,
output wire o_ctrl_pc_en,
output reg o_ctrl_jump,
output wire o_ctrl_trap,
input wire i_ctrl_misalign,
output wire o_alu_shamt_en,
input wire i_alu_sh_done,
output wire o_dbus_cyc,
output wire [1:0] o_mem_bytecnt,
input wire i_mem_misalign,
output reg o_cnt_done,
output wire o_bufreg_en);
wire cnt4;
reg stage_two_req;
reg [4:2] o_cnt;
reg [3:0] o_cnt_r;
reg ibus_cyc;
//Update PC in RUN or TRAP states
assign o_ctrl_pc_en = o_cnt_en & !o_init;
assign o_cnt_en = |o_cnt_r;
assign o_mem_bytecnt = o_cnt[4:3];
assign o_cnt0to3 = (o_cnt[4:2] == 3'd0);
assign o_cnt12to31 = (o_cnt[4] | (o_cnt[3:2] == 2'b11));
assign o_cnt0 = (o_cnt[4:2] == 3'd0) & o_cnt_r[0];
assign o_cnt1 = (o_cnt[4:2] == 3'd0) & o_cnt_r[1];
assign o_cnt2 = (o_cnt[4:2] == 3'd0) & o_cnt_r[2];
assign o_cnt3 = (o_cnt[4:2] == 3'd0) & o_cnt_r[3];
assign cnt4 = (o_cnt[4:2] == 3'd1) & o_cnt_r[0];
assign o_cnt7 = (o_cnt[4:2] == 3'd1) & o_cnt_r[3];
assign o_alu_shamt_en = (o_cnt0to3 | cnt4) & o_init;
//Take branch for jump or branch instructions (opcode == 1x0xx) if
//a) It's an unconditional branch (opcode[0] == 1)
//b) It's a conditional branch (opcode[0] == 0) of type beq,blt,bltu (funct3[0] == 0) and ALU compare is true
//c) It's a conditional branch (opcode[0] == 0) of type bne,bge,bgeu (funct3[0] == 1) and ALU compare is false
//Only valid during the last cycle of INIT, when the branch condition has
//been calculated.
wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge));
//slt*, branch/jump, shift, load/store
wire two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op;
assign o_dbus_cyc = !o_cnt_en & init_done & i_mem_op & !i_mem_misalign;
wire trap_pending = WITH_CSR & ((o_ctrl_jump & i_ctrl_misalign) | (i_mem_op & i_mem_misalign));
//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 & trap_pending);
//Prepare RF for writes when everything is ready to enter stage two
assign o_rf_wreq = ((i_shift_op & i_alu_sh_done & init_done) | (i_mem_op & i_dbus_ack) | (stage_two_req & (i_slt_op | i_branch_op))) & !trap_pending;
assign o_rf_rd_en = i_rd_op & o_cnt_en & !o_init;
/*
bufreg is used during mem. branch and shift operations
mem : bufreg is used for dbus address. Shift in data during phase 1.
Shift out during phase 2 if there was an misalignment exception.
branch : Shift in during phase 1. Shift out during phase 2
shift : Shift in during phase 1. Continue shifting between phases (except
for the first cycle after init). Shift out during phase 2
*/
assign o_bufreg_en = (o_cnt_en & (o_init | o_ctrl_trap | i_branch_op)) | (!stage_two_req & i_shift_op);
assign o_ibus_cyc = ibus_cyc & !i_rst;
assign o_init = two_stage_op & !o_pending_irq & !init_done;
reg init_done;
always @(posedge i_clk) begin
//ibus_cyc changes on three conditions.
//1. i_rst is asserted. Together with the async gating above, o_ibus_cyc
// will be asserted as soon as the reset is released. This is how the
// first instruction is fetced
//2. o_cnt_done and o_ctrl_pc_en are asserted. This means that SERV just
// finished updating the PC, is done with the current instruction and
// o_ibus_cyc gets asserted to fetch a new instruction
//3. When i_ibus_ack, a new instruction is fetched and o_ibus_cyc gets
// deasserted to finish the transaction
if (i_ibus_ack | o_cnt_done | i_rst)
ibus_cyc <= o_ctrl_pc_en | i_rst;
if (o_cnt_done) begin
init_done <= o_init & !init_done;
o_ctrl_jump <= o_init & take_branch;
end
o_cnt_done <= (o_cnt[4:2] == 3'b111) & o_cnt_r[2];
//Need a strobe for the first cycle in the IDLE state after INIT
stage_two_req <= o_cnt_done & o_init;
/*
Because SERV is 32-bit bit-serial we need a counter than can count 0-31
to keep track of which bit we are currently processing. o_cnt and o_cnt_r
are used together to create such a counter.
The top three bits (o_cnt) are implemented as a normal counter, but
instead of the two LSB, o_cnt_r is a 4-bit shift register which loops 0-3
When o_cnt_r[3] is 1, o_cnt will be increased.
The counting starts when the core is idle and the i_rf_ready signal
comes in from the RF module by shifting in the i_rf_ready bit as LSB of
the shift register. Counting is stopped by using o_cnt_done to block the
bit that was supposed to be shifted into bit 0 of o_cnt_r.
There are two benefit of doing the counter this way
1. We only need to check four bits instead of five when we want to check
if the counter is at a certain value. For 4-LUT architectures this means
we only need one LUT instead of two for each comparison.
2. We don't need a separate enable signal to turn on and off the counter
between stages, which saves an extra FF and a unique control signal. We
just need to check if o_cnt_r is not zero to see if the counter is
currently running
*/
o_cnt <= o_cnt + {2'd0,o_cnt_r[3]};
o_cnt_r <= {o_cnt_r[2:0],(o_cnt_r[3] & !o_cnt_done) | (i_rf_ready & !o_cnt_en)};
if (i_rst) begin
if (RESET_STRATEGY != "NONE") begin
o_cnt <= 3'd0;
init_done <= 1'b0;
o_ctrl_jump <= 1'b0;
o_cnt_r <= 4'b0000;
end
end
end
generate
if (WITH_CSR) begin
reg irq_sync;
reg misalign_trap_sync;
assign o_ctrl_trap = i_e_op | o_pending_irq | misalign_trap_sync;
always @(posedge i_clk) begin
if (i_ibus_ack)
irq_sync <= 1'b0;
if (i_new_irq)
irq_sync <= 1'b1;
if (i_ibus_ack)
o_pending_irq <= irq_sync;
if (stage_two_req)
misalign_trap_sync <= trap_pending;
if (o_cnt_done)
misalign_trap_sync <= 1'b0;
if (i_rst)
if (RESET_STRATEGY != "NONE") begin
misalign_trap_sync <= 1'b0;
irq_sync <= 1'b0;
o_pending_irq <= 1'b0;
end
end // always @ (posedge i_clk)
end else begin
assign o_ctrl_trap = 0;
always @(*)
o_pending_irq = 1'b0;
end
endgenerate
endmodule