//////////////////////////////////////////////////////////////////////////////// // // Filename: zipcore.v // {{{ // Project: 10Gb Ethernet switch // // Purpose: // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // }}} // Copyright (C) 2023, Gisselquist Technology, LLC // {{{ // This file is part of the ETH10G project. // // The ETH10G project contains free software and gateware, licensed under the // Apache License, Version 2.0 (the "License"). You may not use this project, // or this file, except in compliance with the License. You may obtain a copy // of the License at // }}} // http://www.apache.org/licenses/LICENSE-2.0 // {{{ // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // //////////////////////////////////////////////////////////////////////////////// // `default_nettype none // }}} module zipcore #( // {{{ parameter ADDRESS_WIDTH=30, // 32-b word addr width parameter [31:0] RESET_ADDRESS=32'h010_0000, parameter OPT_MPY = 0, parameter [0:0] OPT_SHIFTS = 1, parameter [0:0] OPT_DIV = 1, parameter [0:0] IMPLEMENT_FPU = 0, parameter [0:0] OPT_EARLY_BRANCHING = 1, parameter [0:0] OPT_CIS = 1'b1, parameter [0:0] OPT_SIM = 1'b0, parameter [0:0] OPT_DISTRIBUTED_REGS = 1'b1, parameter [0:0] OPT_PIPELINED = 1'b1, parameter [0:0] OPT_PIPELINED_BUS_ACCESS = (OPT_PIPELINED), parameter [0:0] OPT_LOCK=1, parameter [0:0] OPT_DCACHE = 1, parameter [0:0] OPT_USERMODE = 1'b1, parameter [0:0] OPT_LOWPOWER = 1'b0, parameter [0:0] OPT_CLKGATE = 1'b1, parameter [0:0] OPT_START_HALTED = 1'b1, parameter [0:0] OPT_DBGPORT = 1'b1, parameter [0:0] OPT_TRACE_PORT = 1'b0, parameter [0:0] OPT_PROFILER = 1'b0, localparam AW=ADDRESS_WIDTH `ifdef FORMAL , parameter F_LGDEPTH=4 `endif // }}} ) ( // {{{ input wire i_clk, i_reset, i_interrupt, output wire o_clken, // Debug interface // {{{ input wire i_halt, i_clear_cache, input wire [4:0] i_dbg_wreg, input wire i_dbg_we, input wire [31:0] i_dbg_data, input wire [4:0] i_dbg_rreg, // output wire o_dbg_stall, output wire [31:0] o_dbg_reg, output reg [2:0] o_dbg_cc, output wire o_break, // }}} // Instruction fetch interface // {{{ output wire o_pf_new_pc, output wire o_clear_icache, output wire o_pf_ready, output wire [(AW+1):0] o_pf_request_address, input wire i_pf_valid, i_pf_illegal, input wire [31:0] i_pf_instruction, input wire [(AW+1):0] i_pf_instruction_pc, // }}} // Memory unit interface // {{{ output wire o_clear_dcache, output wire o_mem_ce, output wire o_bus_lock, output wire [2:0] o_mem_op, output wire [31:0] o_mem_addr, output wire [31:0] o_mem_data, output wire [AW+1:0] o_mem_lock_pc, output wire [4:0] o_mem_reg, input wire i_mem_busy, i_mem_rdbusy, i_mem_pipe_stalled, i_mem_valid, input wire i_bus_err, input wire [4:0] i_mem_wreg, input wire [31:0] i_mem_result, // }}} // Accounting outputs ... to help us count stalls and usage // {{{ output wire o_op_stall, output wire o_pf_stall, output wire o_i_count, // }}} // Debug data for on-line/live tracing // {{{ output wire [31:0] o_debug, //}}} // (Optional) Profiler data // {{{ output wire o_prof_stb, output wire [AW+1:0] o_prof_addr, output wire [31:0] o_prof_ticks // }}} // }}} ); // Local parameter definitions // {{{ // Verilator lint_off UNUSED localparam [0:0] OPT_MEMPIPE = OPT_PIPELINED_BUS_ACCESS; localparam [(AW-1):0] RESET_BUS_ADDRESS = RESET_ADDRESS[AW+1:2]; localparam [3:0] CPU_CC_REG = 4'he, CPU_PC_REG = 4'hf; localparam [3:0] CPU_SUB_OP = 4'h0,// also a compare instruction CPU_AND_OP = 4'h1,// also a test instruction CPU_BREV_OP= 4'h8, CPU_MOV_OP = 4'hd; localparam CPU_CLRDCACHE_BIT=15, // Set to clr D-cache,auto clears CPU_CLRICACHE_BIT=14, // Set to clr I-cache,auto clears CPU_PHASE_BIT = 13, // Set on last half of a CIS CPU_FPUERR_BIT = 12, // Set on floating point error CPU_DIVERR_BIT = 11, // Set on divide error CPU_BUSERR_BIT = 10, // Set on bus error CPU_TRAP_BIT = 9, // User TRAP has taken place CPU_ILL_BIT = 8, // Illegal instruction CPU_BREAK_BIT = 7, CPU_STEP_BIT = 6, // Will step one (or two CIS) ins CPU_GIE_BIT = 5, CPU_SLEEP_BIT = 4; // Verilator lint_on UNUSED // }}} // Register declarations // {{{ // The distributed RAM style comment is necessary on the // SPARTAN6 with XST to prevent XST from oversimplifying the register // set and in the process ruining everything else. It basically // optimizes logic away, to where it no longer works. The logic // as described herein will work, this just makes sure XST implements // that logic. // (* ram_style = "distributed" *) reg [31:0] regset [0:(OPT_USERMODE)? 31:15]; // Condition codes // (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z reg [3:0] flags, iflags; wire [15:0] w_uflags, w_iflags; reg break_en, user_step, sleep, r_halted; wire break_pending, trap, gie, ubreak, pending_interrupt, stepped; wire step; wire ill_err_u; reg ill_err_i; reg ibus_err_flag; wire ubus_err_flag; wire idiv_err_flag, udiv_err_flag; // Verilator coverage_off wire ifpu_err_flag, ufpu_err_flag; // Verilator coverage_on wire ihalt_phase, uhalt_phase; // The master chip enable wire master_ce, master_stall; // // // PIPELINE STAGE #1 :: Prefetch // Variable declarations // // {{{ reg [(AW+1):0] pf_pc; reg new_pc; wire clear_pipeline; reg dcd_stalled; // wire pf_cyc, pf_stb, pf_we, pf_stall, pf_ack, pf_err; // wire [(AW-1):0] pf_addr; // wire pf_valid, pf_gie, pf_illegal; wire pf_gie; `ifdef FORMAL wire [31:0] f_dcd_insn_word; wire f_dcd_insn_gie; wire f_dcd_insn_is_pipeable; reg [31:0] f_op_insn_word; reg [31:0] f_alu_insn_word; `endif assign clear_pipeline = new_pc; // }}} // // PIPELINE STAGE #2 :: Instruction Decode // Variable declarations // // // {{{ wire [3:0] dcd_opn; wire dcd_ce, dcd_phase; wire [4:0] dcd_A, dcd_B, dcd_R, dcd_preA, dcd_preB; wire dcd_Acc, dcd_Bcc, dcd_Apc, dcd_Bpc, dcd_Rcc, dcd_Rpc; wire [3:0] dcd_F; wire dcd_wR, dcd_rA, dcd_rB, dcd_ALU, dcd_M, dcd_DIV, dcd_FP, dcd_wF, dcd_gie, dcd_break, dcd_lock, dcd_pipe, dcd_ljmp; wire dcd_valid; wire [AW+1:0] dcd_pc /* verilator public_flat */; wire [31:0] dcd_I; wire dcd_zI; // true if dcd_I == 0 wire dcd_A_stall, dcd_B_stall, dcd_F_stall; wire dcd_illegal; wire dcd_early_branch, dcd_early_branch_stb; wire [(AW+1):0] dcd_branch_pc; wire dcd_sim; wire [22:0] dcd_sim_immv; wire prelock_stall, last_lock_insn; wire cc_invalid_for_dcd; wire pending_sreg_write; // }}} // // // PIPELINE STAGE #3 :: Read Operands // Variable declarations // // // // {{{ // Now, let's read our operands reg op_valid /* verilator public_flat */, op_valid_mem, op_valid_alu; reg op_valid_div, op_valid_fpu; wire op_stall; wire [3:0] op_opn; wire [4:0] op_R; reg op_Rcc; wire [4:0] op_Aid, op_Bid; wire op_rA, op_rB; reg [31:0] r_op_Av, r_op_Bv; wire [AW+1:0] op_pc; wire [31:0] w_op_Av, w_op_Bv, op_Av, op_Bv; reg [31:0] w_pcB_v, w_pcA_v; reg [31:0] w_op_BnI; wire op_wR; reg op_wF; wire op_gie; wire [3:0] op_Fl; reg [6:0] r_op_F; wire [7:0] op_F; wire op_ce, op_phase, op_pipe; reg r_op_break; wire w_op_valid; wire op_lowpower_clear; wire [8:0] w_cpu_info; // Some pipeline control wires reg op_illegal; wire op_break; wire op_lock; wire op_sim /* verilator public_flat */; wire [22:0] op_sim_immv /* verilator public_flat */; wire alu_sim /* verilator public_flat */; wire [22:0] alu_sim_immv /* verilator public_flat */; // }}} // // // PIPELINE STAGE #4 :: ALU / Memory // Variable declarations // // // {{{ wire [(AW+1):0] alu_pc; reg [4:0] alu_reg; reg r_alu_pc_valid, mem_pc_valid; wire alu_pc_valid; wire alu_phase; wire alu_ce /* verilator public_flat */, alu_stall; wire [31:0] alu_result; wire [3:0] alu_flags; wire alu_valid, alu_busy; wire set_cond; reg alu_wR, alu_wF; wire alu_gie, alu_illegal; wire mem_ce, mem_stalled; wire div_ce, div_error, div_busy, div_valid; wire [31:0] div_result; wire [3:0] div_flags; // Verilator coverage_off wire fpu_ce, fpu_error, fpu_busy, fpu_valid; wire [31:0] fpu_result; wire [3:0] fpu_flags; // Verilator coverage_on reg adf_ce_unconditional; reg dbgv, dbg_clear_pipe; reg [31:0] dbg_val; reg [31:0] debug_pc; reg r_dbg_stall; assign div_ce = (op_valid_div)&&(adf_ce_unconditional)&&(set_cond); assign fpu_ce = (IMPLEMENT_FPU)&&(op_valid_fpu)&&(adf_ce_unconditional)&&(set_cond); // }}} // // // PIPELINE STAGE #5 :: Write-back // Variable declarations // // {{{ wire wr_write_pc, wr_write_cc, wr_write_scc, wr_write_ucc; reg wr_reg_ce, wr_flags_ce; reg [3:0] wr_flags; reg [2:0] wr_index; wire [4:0] wr_reg_id; reg [31:0] wr_gpreg_vl, wr_spreg_vl; wire w_switch_to_interrupt, w_release_from_interrupt; reg [(AW+1):0] ipc; wire [(AW+1):0] upc; reg last_write_to_cc; wire cc_write_hold; reg r_clear_icache; reg pfpcset; reg [2:0] pfpcsrc; wire w_clken; // }}} // }}} //////////////////////////////////////////////////////////////////////// // // MASTER: clock enable. // {{{ //////////////////////////////////////////////////////////////////////// // // assign master_ce = (!i_halt || alu_phase) &&(!cc_write_hold)&&(!o_break)&&(!sleep); // }}} //////////////////////////////////////////////////////////////////////// // // Calculate stall conditions // {{{ //////////////////////////////////////////////////////////////////////// // // // PIPELINE STAGE #1 :: Prefetch // These are calculated externally, within the prefetch module. // // PIPELINE STAGE #2 :: Instruction Decode // {{{ always @(*) if (OPT_PIPELINED) dcd_stalled = (dcd_valid && op_stall); else dcd_stalled = (!master_ce) // Can't step forward when we need to be halted ||(ill_err_i)||(ibus_err_flag)||(idiv_err_flag) ||((dcd_valid || op_valid) && !dcd_early_branch) ||(alu_busy)||(div_busy)||(fpu_busy)||(i_mem_busy); // }}} // // PIPELINE STAGE #3 :: Read Operands // {{{ generate if (OPT_PIPELINED) begin : GEN_OP_STALL reg r_cc_invalid_for_dcd; reg r_pending_sreg_write; // cc_invalid_for_dcd // {{{ initial r_cc_invalid_for_dcd = 1'b0; always @(posedge i_clk) if (clear_pipeline) r_cc_invalid_for_dcd <= 1'b0; else if ((alu_ce || mem_ce)&&(set_cond)&&(op_valid) &&((op_wF) ||((op_wR)&&(op_R[4:0] == { op_gie, CPU_CC_REG })))) // If an instruction in the pipeline will write to the // CC register, then we can't be allowed to release // any instruction depending upon the CC register // (for other than conditional execution) into the // pipeline. r_cc_invalid_for_dcd <= 1'b1; else if (r_cc_invalid_for_dcd) // While the pipeline is busy, keep r_cc_invalid_for_dcd // high. r_cc_invalid_for_dcd <= ((alu_busy)||(i_mem_rdbusy)||(div_busy)||(fpu_busy)); else r_cc_invalid_for_dcd <= 1'b0; assign cc_invalid_for_dcd = r_cc_invalid_for_dcd; // }}} // pending_sreg_write // {{{ // Used to determine if we need to stall for flags to be ready initial r_pending_sreg_write = 1'b0; always @(posedge i_clk) if (clear_pipeline) r_pending_sreg_write <= 1'b0; else if (((adf_ce_unconditional)||(mem_ce)) &&(set_cond)&&(!op_illegal) &&(op_wR) &&(op_R[3:1] == 3'h7) &&(op_R[4:0] != { gie, 4'hf })) r_pending_sreg_write <= 1'b1; else if ((!i_mem_rdbusy)&&(!alu_busy)) r_pending_sreg_write <= 1'b0; assign pending_sreg_write = r_pending_sreg_write; // }}} assign op_stall = (op_valid)&&( // {{{ // Only stall if we're loaded w/valid insns and the // next stage is accepting our instruction (!adf_ce_unconditional)&&(!mem_ce) ) ||(dcd_valid)&&( // If we are halted, then accepting anything // into the Op stage might accept a register // that then gets modified by the debugging // interface so as to be invalid. i_halt // Stall if we need to wait for an operand A // to be ready to read || (dcd_A_stall) // Likewise for B, also includes logic // regarding immediate offset (register must // be in register file if we need to add to // an immediate) ||(dcd_B_stall) // Or if we need to wait on flags to work on the // CC register ||(dcd_F_stall) ); // }}} assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall); end else begin : NO_OP_STALLS // !OPT_PIPELINED // {{{ assign op_stall = 1'b0; // (o_break)||(pending_interrupt); assign op_ce = ((dcd_valid)||(dcd_early_branch))&&(!op_stall); assign pending_sreg_write = 1'b0; assign cc_invalid_for_dcd = 1'b0; // verilator coverage_off // Verilator lint_off UNUSED wire pipe_unused; assign pipe_unused = &{ 1'b0, cc_invalid_for_dcd, pending_sreg_write }; // Verilator lint_on UNUSED // verilator coverage_on // }}} end endgenerate // BUT ... op_ce is too complex for many of the data operations. So // let's make their circuit enable code simpler. In particular, if // op_ doesn't need to be preserved, we can change it all we want // ... right? The clear_pipeline code, for example, really only needs // to determine whether op_valid is true. // assign op_change_data_ce = (!op_stall); // }}} // // PIPELINE STAGE #4 :: ALU / Memory // {{{ // 1. Basic stall is if the previous stage is valid and the next is // busy. // 2. Also stall if the prior stage is valid and the master clock enable // is de-selected // 3. Stall if someone on the other end is writing the CC register, // since we don't know if it'll put us to sleep or not. // 4. Last case: Stall if we would otherwise move a break instruction // through the ALU. Break instructions are not allowed through // the ALU. generate if (OPT_PIPELINED) begin : GEN_ALU_STALL // alu_stall, alu_ce // {{{ assign alu_stall = (((master_stall)||(i_mem_rdbusy))&&(op_valid_alu)) //Case 1&2 ||(wr_reg_ce)&&(wr_write_cc); // assign // alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall) // &&(!clear_pipeline)&&(!op_illegal) // &&(!pending_sreg_write) // &&(!alu_sreg_stall); assign alu_ce = (adf_ce_unconditional)&&(op_valid_alu); // verilator coverage_off // Verilator lint_off unused wire unused_alu_stall = &{ 1'b0, alu_stall }; // Verilator lint_on unused // verilator coverage_on // }}} end else begin : NO_ALU_STALLS // alu_stall, alu_ce // {{{ assign alu_stall = (master_stall); //assign alu_ce = (master_ce)&&(op_valid_alu) // &&(!clear_pipeline) // &&(!alu_stall); assign alu_ce = (adf_ce_unconditional)&&(op_valid_alu); // verilator coverage_off // Verilator lint_off unused wire unused_alu_stall = &{ 1'b0, alu_stall }; // Verilator lint_on unused // verilator coverage_on // }}} end endgenerate // // mem_ce // {{{ // Note: if you change the conditions for mem_ce, you must also change // alu_pc_valid. // assign mem_ce = (master_ce)&&(op_valid_mem)&&(!mem_stalled) &&(!clear_pipeline); // }}} // mem_stalled // {{{ generate if (OPT_PIPELINED_BUS_ACCESS) begin : GEN_PIPELINE_MEM_STALL // {{{ assign mem_stalled = (master_stall)||((op_valid_mem)&&( (i_mem_pipe_stalled) ||(i_bus_err)||(div_error) ||(!op_pipe && i_mem_busy) // Stall waiting for flags to be valid // Or waiting for a write to the PC register // Or CC register, since that can change the // PC as well ||((wr_reg_ce) &&((wr_write_pc)||(wr_write_cc))))); // }}} end else if (OPT_PIPELINED) begin : GEN_MEM_STALL // {{{ assign mem_stalled = (master_stall)||((op_valid_mem)&&( (i_bus_err)||(div_error)||(i_mem_busy) // Stall waiting for flags to be valid // Or waiting for a write to the PC register // Or CC register, since that can change the // PC as well ||((wr_reg_ce) &&((wr_write_pc)||(wr_write_cc))))); // }}} end else begin : NO_MEM_STALL // {{{ assign mem_stalled = master_stall || i_mem_busy; // }}} end endgenerate // }}} // }}} // // Master stall condition // {{{ assign master_stall = (!master_ce)||(!op_valid)||(ill_err_i) ||(step && stepped) ||(ibus_err_flag)||(idiv_err_flag) ||(pending_interrupt && !o_bus_lock)&&(!alu_phase) ||(alu_busy)||(div_busy)||(fpu_busy)||(op_break) ||((OPT_PIPELINED)&&( prelock_stall ||((i_mem_busy)&&(op_illegal)) ||((i_mem_busy)&&(op_valid_div)) ||(alu_illegal)||(o_break))); // }}} // // (Everything but memory) stall condition // {{{ // ALU, DIV, or FPU CE ... equivalent to the OR of all three of these always @(*) if (OPT_PIPELINED) adf_ce_unconditional = (!master_stall)&&(!op_valid_mem)&&(!i_mem_rdbusy) &&(!i_mem_busy || !op_wR || op_R[4:0] != { gie, CPU_CC_REG }); else adf_ce_unconditional = (!master_stall)&&(op_valid)&&(!op_valid_mem); // }}} // }}} //////////////////////////////////////////////////////////////////////// // // PIPELINE STAGE #1 :: Instruction fetch // {{{ //////////////////////////////////////////////////////////////////////// // // assign o_pf_ready = (!dcd_stalled && !dcd_phase); assign o_pf_new_pc = (new_pc)||((dcd_early_branch_stb)&&(!clear_pipeline)); assign o_pf_request_address = ((dcd_early_branch_stb)&&(!clear_pipeline)) ? dcd_branch_pc:pf_pc; assign pf_gie = gie; // }}} //////////////////////////////////////////////////////////////////////// // // PIPELINE STAGE #2 :: Instruction Decode // {{{ //////////////////////////////////////////////////////////////////////// // // assign dcd_ce =((OPT_PIPELINED)&&(!dcd_valid))||(!dcd_stalled); wire [6:0] dcd_full_R, dcd_full_A, dcd_full_B; idecode #( // {{{ .ADDRESS_WIDTH(AW), .OPT_MPY((OPT_MPY!=0)? 1'b1:1'b0), .OPT_SHIFTS(OPT_SHIFTS), .OPT_PIPELINED(OPT_PIPELINED), .OPT_EARLY_BRANCHING(OPT_EARLY_BRANCHING), .OPT_DIVIDE(OPT_DIV), .OPT_FPU(IMPLEMENT_FPU), .OPT_LOCK(OPT_LOCK), .OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), .OPT_USERMODE(OPT_USERMODE), .OPT_SIM(OPT_SIM), .OPT_CIS(OPT_CIS), .OPT_LOWPOWER(OPT_LOWPOWER) // }}} ) instruction_decoder( // {{{ .i_clk(i_clk), .i_reset(i_reset||clear_pipeline||o_clear_icache), .i_ce(dcd_ce), .i_stalled(dcd_stalled), .i_instruction(i_pf_instruction), .i_gie(pf_gie), .i_pc(i_pf_instruction_pc), .i_pf_valid(i_pf_valid), .i_illegal(i_pf_illegal), .o_valid(dcd_valid), .o_phase(dcd_phase), .o_illegal(dcd_illegal), .o_pc(dcd_pc), .o_dcdR(dcd_full_R), .o_dcdA(dcd_full_A), .o_dcdB(dcd_full_B), .o_preA(dcd_preA), .o_preB(dcd_preB), .o_I(dcd_I), .o_zI(dcd_zI), .o_cond(dcd_F), .o_wF(dcd_wF), .o_op(dcd_opn), .o_ALU(dcd_ALU), .o_M(dcd_M), .o_DV(dcd_DIV), .o_FP(dcd_FP), .o_break(dcd_break), .o_lock(dcd_lock), .o_wR(dcd_wR), .o_rA(dcd_rA), .o_rB(dcd_rB), .o_early_branch(dcd_early_branch), .o_early_branch_stb(dcd_early_branch_stb), .o_branch_pc(dcd_branch_pc), .o_ljmp(dcd_ljmp), .o_pipe(dcd_pipe), .o_sim(dcd_sim), .o_sim_immv(dcd_sim_immv) `ifdef FORMAL , .f_insn_word(f_dcd_insn_word), .f_insn_gie(f_dcd_insn_gie), .f_insn_is_pipeable(f_dcd_insn_is_pipeable) `endif // }}} ); assign { dcd_Rcc, dcd_Rpc, dcd_R } = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? dcd_full_R : 7'h0; assign { dcd_Acc, dcd_Apc, dcd_A } = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? dcd_full_A : 7'h0; assign { dcd_Bcc, dcd_Bpc, dcd_B } = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? dcd_full_B : 7'h0; assign dcd_gie = pf_gie; // }}} //////////////////////////////////////////////////////////////////////// // // PIPELINE STAGE #3 :: Read Operands (Registers) // {{{ //////////////////////////////////////////////////////////////////////// // // // op_pipe // {{{ generate if (OPT_PIPELINED_BUS_ACCESS) begin : GEN_OP_PIPE // Memory pipelining reg r_op_pipe; // To be a pipeable operation, there must be // two valid adjacent instructions // Both must be memory instructions // Both must be writes, or both must be reads // Both operations must be to the same identical address, // or at least a single (one) increment above that // address // // However ... we need to know this before this clock, hence // this is calculated in the instruction decoder. initial r_op_pipe = 1'b0; always @(posedge i_clk) if ((OPT_LOWPOWER && i_reset)||(clear_pipeline)||(i_halt)) r_op_pipe <= 1'b0; else if (op_ce) r_op_pipe <= (dcd_pipe)&&(op_valid_mem)&&(!OPT_LOWPOWER || !dcd_illegal); else if ((wr_reg_ce)&&(wr_reg_id == op_Bid[4:0])) r_op_pipe <= 1'b0; else if (mem_ce) // Clear us any time an op_ is clocked in r_op_pipe <= 1'b0; assign op_pipe = r_op_pipe; `ifdef FORMAL always @(*) if (OPT_LOWPOWER && !op_valid_mem) assert(!r_op_pipe); `endif end else begin : NO_OP_PIPE assign op_pipe = 1'b0; end endgenerate // }}} // Read from our register set // {{{ generate if (OPT_DISTRIBUTED_REGS) begin : GEN_DISTRIBUTED_REGS // {{{ if (OPT_USERMODE) begin : GEN_FULL_REGSET assign w_op_Av = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? regset[dcd_A] : 32'h0; assign w_op_Bv = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? regset[dcd_B] : 32'h0; end else begin : GEN_NO_USERREGS assign w_op_Av = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? regset[dcd_A[3:0]] : 32'h0; assign w_op_Bv = (!OPT_LOWPOWER || dcd_valid || !OPT_PIPELINED) ? regset[dcd_B[3:0]] : 32'h0; end // verilator coverage_off // verilator lint_off UNUSED wire unused_prereg_addrs; assign unused_prereg_addrs = &{ 1'b0, dcd_preA, dcd_preB }; // verilator lint_on UNUSED // verilator coverage_on // }}} end else begin : GEN_BLOCKRAM // {{{ reg [31:0] pre_rewrite_value, pre_op_Av, pre_op_Bv; reg pre_rewrite_flag_A, pre_rewrite_flag_B; always @(posedge i_clk) if (dcd_ce) begin pre_rewrite_flag_A <= (wr_reg_ce)&&(dcd_preA == wr_reg_id); pre_rewrite_flag_B <= (wr_reg_ce)&&(dcd_preB == wr_reg_id); pre_rewrite_value <= wr_gpreg_vl; end else if (OPT_PIPELINED && dcd_valid) begin pre_rewrite_flag_A <= (wr_reg_ce)&&(dcd_A == wr_reg_id); pre_rewrite_flag_B <= (wr_reg_ce)&&(dcd_B == wr_reg_id); pre_rewrite_value <= wr_gpreg_vl; end if (OPT_USERMODE) begin : GEN_FULL_REGSET always @(posedge i_clk) if (dcd_ce || (OPT_PIPELINED && dcd_valid)) pre_op_Av <= regset[dcd_ce ? dcd_preA : dcd_A]; always @(posedge i_clk) if (dcd_ce || (OPT_PIPELINED && dcd_valid)) pre_op_Bv <= regset[dcd_ce ? dcd_preB : dcd_B]; end else begin : GEN_NO_USERREGS always @(posedge i_clk) if (dcd_ce || (OPT_PIPELINED && dcd_valid)) pre_op_Av <= regset[dcd_ce ? dcd_preA[3:0] : dcd_A[3:0]]; always @(posedge i_clk) if (dcd_ce || (OPT_PIPELINED && dcd_valid)) pre_op_Bv <= regset[dcd_ce ? dcd_preB[3:0] : dcd_B[3:0]]; end assign w_op_Av = (pre_rewrite_flag_A) ? pre_rewrite_value : pre_op_Av; assign w_op_Bv = (pre_rewrite_flag_B) ? pre_rewrite_value : pre_op_Bv; // }}} end endgenerate // }}} assign w_cpu_info = { // {{{ 1'b1, (OPT_MPY >0)? 1'b1:1'b0, (OPT_DIV >0)? 1'b1:1'b0, (IMPLEMENT_FPU >0)? 1'b1:1'b0, OPT_PIPELINED, 1'b0, (OPT_EARLY_BRANCHING > 0)? 1'b1:1'b0, OPT_PIPELINED_BUS_ACCESS, OPT_CIS }; // }}} always @(*) begin w_pcA_v = 0; if ((!OPT_USERMODE)||(dcd_A[4] == dcd_gie)) w_pcA_v[(AW+1):0] = { dcd_pc[AW+1:2], 2'b00 }; else w_pcA_v[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; end // Op register addresses // {{{ generate if (OPT_PIPELINED) begin : OP_REG_ADVANEC // {{{ reg [4:0] r_op_R; reg [4:0] r_op_Aid, r_op_Bid; reg r_op_rA, r_op_rB; initial r_op_R = 0; initial r_op_Aid = 0; initial r_op_Bid = 0; initial r_op_rA = 0; initial r_op_rB = 0; initial op_Rcc = 0; always @(posedge i_clk) begin if (op_ce && (!OPT_LOWPOWER || w_op_valid)) begin r_op_R <= dcd_R; r_op_Aid <= dcd_A; if ((dcd_rB)&&(!dcd_early_branch)&&(!dcd_illegal)) r_op_Bid <= dcd_B; r_op_rA <= (dcd_rA)&&(!dcd_early_branch)&&(!dcd_illegal); r_op_rB <= (dcd_rB)&&(!dcd_early_branch)&&(!dcd_illegal); op_Rcc <= (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie); end if (op_lowpower_clear) begin r_op_rA <= 1'b0; r_op_rB <= 1'b0; end end assign op_R = r_op_R; assign op_Aid = r_op_Aid; assign op_Bid = r_op_Bid; assign op_rA = r_op_rA; assign op_rB = r_op_rB; // }}} end else begin : OP_REG_COPY // {{{ assign op_R = dcd_R; assign op_Aid = dcd_A; assign op_Bid = dcd_B; assign op_rA = dcd_rA; assign op_rB = dcd_rB; always @(*) op_Rcc = (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie); // }}} end endgenerate // }}} // r_op_Av -- The registered value of the op A register // {{{ reg [2:0] avsrc; reg [2:0] bvsrc; reg [1:0] bisrc; always @(*) begin avsrc = 3'b000; if (!OPT_PIPELINED || op_ce) begin if (dcd_Apc) avsrc = 3'b101; else if (dcd_Acc) avsrc = 3'b110; else avsrc = 3'b111; end if (OPT_PIPELINED && wr_reg_ce) begin if (!op_ce && wr_reg_id == op_Aid && op_rA) avsrc = 3'b100; else if (op_ce && wr_reg_id == dcd_A) avsrc = 3'b100; end end // 44313 initial r_op_Av = 0; always @(posedge i_clk) begin if (avsrc[2]) case(avsrc[1:0]) 2'b00: r_op_Av <= wr_gpreg_vl; 2'b01: r_op_Av <= w_pcA_v; 2'b10: r_op_Av <= { w_cpu_info, w_op_Av[22:16], (dcd_A[4])?w_uflags:w_iflags }; 2'b11: r_op_Av <= w_op_Av; endcase if (op_lowpower_clear) r_op_Av <= 0; end // }}} // w_op_B -- The registered value of the op B register // {{{ always @(*) begin w_pcB_v = 0; if ((!OPT_USERMODE)||(dcd_B[4] == dcd_gie)) w_pcB_v[(AW+1):0] = { dcd_pc[AW+1:2], 2'b00 }; else w_pcB_v[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; end always @(*) if (!dcd_rB) bisrc = 0; else if ((OPT_PIPELINED)&&(wr_reg_ce)&&(wr_reg_id == dcd_B)) bisrc = 1; else if (dcd_Bcc) bisrc = 2; else bisrc = 3; always @(*) case(bisrc[1:0]) 2'b00: w_op_BnI = 0; 2'b01: w_op_BnI = wr_gpreg_vl; 2'b10: w_op_BnI = { w_cpu_info, w_op_Bv[22:16], (dcd_B[4]) ? w_uflags : w_iflags }; 2'b11: w_op_BnI = w_op_Bv; endcase always @(*) begin bvsrc = 0; if ((!OPT_PIPELINED)||(op_ce)) begin if ((dcd_Bpc)&&(dcd_rB)) bvsrc = 3'b100; else bvsrc = 3'b101; end else if ((OPT_PIPELINED)&&(op_rB) &&(wr_reg_ce)&&(op_Bid == wr_reg_id)) bvsrc = 3'b110; end initial r_op_Bv = 0; always @(posedge i_clk) begin if (bvsrc[2]) casez(bvsrc[1:0]) 2'b00: r_op_Bv <= w_pcB_v + { dcd_I[29:0], 2'b00 }; 2'b01: r_op_Bv <= w_op_BnI + dcd_I; 2'b1?: r_op_Bv <= wr_gpreg_vl; endcase if (op_lowpower_clear) r_op_Bv <= 0; end // }}} // op_F // {{{ // The logic here has become more complex than it should be, no thanks // to Xilinx's Vivado trying to help. The conditions are supposed to // be two sets of four bits: the top bits specify what bits matter, the // bottom specify what those top bits must equal. However, two of // conditions check whether bits are on, and those are the only two // conditions checking those bits. Therefore, Vivado complains that // these two bits are redundant. Hence the convoluted expression // below, arriving at what we finally want in the (now wire net) // op_F. initial r_op_F = 7'h00; always @(posedge i_clk) begin if ((!OPT_PIPELINED)||(op_ce)) // Cannot do op_change_data_ce here since op_F depends // upon being either correct for a valid op, or correct // for the last valid op begin // Set the flag condition codes, bit order is [3:0]=VNCZ case(dcd_F[2:0]) 3'h0: r_op_F <= 7'h00; // Always 3'h1: r_op_F <= 7'h11; // Z 3'h2: r_op_F <= 7'h44; // LT 3'h3: r_op_F <= 7'h22; // C 3'h4: r_op_F <= 7'h08; // V 3'h5: r_op_F <= 7'h10; // NE 3'h6: r_op_F <= 7'h40; // GE (!N) 3'h7: r_op_F <= 7'h20; // NC endcase end if (op_lowpower_clear) r_op_F <= 7'h00; end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } assign op_F = { r_op_F[3], r_op_F[6:0] }; // }}} // op_valid // {{{ assign w_op_valid = (!clear_pipeline)&&(dcd_valid) &&(!dcd_ljmp)&&(!dcd_early_branch); initial op_valid = 1'b0; initial op_valid_alu = 1'b0; initial op_valid_mem = 1'b0; initial op_valid_div = 1'b0; initial op_valid_fpu = 1'b0; always @(posedge i_clk) if ((i_reset)||(clear_pipeline)) begin // {{{ op_valid <= 1'b0; op_valid_alu <= 1'b0; op_valid_mem <= 1'b0; op_valid_div <= 1'b0; op_valid_fpu <= 1'b0; // }}} end else if (op_ce) begin // {{{ // Do we have a valid instruction? // The decoder may vote to stall one of its // instructions based upon something we currently // have in our queue. This instruction must then // move forward, and get a stall cycle inserted. // Hence, the test on dcd_stalled here. If we must // wait until our operands are valid, then we aren't // valid yet until then. if (OPT_PIPELINED || !op_valid) begin op_valid <= (w_op_valid)||(dcd_early_branch); op_valid_alu <= (w_op_valid)&&((dcd_ALU)||(dcd_illegal)); op_valid_mem <= (dcd_M)&&(!dcd_illegal) &&(w_op_valid); op_valid_div <= (OPT_DIV)&&(dcd_DIV)&&(!dcd_illegal)&&(w_op_valid); op_valid_fpu <= (IMPLEMENT_FPU)&&(dcd_FP)&&(!dcd_illegal)&&(w_op_valid); end else if ((adf_ce_unconditional)||(mem_ce)) begin op_valid <= 1'b0; op_valid_alu <= 1'b0; op_valid_mem <= 1'b0; op_valid_div <= 1'b0; op_valid_fpu <= 1'b0; end // }}} end else if ((adf_ce_unconditional)||(mem_ce)) begin // {{{ // If nothing advances into the OP stage, yet what was in // the OP stage moves forward, then we need to invalidate what // used to be here. op_valid <= 1'b0; op_valid_alu <= 1'b0; op_valid_mem <= 1'b0; op_valid_div <= 1'b0; op_valid_fpu <= 1'b0; // }}} end // }}} // op_lowpower_clear // {{{ generate if (!OPT_LOWPOWER || !OPT_PIPELINED) begin : NO_OP_LOWPOWER_CLEAR assign op_lowpower_clear = 1'b0; end else begin : GEN_OP_LOWPOWER_CLEAR reg r_op_lowpower_clear; always @(*) if (i_reset || clear_pipeline) r_op_lowpower_clear = 1'b1; else if (op_ce && !w_op_valid) r_op_lowpower_clear = 1'b1; else if ((!op_ce || !w_op_valid)&&(adf_ce_unconditional || mem_ce)) r_op_lowpower_clear = 1'b1; else r_op_lowpower_clear = 1'b0; assign op_lowpower_clear = r_op_lowpower_clear; end endgenerate // }}} // op_break // {{{ // Here's part of our debug interface. When we recognize a break // instruction, we set the op_break flag. That'll prevent this // instruction from entering the ALU, and cause an interrupt before // this instruction. Thus, returning to this code will cause the // break to repeat and continue upon return. To get out of this // condition, replace the break instruction with what it is supposed // to be, step through it, and then replace it back. In this fashion, // a debugger can step through code. // assign w_op_break = (dcd_break)&&(r_dcd_I[15:0] == 16'h0001); initial r_op_break = 1'b0; always @(posedge i_clk) if (clear_pipeline) r_op_break <= 1'b0; else if ((OPT_PIPELINED)&&(op_ce)) r_op_break <= (dcd_valid)&&(dcd_break)&&(!dcd_illegal); else if ((!OPT_PIPELINED)&&(dcd_valid)) r_op_break <= (dcd_break)&&(!dcd_illegal)&&(!step || !stepped); assign op_break = r_op_break; `ifdef FORMAL always @(*) if (op_break) assert(op_valid || clear_pipeline); `endif // }}} // op_lock // {{{ generate if (!OPT_LOCK) begin : NO_OPLOCK assign op_lock = 1'b0; // verilator coverage_off // Verilator lint_off UNUSED wire dcd_lock_unused; assign dcd_lock_unused = &{ 1'b0, dcd_lock }; // Verilator lint_on UNUSED // verilator coverage_on end else // if (!OPT_LOCK) begin : GEN_OPLOCK reg r_op_lock; initial r_op_lock = 1'b0; always @(posedge i_clk) if (clear_pipeline) r_op_lock <= 1'b0; else if (op_ce) r_op_lock <= (dcd_valid)&&(dcd_lock) &&(!dcd_illegal); assign op_lock = r_op_lock; end endgenerate // }}} // op_illegal // {{{ initial op_illegal = 1'b0; always @(posedge i_clk) if ((i_reset)||(clear_pipeline)) op_illegal <= 1'b0; else if (OPT_PIPELINED) begin if (op_ce) op_illegal <= (dcd_valid)&&(!dcd_ljmp) &&(!dcd_early_branch)&&(dcd_illegal); end else if (!OPT_PIPELINED) begin if (dcd_valid) op_illegal <= (!dcd_ljmp)&&(!dcd_early_branch)&&(dcd_illegal); end // }}} // op_wF // {{{ always @(posedge i_clk) begin if ((!OPT_PIPELINED)||(op_ce)) op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR)) &&(!dcd_early_branch); if (op_lowpower_clear) op_wF <= 1'b0; end // }}} // op_wR // {{{ generate if ((OPT_PIPELINED)||(OPT_EARLY_BRANCHING)) begin : GEN_OP_WR reg r_op_wR; initial r_op_wR = 1'b0; always @(posedge i_clk) begin if (op_ce) r_op_wR <= (dcd_wR)&&(!dcd_early_branch); if (op_lowpower_clear) r_op_wR <= 1'b0; end assign op_wR = r_op_wR; end else begin : COPY_OP_WR assign op_wR = dcd_wR; end endgenerate // }}} // op_sim, op_sim_immv // {{{ generate if (OPT_SIM) begin : OP_SIM // // Only execute this if OPT_SIM is true--that is, if we // are running from a simulated environment. // reg r_op_sim; reg [22:0] r_op_sim_immv; always @(posedge i_clk) if (op_ce) begin r_op_sim <= dcd_sim && (!OPT_LOWPOWER || w_op_valid); r_op_sim_immv <= dcd_sim_immv; if (OPT_LOWPOWER && (!dcd_sim || !w_op_valid)) r_op_sim_immv <= 0; end else if (adf_ce_unconditional) begin r_op_sim <= 1'b0; if (OPT_LOWPOWER) r_op_sim_immv <= 0; end assign op_sim = r_op_sim; assign op_sim_immv = r_op_sim_immv; end else begin : NO_OP_SIM assign op_sim = 0; assign op_sim_immv = 0; // verilator coverage_off // Verilator lint_off UNUSED wire op_sim_unused; assign op_sim_unused = &{ 1'b0, dcd_sim, dcd_sim_immv }; // Verilator lint_on UNUSED // verilator coverage_on end endgenerate // }}} // op_pc // {{{ generate if ((OPT_PIPELINED)||(OPT_EARLY_BRANCHING)) begin : SET_OP_PC reg [AW+1:0] r_op_pc; initial r_op_pc[0] = 1'b0; always @(posedge i_clk) if (op_ce) begin if (dcd_early_branch) // Need to retire an early branch as a NOOP, // to make sure our PC is properly updated r_op_pc <= dcd_branch_pc; else if (!OPT_LOWPOWER || w_op_valid) r_op_pc <= dcd_pc; end assign op_pc = r_op_pc; end else begin : SET_OP_PC assign op_pc = dcd_pc; end endgenerate // }}} // op_opn -- the opcode of the current operation // {{{ generate if (!OPT_PIPELINED) begin : COPY_DCD_OPN assign op_opn = dcd_opn; end else begin : FWD_OPERATION reg [3:0] r_op_opn; always @(posedge i_clk) if (op_ce && (!OPT_LOWPOWER || w_op_valid || dcd_early_branch)) begin // Which ALU operation? Early branches are // unimplemented moves r_op_opn <= ((dcd_early_branch)||(dcd_illegal)) ? CPU_MOV_OP : dcd_opn; // opM <= dcd_M; // Is this a memory operation? // What register will these results be written into? end assign op_opn = r_op_opn; end endgenerate // }}} assign op_gie = gie; assign op_Fl = (op_gie)?(w_uflags[3:0]):(w_iflags[3:0]); // op_phase // {{{ generate if (OPT_CIS) begin : OPT_CIS_OP_PHASE reg r_op_phase; initial r_op_phase = 1'b0; always @(posedge i_clk) if ((i_reset)||(clear_pipeline)) r_op_phase <= 1'b0; else if (op_ce) r_op_phase <= (dcd_phase)&&((!dcd_wR)||(!dcd_Rpc)); assign op_phase = r_op_phase; end else begin : OPT_NOCIS_OP_PHASE assign op_phase = 1'b0; // verilator lint_off UNUSED wire OPT_CIS_dcdRpc; assign OPT_CIS_dcdRpc = dcd_Rpc; // verilator lint_on UNUSED end endgenerate // }}} // op_Av -- the combinatorial A value input to the ALU/MEM/DIV stage // {{{ // This is tricky. First, the PC and Flags registers aren't kept in // register set but in special registers of their own. So step one // is to select the right register. Step to is to replace that // register with the results of an ALU or memory operation, if such // results are now available. Otherwise, we'd need to insert a wait // state of some type. // // The alternative approach would be to define some sort of // op_stall wire, which would stall any upstream stage. // We'll create a flag here to start our coordination. Once we // define this flag to something other than just plain zero, then // the stalls will already be in place. generate if (OPT_PIPELINED) begin : FWD_OP_AV assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid)) ? wr_gpreg_vl : r_op_Av; end else begin : COPY_OP_AV assign op_Av = r_op_Av; end endgenerate // }}} // dcd_A_stall // {{{ // Stall if we have decoded an instruction that will read register A // AND ... something that may write a register is running // AND (series of conditions here ...) // The operation might set flags, and we wish to read the // CC register // OR ... (No other conditions) generate if (OPT_PIPELINED) begin : GEN_DCDA_STALL assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere &&((op_valid)||(i_mem_rdbusy) ||(div_busy)||(fpu_busy)) &&(((op_wF)||(cc_invalid_for_dcd))&&(dcd_Acc)) ||((dcd_rA)&&(dcd_Acc)&&(cc_invalid_for_dcd)); end else begin : NO_DCDA_STALL // There are no pipeline hazards, if we aren't pipelined assign dcd_A_stall = 1'b0; end endgenerate // }}} // op_Bv // {{{ assign op_Bv = ((OPT_PIPELINED)&&(wr_reg_ce) &&(wr_reg_id == op_Bid)&&(op_rB)) ? wr_gpreg_vl: r_op_Bv; // }}} // dcd_B_stall, dcd_F_stall // {{{ generate if (OPT_PIPELINED) begin : DCD_BF_STALLS // Stall if we have decoded an instruction that will read register B // AND ... something that may write a (unknown) register is running // AND (series of conditions here ...) // The operation might set flags, and we wish to read the // CC register // OR the operation might set register B, and we still need // a clock to add the offset to it assign dcd_B_stall = (dcd_rB) // &&(dcd_valid) is checked for elsewhere // {{{ // If the op stage isn't valid, yet something // is running, then it must have been valid. // We'll use the last values from that stage // (op_wR, op_wF, op_R) in our logic below. &&((op_valid)||(i_mem_rdbusy) ||(div_busy)||(fpu_busy)||(alu_busy)) &&( // Okay, what happens if the result register // from instruction 1 becomes the input for // instruction two, *and* there's an immediate // offset in instruction two? In that case, we // need an extra clock between the two // instructions to calculate the base plus // offset. // // What if instruction 1 (or before) is in a // memory pipeline? We may no longer know what // the register was! We will then need to // blindly wait. We'll temper this only waiting // if we're not piping this new instruction. // If we were piping, the pipe logic in the // decode circuit has told us that the hazard // is clear, so we're okay then. // ((!dcd_zI)&&( ((op_R == dcd_B)&&(op_wR)) ||((i_mem_rdbusy)&&(!dcd_pipe)) ||(((alu_busy || div_busy || i_mem_rdbusy)) &&(alu_reg == dcd_B)) ||((wr_reg_ce)&&(wr_reg_id[3:1] == 3'h7)) )) // Stall following any instruction that will // set the flags, if we're going to need the // flags (CC) register for op_B. ||(((op_wF)||(cc_invalid_for_dcd))&&(dcd_Bcc)) // Stall on any ongoing memory operation that // will write to op_B -- captured above // ||((i_mem_busy)&&(!mem_we)&&(mem_last_reg==dcd_B)&&(!dcd_zI)) ) ||((dcd_rB)&&(dcd_Bcc)&&(cc_invalid_for_dcd)); // }}} assign dcd_F_stall = ((!dcd_F[3]) // {{{ ||((dcd_rA)&&(dcd_A[3:1]==3'h7) &&(dcd_A[4:0] != { gie, 4'hf})) ||((dcd_rB)&&(dcd_B[3:1]==3'h7)) &&(dcd_B[4:0] != { gie, 4'hf})) &&(((op_valid)&&(op_wR) &&(op_R[3:1]==3'h7) &&(op_R[4:0]!={gie, 4'hf})) ||(pending_sreg_write)); // &&(dcd_valid) is checked for elsewhere // }}} end else begin : NO_PIPELINE_NO_STALL // {{{ // No stalls without pipelining, 'cause how can you have a // pipeline hazard without the pipeline? assign dcd_B_stall = 1'b0; assign dcd_F_stall = 1'b0; // }}} end endgenerate // }}} // }}} //////////////////////////////////////////////////////////////////////// // // PIPELINE STAGE #4 :: Apply Instruction // {{{ //////////////////////////////////////////////////////////////////////// // // // The ALU itself cpuops #( // {{{ .OPT_MPY(OPT_MPY), .OPT_SHIFTS(OPT_SHIFTS), .OPT_LOWPOWER(OPT_LOWPOWER) // }}} ) doalu( // {{{ .i_clk(i_clk), .i_reset((i_reset)||(clear_pipeline)), .i_stb(alu_ce), .i_op((!OPT_LOWPOWER || alu_ce) ? op_opn : 4'h0), .i_a( (!OPT_LOWPOWER || alu_ce) ? op_Av : 32'h0), .i_b( (!OPT_LOWPOWER || alu_ce) ? op_Bv : 32'h0), .o_c(alu_result), .o_f(alu_flags), .o_valid(alu_valid), .o_busy(alu_busy) // }}} ); // Divide // {{{ generate if (OPT_DIV != 0) begin : DIVIDE `ifdef FORMAL `define DIVIDE_MODULE abs_div `else `define DIVIDE_MODULE div `endif `DIVIDE_MODULE #( .OPT_LOWPOWER(OPT_LOWPOWER) ) thedivide( // {{{ .i_clk(i_clk), .i_reset(i_reset || clear_pipeline), .i_wr(div_ce), .i_signed(op_opn[0]), .i_numerator(op_Av), .i_denominator(op_Bv), .o_busy(div_busy), .o_valid(div_valid), .o_err(div_error), .o_quotient(div_result), .o_flags(div_flags) // }}} ); end else begin : NO_DIVIDE // {{{ assign div_error = 1'b0; // Can't be high unless div_valid assign div_busy = 1'b0; assign div_valid = 1'b0; assign div_result= 32'h00; assign div_flags = 4'h0; // Make verilator happy here // verilator coverage_off // verilator lint_off UNUSED wire unused_divide; assign unused_divide = &{ 1'b0, div_ce }; // verilator lint_on UNUSED // verilator coverage_on // }}} end endgenerate // }}} // (Non-existent) FPU // {{{ generate if (IMPLEMENT_FPU != 0) begin : FPU // // sfpu thefpu(i_clk, i_reset, fpu_ce, op_opn[2:0], // op_Av, op_Bv, fpu_busy, fpu_valid, fpu_err, fpu_result, // fpu_flags); // `ifdef FORMAL abs_div thefpu(i_clk, ((i_reset)||(clear_pipeline)), fpu_ce, op_opn[2:0], op_Av, op_Bv, fpu_busy, fpu_valid, fpu_error, fpu_result, fpu_flags); `else assign fpu_error = 1'b0; // Must only be true if fpu_valid assign fpu_busy = 1'b0; assign fpu_valid = 1'b0; assign fpu_result= 32'h00; assign fpu_flags = 4'h0; `endif end else begin : GEN_NOFPU assign fpu_error = 1'b0; assign fpu_busy = 1'b0; assign fpu_valid = 1'b0; assign fpu_result= 32'h00; assign fpu_flags = 4'h0; end endgenerate // }}} // Condition handling // {{{ assign set_cond = ((op_F[7:4]&op_Fl[3:0])==op_F[3:0]); initial alu_wF = 1'b0; initial alu_wR = 1'b0; generate if (OPT_PIPELINED) begin : GEN_COND_PIPELINED always @(posedge i_clk) if (i_reset) begin alu_wR <= 1'b0; alu_wF <= 1'b0; end else if (alu_ce) begin // alu_reg <= op_R; alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal); alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal); end else if (!alu_busy) begin // These are strobe signals, so clear them if they // aren't going to be set for any particular clock alu_wR <= (r_halted)&&(OPT_DBGPORT && i_dbg_we && !o_dbg_stall); alu_wF <= 1'b0; end end else begin : GEN_COND_NOPIPE always @(posedge i_clk) begin alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal); alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal); end end endgenerate // }}} // alu_phase // {{{ // Instruction phase (which half of the instruction we are on) tracking generate if (OPT_CIS) begin : GEN_ALU_PHASE reg r_alu_phase; initial r_alu_phase = 1'b0; always @(posedge i_clk) if ((i_reset)||(clear_pipeline)) r_alu_phase <= 1'b0; else if (((adf_ce_unconditional)||(mem_ce))&&(op_valid)) r_alu_phase <= op_phase; else if ((adf_ce_unconditional)||(mem_ce)) r_alu_phase <= 1'b0; assign alu_phase = r_alu_phase; end else begin : NO_ALUPHASE assign alu_phase = 1'b0; end endgenerate // }}} // alu_reg // {{{ generate if (OPT_PIPELINED) begin : GEN_ALUREG_PIPE always @(posedge i_clk) if (alu_ce || div_ce || o_mem_ce || fpu_ce) alu_reg <= op_R; else if (OPT_DBGPORT && i_dbg_we && !o_dbg_stall) alu_reg <= i_dbg_wreg; end else begin : GEN_ALUREG_NOPIPE always @(posedge i_clk) if (OPT_DBGPORT && i_dbg_we && !o_dbg_stall) alu_reg <= i_dbg_wreg; else alu_reg <= op_R; end endgenerate // }}} // wr_index // {{{ initial wr_index = 0; always @(posedge i_clk) begin if ((OPT_PIPELINED && (mem_ce || adf_ce_unconditional)) ||(!OPT_PIPELINED && op_valid)) begin wr_index <= 0; /* if (op_valid_mem) wr_index <= 3'b001; if (op_valid_alu) wr_index <= 3'b010; if (op_valid_div) wr_index <= 3'b011; if (op_valid_fpu) wr_index <= 3'b100; */ wr_index[0] <= (op_valid_mem | op_valid_div); wr_index[1] <= (op_valid_alu | op_valid_div); wr_index[2] <= (op_valid_fpu); end if (OPT_DBGPORT && i_dbg_we && !o_dbg_stall) wr_index <= 3'b000; if (!IMPLEMENT_FPU) wr_index[2] <= 1'b0; end // }}} // // DEBUG Register write access starts here // // {{{ initial dbgv = 1'b0; always @(posedge i_clk) if (i_reset || !r_halted) dbgv <= 0; else dbgv <= OPT_DBGPORT && i_dbg_we && !o_dbg_stall; always @(posedge i_clk) if (!OPT_LOWPOWER || (OPT_DBGPORT && i_dbg_we)) dbg_val <= i_dbg_data; // dbg_clear_pipe // {{{ always @(posedge i_clk) if (i_reset || clear_pipeline || !r_halted) dbg_clear_pipe <= 0; else if (OPT_DBGPORT && i_dbg_we && !o_dbg_stall) begin dbg_clear_pipe <= 1'b0; if (!OPT_PIPELINED) dbg_clear_pipe <= 1'b1; if ((i_dbg_wreg == op_Bid)&&(op_rB)) dbg_clear_pipe <= 1'b1; if (i_dbg_wreg[3:1] == 3'h7) dbg_clear_pipe <= 1'b1; end else if ((!OPT_PIPELINED)&&(i_clear_cache && !o_dbg_stall)) dbg_clear_pipe <= 1'b1; else dbg_clear_pipe <= 1'b0; // }}} assign alu_gie = gie; // }}} // r_alu_pc // {{{ generate if (OPT_PIPELINED) begin : GEN_ALU_PC reg [(AW+1):0] r_alu_pc; initial r_alu_pc = 0; always @(posedge i_clk) if ((adf_ce_unconditional) ||((master_ce)&&(op_valid_mem) &&(!clear_pipeline)&&(!mem_stalled))) r_alu_pc <= op_pc; assign alu_pc = r_alu_pc; end else begin : GEN_ALU_PC_NOPIPE assign alu_pc = op_pc; end endgenerate // }}} // r_alu_illegal // {{{ generate if (OPT_PIPELINED) begin : SET_ALU_ILLEGAL reg r_alu_illegal; initial r_alu_illegal = 0; always @(posedge i_clk) if (clear_pipeline) r_alu_illegal <= 1'b0; else if (adf_ce_unconditional) r_alu_illegal <= op_illegal; else r_alu_illegal <= 1'b0; assign alu_illegal = (r_alu_illegal); end else begin : SET_ALU_ILLEGAL assign alu_illegal = op_illegal; end endgenerate // }}} // r_alu_pc_valid, mem_pc_valid // {{{ initial r_alu_pc_valid = 1'b0; initial mem_pc_valid = 1'b0; always @(posedge i_clk) if (clear_pipeline) r_alu_pc_valid <= 1'b0; else if ((adf_ce_unconditional)&&(!op_phase)) r_alu_pc_valid <= 1'b1; else if (((!alu_busy)&&(!div_busy)&&(!fpu_busy))||(clear_pipeline)) r_alu_pc_valid <= 1'b0; assign alu_pc_valid = (r_alu_pc_valid) &&((!alu_busy)&&(!div_busy)&&(!fpu_busy)); always @(posedge i_clk) if (i_reset) mem_pc_valid <= 1'b0; else mem_pc_valid <= (mem_ce); // }}} // Bus lock logic // {{{ generate if (OPT_LOCK) begin : BUSLOCK reg r_prelock_stall; reg [1:0] r_bus_lock; reg [AW+1:0] r_lock_pc; // r_prelock_stall // {{{ initial r_prelock_stall = 1'b0; always @(posedge i_clk) if (!OPT_PIPELINED || clear_pipeline) r_prelock_stall <= 1'b0; else if (op_valid && op_lock && r_bus_lock == 2'b00 && adf_ce_unconditional) r_prelock_stall <= 1'b1; else if (op_valid && dcd_valid && (i_pf_valid || dcd_early_branch)) r_prelock_stall <= 1'b0; // }}} // r_lock_pc // {{{ always @(posedge i_clk) if (op_valid && op_ce && op_lock) r_lock_pc <= op_pc-4; `ifdef FORMAL always @(posedge i_clk) cover(op_valid && op_ce && op_lock); `endif // }}} // r_bus_lock // {{{ // Count 3 cycles. The lock will only hold solid for three // cycles after the LOCK instruction is received. Count those // cycles here. initial r_bus_lock = 2'b00; always @(posedge i_clk) if (clear_pipeline) r_bus_lock <= 2'b00; else if (op_valid && (adf_ce_unconditional||mem_ce)) begin if (r_bus_lock != 2'b00) r_bus_lock <= r_bus_lock - 1; else if (op_lock) r_bus_lock <= 2'b11; end // }}} assign prelock_stall = OPT_PIPELINED && r_prelock_stall; assign o_bus_lock = |r_bus_lock; assign o_mem_lock_pc = r_lock_pc; assign last_lock_insn = (r_bus_lock <= 1); `ifdef FORMAL // {{{ if (OPT_PIPELINED) begin (* anyconst *) reg r_nojump_lock; always @(*) if (r_nojump_lock && (r_prelock_stall || r_bus_lock)) begin assume(!dcd_early_branch); assume(!dcd_ljmp); end always @(*) if (!clear_pipeline) case(r_bus_lock) 2'b11: if (!prelock_stall && r_nojump_lock) begin assert(op_valid && dcd_valid && i_pf_valid); end 2'b10: begin assert(!prelock_stall); if (r_nojump_lock) begin assert(dcd_valid); assert(op_valid + dcd_valid + i_pf_valid >= 2'b10); end end 2'b01: begin assert(!prelock_stall); if (r_nojump_lock && !dcd_illegal) begin assert(op_valid || dcd_valid || i_pf_valid); end end 2'b00: assert(!prelock_stall); endcase end else begin always @(*) assert(!prelock_stall); end // }}} `endif end else begin : NO_BUSLOCK // {{{ assign prelock_stall = 1'b0; assign o_bus_lock = 1'b0; assign o_mem_lock_pc = 0; assign last_lock_insn= 1; // }}} end endgenerate // }}} // Memory interface // {{{ // This logic is now managed outside the ZipCore // assign o_mem_ce = mem_ce && set_cond; assign o_mem_op = ((mem_ce && set_cond) || !OPT_LOWPOWER) ? op_opn[2:0] : 3'h0; assign o_mem_data = ((mem_ce && set_cond) || !OPT_LOWPOWER) ? op_Av : 32'h0; assign o_mem_addr = ((mem_ce && set_cond) || !OPT_LOWPOWER) ? op_Bv : 32'h0; assign o_mem_reg = ((mem_ce && set_cond) || !OPT_LOWPOWER) ? op_R : 5'h0; // }}} // Sim instructions, alu_sim, alu_sim_immv // {{{ wire cpu_sim; generate if (OPT_SIM) begin : ALU_SIM reg r_alu_sim; reg [22:0] r_alu_sim_immv; wire [4:0] regid; assign regid = { (OPT_USERMODE && gie), op_sim_immv[3:0]}; if (OPT_USERMODE) begin : GEN_ALLSIM // {{{ assign cpu_sim = !i_reset && !clear_pipeline && adf_ce_unconditional && set_cond && op_sim && op_valid_alu &&(!wr_reg_ce || !wr_write_pc || wr_reg_id[4] != alu_gie); initial r_alu_sim = 1'b0; always @(posedge i_clk) begin if (cpu_sim) begin // Execute simulation only instructions // {{{ if ((op_sim_immv[19:10] == 10'h0)&&(op_sim_immv[8])) begin // [N/S]EXIT // {{{ $finish; // if (op_sim_immv[19:4] == 16'h0031) // Exit(User reg), code cpu_wr_gpreg // Verilog offers no support for this. // Veri1ator might, but it isn't // standard. // if (op_sim_immv[19:4] == 16'h0030) // Exit(Normal reg), code cpu_wr_gpreg // $finish; // if (op_sim_immv[19:8] == 12'h001) // Exit(Immediate), code cpu_wr_gpreg // $finish; // }}} end if (op_sim_immv[19:0] == 20'h2ff) begin // DUMP all registers // {{{ if (!op_gie) begin $write("sR0 : %08x ", regset[0]); $write("sR1 : %08x ", regset[1]); $write("sR2 : %08x ", regset[2]); $write("sR3 : %08x\n",regset[3]); $write("sR4 : %08x ", regset[4]); $write("sR5 : %08x ", regset[5]); $write("sR6 : %08x ", regset[6]); $write("sR7 : %08x\n",regset[7]); $write("sR8 : %08x ", regset[8]); $write("sR9 : %08x ", regset[9]); $write("sR10: %08x ", regset[10]); $write("sR11: %08x\n",regset[11]); $write("sR12: %08x ", regset[12]); $write("sSP : %08x ", regset[13]); $write("sCC : %08x ", w_iflags); $write("sPC : %08x\n", (!op_gie) ? op_pc : ipc); $write("\n", (!op_gie) ? op_pc : ipc); end $write("uR0 : %08x ", regset[16]); $write("uR1 : %08x ", regset[17]); $write("uR2 : %08x ", regset[18]); $write("uR3 : %08x\n",regset[19]); $write("uR4 : %08x ", regset[20]); $write("uR5 : %08x ", regset[21]); $write("uR6 : %08x ", regset[22]); $write("uR7 : %08x\n",regset[23]); $write("uR8 : %08x ", regset[24]); $write("uR9 : %08x ", regset[25]); $write("uR10: %08x ", regset[26]); $write("uR11: %08x\n",regset[27]); $write("uR12: %08x ", regset[28]); $write("uSP : %08x ", regset[29]); $write("uCC : %08x ", w_uflags); $write("uPC : %08x\n", (op_gie) ? op_pc : upc); // }}} end if (op_sim_immv[19:4] == 16'h0020) begin // Dump a register // {{{ $write("@%08x ", op_pc); $write("%c", (op_gie) ? "s":"u"); $write("R[%2d] = 0x", op_sim_immv[3:0]); // Dump a register if (wr_reg_ce && wr_reg_id == regid) $display("%08x", wr_gpreg_vl); else $display("%08x", regset[regid]); // }}} end if (op_sim_immv[19:4] == 16'h0021) begin // Dump a user register // {{{ $write("@%08x u", op_pc); $write("R[%2d] = 0x", op_sim_immv[3:0]); if (wr_reg_ce && wr_reg_id == { 1'b1, op_sim_immv[3:0] }) $display("%08x\n", wr_gpreg_vl); else $display("%08x\n", regset[{ 1'b1, op_sim_immv[3:0]}]); // }}} end if (op_sim_immv[19:4] == 16'h0023) begin // SOUT(user register) // {{{ if (wr_reg_ce && wr_reg_id == { 1'b1, op_sim_immv[3:0] }) $write("%c", wr_gpreg_vl[7:0]); else $write("%c", regset[{ 1'b1, op_sim_immv[3:0]}][7:0]); // }}} end if (op_sim_immv[19:4] == 16'h0022) begin // SOUT(register) // {{{ if (wr_reg_ce && wr_reg_id == regid) $write("%c", wr_gpreg_vl[7:0]); else $write("%c", regset[regid][7:0]); // }}} end if (op_sim_immv[19:8] == 12'h004) begin // SOUT(Immediate) // {{{ $write("%c", op_sim_immv[7:0]); // }}} end // ELSE unrecognized SIM instruction // Set alu_sim either way r_alu_sim <= 1'b1; // }}} end else r_alu_sim <= 1'b0; if (adf_ce_unconditional) r_alu_sim_immv <= op_sim_immv; end // }}} end else begin : GEN_NO_USERSIM // {{{ assign cpu_sim = !i_reset && !clear_pipeline && adf_ce_unconditional && set_cond && op_sim && op_valid_alu &&(!wr_reg_ce || !wr_write_pc || wr_reg_id[4] != alu_gie); initial r_alu_sim = 1'b0; always @(posedge i_clk) begin if (cpu_sim) begin // Execute simulation only instructions // {{{ if ((op_sim_immv[19:10] == 10'h0)&&(op_sim_immv[8])) begin // [N/S]EXIT // {{{ $finish; // if (op_sim_immv[19:4] == 16'h0031) // Exit(User reg), code cpu_wr_gpreg // Verilog offers no support for this. // Veri1ator might, but it isn't // standard. // if (op_sim_immv[19:4] == 16'h0030) // Exit(Normal reg), code cpu_wr_gpreg // $finish; // if (op_sim_immv[19:8] == 12'h001) // Exit(Immediate), code cpu_wr_gpreg // $finish; // }}} end if (op_sim_immv[19:0] == 20'h2ff) begin // DUMP all registers // {{{ if (!op_gie) begin $write(" R0 : %08x ", regset[0]); $write(" R1 : %08x ", regset[1]); $write(" R2 : %08x ", regset[2]); $write(" R3 : %08x\n",regset[3]); $write(" R4 : %08x ", regset[4]); $write(" R5 : %08x ", regset[5]); $write(" R6 : %08x ", regset[6]); $write(" R7 : %08x\n",regset[7]); $write(" R8 : %08x ", regset[8]); $write(" R9 : %08x ", regset[9]); $write(" R10: %08x ", regset[10]); $write(" R11: %08x\n",regset[11]); $write(" R12: %08x ", regset[12]); $write(" SP : %08x ", regset[13]); $write(" CC : %08x ", w_iflags); $write(" PC : %08x\n", op_pc); end // }}} end if (op_sim_immv[19:5] == 15'h0010) begin // Dump a register // {{{ $write("@%08x ", op_pc); $write(" R[%2d] = 0x", op_sim_immv[3:0]); // Dump a register if (wr_reg_ce&&wr_reg_id[3:0] == regid[3:0]) $display("%08x", wr_gpreg_vl); else $display("%08x", regset[regid[3:0]]); // }}} end if (op_sim_immv[19:5] == 15'h0011) begin // SOUT(user register) // {{{ if (wr_reg_ce && wr_reg_id[3:0] == op_sim_immv[3:0]) $write("%c", wr_gpreg_vl[7:0]); else $write("%c", regset[op_sim_immv[3:0]][7:0]); // }}} end if (op_sim_immv[19:8] == 12'h004) begin // SOUT(Immediate) // {{{ $write("%c", op_sim_immv[7:0]); // }}} end // ELSE unrecognized SIM instruction // Set alu_sim either way r_alu_sim <= 1'b1; // }}} end else r_alu_sim <= 1'b0; if (adf_ce_unconditional) r_alu_sim_immv <= op_sim_immv; end // Verilator lint_off UNUSED wire unused_simmv; assign unused_simmv = &{ 1'b0, regid[4] }; // Verilator lint_on UNUSED // }}} end assign alu_sim = r_alu_sim; assign alu_sim_immv = r_alu_sim_immv; end else begin : NO_ALU_SIM assign alu_sim = 0; assign alu_sim_immv = 0; assign cpu_sim = 0; end endgenerate // }}} // }}} //////////////////////////////////////////////////////////////////////// // // PIPELINE STAGE #5 :: Write-back results // {{{ //////////////////////////////////////////////////////////////////////// // // // // This stage is not allowed to stall. If results are ready to be // written back, they are written back at all cost. Sleepy CPU's // won't prevent write back, nor debug modes, halting the CPU, nor // anything else. Indeed, the (master_ce) bit is only as relevant // as knowinig something is available for writeback. // (was) wr_discard, wr_reg_ce // {{{ // // Write back to our generic register set ... // When shall we write back? On one of two conditions // Note that the flags needed to be checked before issuing the // bus instruction, so they don't need to be checked here. // Further, alu_wR includes (set_cond), so we don't need to // check for that here either. It also includes alu_illegal, so // again--doesn't need to be checked again here. /* // 12 inputs. This implementation is made worse by wr_index. always @(*) case(wr_index) 3'b000: wr_reg_ce = dbgv; 3'b001: wr_reg_ce = i_mem_valid; //3'b010: wr_reg_ce = (!clear_pipeline)&&(alu_wR && alu_valid); 3'b011: wr_reg_ce = (!clear_pipeline)&&(div_valid)&&(!div_error); 3'b1??: wr_reg_ce = (!clear_pipeline)&&(fpu_valid)&&(!fpu_error); default: wr_reg_ce = (!clear_pipeline)&&(alu_wR && alu_valid); endcase */ // 7-LUT -- without FPU or wr_index (which wasn't needed) always @(*) begin wr_reg_ce = dbgv || i_mem_valid; if ((alu_wR && alu_valid) ||(div_valid && !div_error) ||(fpu_valid && !fpu_error)) wr_reg_ce = wr_reg_ce || !clear_pipeline; end `ifdef FORMAL always @(*) if (!i_reset && ((alu_wR && alu_valid) ||(div_valid && !div_error) ||(fpu_valid && !fpu_error))) assert(!dbgv && !i_mem_valid); always @(*) if (!i_reset) casez(wr_index) 3'b000: assert(!i_mem_valid && (!alu_wR || !alu_valid) && (!div_valid || div_error) && (!fpu_valid || fpu_error)); 3'b001: assert(!dbgv && (!alu_wR || !alu_valid) && (!div_valid || div_error) && (!fpu_valid || fpu_error)); 3'b010: assert(!dbgv && !i_mem_valid // && (!alu_wR || !alu_valid) && (!div_valid || div_error) && (!fpu_valid || fpu_error)); 3'b011: assert(!dbgv && !i_mem_valid && (!alu_wR || !alu_valid) // && (!div_valid || div_error) && (!fpu_valid || fpu_error)); 3'b100: assert(!dbgv && !i_mem_valid && (!alu_wR || !alu_valid) && (!div_valid || div_error)); // && (!fpu_valid || fpu_error); default: assert(0); endcase `endif // }}} // wr_reg_id, wr_write-cc, wr_write_scc, wr_write_ucc, wr_write_pc // {{{ // Which register shall be written? // COULD SIMPLIFY THIS: by adding three bits to these registers, // One or PC, one for CC, and one for GIE match // Note that the alu_reg is the register to write on a divide or // FPU operation. generate if (OPT_USERMODE) begin : GEN_USERREG assign wr_reg_id = (i_mem_valid) ? i_mem_wreg : alu_reg; end else begin : NO_USERREG assign wr_reg_id[3:0] = (i_mem_valid) ? i_mem_wreg[3:0] : alu_reg[3:0]; assign wr_reg_id[4] = 1'b0; end endgenerate // Are we writing to the CC register? // one 5-LUT ea assign wr_write_cc = (wr_reg_id[3:0] == CPU_CC_REG); assign wr_write_scc = (wr_reg_id[4:0] == {1'b0, CPU_CC_REG}); assign wr_write_ucc = (wr_reg_id[4:0] == {1'b1, CPU_CC_REG}); // Are we writing to the PC? assign wr_write_pc = (wr_reg_id[3:0] == CPU_PC_REG); // }}} // wr_?preg_vl: Select from among sources the value to be writtena // {{{ // One 6-LUT per bit, or 32 6-LUTs w/o FPU always @(*) casez(wr_index) 3'b000: wr_gpreg_vl = dbg_val; 3'b001: wr_gpreg_vl = i_mem_result; // 3'b010: wr_gpreg_vl = alu_result; 3'b011: wr_gpreg_vl = div_result; 3'b1??: wr_gpreg_vl = fpu_result; default: wr_gpreg_vl = alu_result; endcase // One 6-LUT per bit, or 32 6-LUTs always @(*) case(wr_index[1:0]) 2'b00: wr_spreg_vl = dbg_val; 2'b01: wr_spreg_vl = i_mem_result; // 3'b010: wr_gpreg_vl = alu_result; default: wr_spreg_vl = alu_result; endcase // }}} // Update the register set // {{{ generate if (OPT_USERMODE) begin : SET_REGISTERS always @(posedge i_clk) if (wr_reg_ce) regset[wr_reg_id] <= wr_gpreg_vl; end else begin : SET_SREGISTERS always @(posedge i_clk) if (wr_reg_ce) regset[wr_reg_id[3:0]] <= wr_gpreg_vl; end endgenerate // }}} // // Write back to the condition codes/flags register ... // wr_flags_ce : should condition codes be written to? // {{{ // When shall we write to our flags register? alu_wF already // includes the set condition ... // assign wr_flags_ce = (alu_wF)&&((alu_valid) // ||(div_valid)||(fpu_valid)) // &&(!clear_pipeline)&&(!alu_illegal); /* // 10 inputs always @(*) begin wr_flags_ce = 0; if (alu_wF && !clear_pipeline) // Includes !alu_illegal in wF case(wr_index) // 3'b000: wr_flags_ce = 0; // Debug // 3'b001: wr_flags_ce = 0; // Memory 3'b010: wr_flags_ce = alu_valid; // ALU 3'b011: wr_flags_ce = div_valid && !div_error; // DIV 3'b1??: wr_flags_ce = fpu_valid && !fpu_error; // FPU default: wr_flags_ce = 0; endcase end */ // 7-LUT -- since we don't need wr_index always @(*) begin wr_flags_ce = alu_valid || (div_valid && !div_error) || (fpu_valid && !fpu_error); if (!alu_wF || clear_pipeline) wr_flags_ce = 1'b0; end `ifdef FORMAL always @(*) if (!i_reset) begin casez(wr_index) 3'b000: assert(wr_flags_ce == 1'b0); 3'b001: assert(wr_flags_ce == 1'b0); 3'b010: assert(wr_flags_ce == (alu_wF && !clear_pipeline && alu_valid)); 3'b011: assert(wr_flags_ce == (alu_wF && !clear_pipeline && div_valid && !div_error)); 3'b100: assert(IMPLEMENT_FPU && wr_flags_ce == (alu_wF && !clear_pipeline && fpu_valid && !fpu_error)); default: assert(0); endcase if (alu_illegal) assert(!div_valid && !fpu_valid && (!alu_wF || !alu_valid)); end `endif // }}} // wr_flags: what should the new condition codes be? // {{{ always @(*) begin wr_flags = 0; casez(wr_index) 3'b010: wr_flags = alu_flags; 3'b011: wr_flags = div_flags; 3'b1??: wr_flags = fpu_flags; default: wr_flags = 0; endcase end // }}} // w_uflags, w_iflags : Define the current CC registers // {{{ assign w_uflags = { 2'b00, uhalt_phase, ufpu_err_flag, udiv_err_flag, ubus_err_flag, trap, ill_err_u, ubreak, !gie && user_step, 1'b1, sleep, (wr_flags_ce && alu_gie) ? wr_flags : flags }; assign w_iflags = { 2'b00, ihalt_phase, ifpu_err_flag, idiv_err_flag, ibus_err_flag, trap, ill_err_i, break_en, 1'b0, 1'b0, sleep, (wr_flags_ce && !alu_gie) ? wr_flags : iflags }; // }}} // flags: The user condition codes, Z, C, N, and V // {{{ // What value to write? always @(posedge i_clk) // If explicitly writing the register itself if (wr_reg_ce && wr_write_ucc) flags <= wr_gpreg_vl[3:0]; // Otherwise if we're setting the flags from an ALU operation else if (wr_flags_ce && alu_gie) flags <= wr_flags; // }}} // iflags: The supervisor condition codes, Z, C, N, and V // {{{ always @(posedge i_clk) if (wr_reg_ce && wr_write_scc) iflags <= wr_gpreg_vl[3:0]; else if (wr_flags_ce && !alu_gie) iflags <= wr_flags; // }}} // break_en // {{{ // The 'break' enable bit. This bit can only be set from supervisor // mode. It controls what the CPU does upon encountering a break // instruction. // // The goal, upon encountering a break is that the CPU should stop and // not execute the break instruction, choosing instead to enter into // either interrupt mode or halt first. // if ((break_en) AND (break_instruction)) // user mode or not // HALT CPU // else if (break_instruction) // only in user mode // set an interrupt flag, set the user break bit, // go to supervisor mode, allow supervisor to step the CPU. initial break_en = 1'b0; always @(posedge i_clk) if (i_reset) break_en <= 1'b0; else if ((wr_reg_ce)&&(wr_write_scc)) break_en <= wr_spreg_vl[CPU_BREAK_BIT]; // }}} // break_pending // {{{ generate if (OPT_PIPELINED) begin : GEN_PENDING_BREAK reg r_break_pending; initial r_break_pending = 1'b0; always @(posedge i_clk) if (clear_pipeline || !op_valid) r_break_pending <= 1'b0; else if (op_break && !r_break_pending) r_break_pending <= (!alu_busy)&&(!div_busy) &&(!fpu_busy)&&(!i_mem_busy) &&(!wr_reg_ce) && (!step || !stepped); // else // No need to clear this here. The break will force the // pipeline to be cleared above, at which point we can // clear this register. // r_break_pending <= 1'b0; assign break_pending = r_break_pending; end else begin : GEN_BREAK_NOPIPE assign break_pending = op_break; `ifdef FORMAL always @(*) if (!gie && user_step && stepped) assert(!op_break); `endif end endgenerate // }}} // o_break // {{{ // // This is a 12-input equation on an output. Can this be // simplified any? What happens if o_break is set? Will it ever // clear on its own, or does it require a write to the debug port? assign o_break = (break_en || !op_gie)&&(break_pending) &&(!clear_pipeline) ||(ill_err_i) ||((!alu_gie)&&(i_bus_err)) ||((!alu_gie)&&(div_error)) ||((!alu_gie)&&(fpu_error)) ||((!alu_gie)&&(alu_illegal)&&(!clear_pipeline)); `ifdef FORMAL // Can I assume that, if break_pending is true, that we're either // in supervisor mode, or that break_en is set? If so, can I // simplify the calculation above? // // No, because once break_pending is set, the supervisor can then // adjust break_en without adjusting the external break. // // always @(*) // if (break_pending) // assert(!op_gie || break_en); always @(*) if (!alu_gie && alu_illegal && !clear_pipeline) assert(!master_ce); // Do I need to break on the last condition above? always @(posedge i_clk) if (!i_reset && $past(!i_reset && !alu_gie && alu_illegal && !clear_pipeline && !dbgv)) assert(ill_err_i); always @(*) if (!i_reset) assert(!dbgv || !alu_valid); `endif // }}} // sleep // {{{ // The sleep register. Setting the sleep register causes the CPU to // sleep until the next interrupt. Setting the sleep register within // interrupt mode causes the processor to halt until a reset. This is // a panic/fault halt. The trick is that you cannot be allowed to // set the sleep bit and switch to supervisor mode in the same // instruction: users are not allowed to halt the CPU. initial sleep = 1'b0; generate if (OPT_USERMODE) begin : GEN_SLEEP // {{{ initial sleep = 1'b0; always @(posedge i_clk) if (i_reset || w_switch_to_interrupt) // Wake up on any reset or any switch to supervisor mode sleep <= 1'b0; else if (wr_reg_ce && wr_write_cc) begin // // !GIE && SLEEP ==> halted // GIE && SLEEP ==> sleep until an interrupt // if (!alu_gie) // In supervisor mode, we have no protections. // The supervisor can set the sleep bit however // he wants. Well ... not quite. Switching to // user mode and sleep mode should only be // possible if the interrupt flag isn't set. // Hence, if an interrupt is pending, then any // WAIT instruction essentially becomes a NOOP. // // Thus: if (i_interrupt) // &&(wr_spreg_vl[GIE]) // don't set the sleep bit // otherwise however it would o.w. be set sleep <= (wr_spreg_vl[CPU_SLEEP_BIT]) &&((!i_interrupt) ||(!wr_spreg_vl[CPU_GIE_BIT])); else if (wr_spreg_vl[CPU_GIE_BIT]) // In user mode, however, you can only set the // sleep mode while remaining in user mode. // You can't switch to sleep mode *and* // supervisor mode at the same time, lest you // halt the CPU. sleep <= wr_spreg_vl[CPU_SLEEP_BIT]; end // }}} end else begin : GEN_NO_USERMODE_SLEEP // {{{ // Even with no user mode, we still want to implement a sleep // instruction. Here, we create an r_sleep_is_halt to // differentiate between the halt and the sleep condition // so that the supervisor can still cause the CPU to sleep. // reg r_sleep_is_halt; initial r_sleep_is_halt = 1'b0; always @(posedge i_clk) if (i_reset) r_sleep_is_halt <= 1'b0; else if (wr_reg_ce && wr_write_cc && wr_spreg_vl[CPU_SLEEP_BIT] && !wr_spreg_vl[CPU_GIE_BIT]) // Setting SLEEP and supervisor mode at the same time // halts the CPU. Halts can only be set here. // They can only be cleared on reset. r_sleep_is_halt <= 1'b1; // Trying to switch to user mode, either via a WAIT or an RTU // instruction will cause the CPU to sleep until an interrupt, // in the NO-USERMODE build. always @(posedge i_clk) if (i_reset || (i_interrupt && !r_sleep_is_halt)) sleep <= 1'b0; else if ((wr_reg_ce)&&(wr_write_cc) &&(wr_spreg_vl[CPU_GIE_BIT])) sleep <= 1'b1; // }}} end endgenerate // }}} // step : debug single-step control // {{{ initial user_step = 1'b0; always @(posedge i_clk) if (i_reset || !OPT_USERMODE) user_step <= 1'b0; else if ((wr_reg_ce)&&(!alu_gie)&&(wr_write_ucc)) // The supervisor can adjust whether or not we are stepping // the user mode process. This bit does not automatically // clear, but can always be written while in supervisor mode. user_step <= wr_spreg_vl[CPU_STEP_BIT]; assign step = user_step && gie; // }}} // o_clken // {{{ generate if (!OPT_CLKGATE) begin : NO_CLOCK_GATE assign w_clken = 1'b1; assign o_clken = 1'b1; end else begin : GEN_CLOCK_GATE reg r_clken; // Actual clock gating signal // // = r_clken || (i_interrupt&&!i_halt) || i_dbg_we // initial r_clken = !OPT_START_HALTED; always @(posedge i_clk) if (i_reset) r_clken <= !OPT_START_HALTED; else if (i_halt && r_halted && (!OPT_DBGPORT || !i_dbg_we)) r_clken <= i_mem_busy || !i_halt || o_mem_ce; else if (!i_halt&& (!sleep || i_interrupt || pending_interrupt)) r_clken <= 1'b1; else // if (sleep || i_halt) begin r_clken <= 1'b0; // If we are in the middle of a lock operation, then // don't shut the clock off if (o_bus_lock) r_clken <= 1'b1; // If we are in between two compressed instructions // from the same word, then don't disable the clock if (alu_phase) r_clken <= 1'b1; // Don't shut the clock off if we are still busy with // any previous operation(s) if (i_mem_busy || o_mem_ce || alu_busy || div_busy || fpu_busy || wr_reg_ce ||(OPT_DBGPORT && i_dbg_we) || i_bus_err) r_clken <= 1'b1; if (i_halt && !r_halted) r_clken <= 1'b1; // Should we wait for a valid PF result before halting? // if (!i_pf_valid) r_clken <= 1'b1; // if (!op_valid && !dcd_illegal) r_clken <= 1'b1; // if (!dcd_valid && !i_pf_illegal) r_clken <= 1'b1; end assign w_clken = r_clken; // Wake up on interrupts, debug write requests, or the raising // of the halt flag if we're not sleeping. assign o_clken = r_clken || (OPT_DBGPORT && i_dbg_we) || i_clear_cache || (!i_halt && (i_interrupt || !sleep)); end endgenerate // }}} // gie, switch_to_interrupt, release_from_interrupt, r_user_stepped // {{{ // The GIE register. Only interrupts can disable the interrupt register generate if (OPT_USERMODE) begin : GEN_PENDING_INTERRUPT // {{{ reg r_pending_interrupt; reg r_user_stepped; // r_user_stepped is used to make certain that we stop a // user task once a full instruction has been accomplished. initial r_user_stepped = 1'b0; always @(posedge i_clk) if (i_reset || !gie || !user_step) r_user_stepped <= 1'b0; // else if(w_switch_to_interrupt) // While this is technically what we want, we can wait // a clock cycle to speed up the CPU by not depending upon // the complex w_switch_to_interrupt calculation here // r_user_stepped <= 1'b0; else if (op_valid && !op_phase && !op_lock && last_lock_insn && (adf_ce_unconditional || mem_ce)) r_user_stepped <= 1'b1; initial r_pending_interrupt = 1'b0; always @(posedge i_clk) if (i_reset) r_pending_interrupt <= 1'b0; else if (!gie || w_switch_to_interrupt) r_pending_interrupt <= 1'b0; else if (clear_pipeline && (!user_step || !stepped)) r_pending_interrupt <= 1'b0; else begin if (i_interrupt) r_pending_interrupt <= 1'b1; if (break_pending) r_pending_interrupt <= 1'b1; if (adf_ce_unconditional && op_illegal) r_pending_interrupt <= 1'b1; if (((!alu_busy && !i_mem_busy && !div_busy && !fpu_busy) || wr_reg_ce) && user_step && stepped) r_pending_interrupt <= 1'b1; end assign pending_interrupt = r_pending_interrupt && !i_halt; assign w_switch_to_interrupt = (gie)&&( // On interrupt (obviously) ((pending_interrupt) &&(!alu_phase)&&(!o_bus_lock)&&(!i_mem_busy)) // // On division by zero. If the divide isn't // implemented, div_valid and div_error will be short // circuited and that logic will be bypassed ||(div_error) // // Same thing on a floating point error. Note that // fpu_error must *never* be set unless fpu_valid is // also set as well, else this will fail. ||(fpu_error) // // ||(i_bus_err) // // If we write to the CC register ||((wr_reg_ce)&&(!wr_spreg_vl[CPU_GIE_BIT]) &&(wr_reg_id[4])&&(wr_write_cc)) ); assign w_release_from_interrupt = (!gie)&&(!i_interrupt) // Then if we write the sCC register &&(((wr_reg_ce)&&(wr_spreg_vl[CPU_GIE_BIT]) &&(wr_write_scc)) ); `ifdef FORMAL always @(posedge i_clk) if (r_pending_interrupt && gie && !clear_pipeline) assert(i_interrupt || user_step || alu_illegal || ill_err_u || break_pending); always @(posedge i_clk) if (f_past_valid && $past(user_step && stepped && !o_bus_lock && !o_dbg_stall)) assert(!gie || r_pending_interrupt); `endif assign stepped = r_user_stepped; // }}} end else begin : NO_PENDING_INTS // {{{ assign w_switch_to_interrupt = 1'b0; assign w_release_from_interrupt = 1'b0; assign pending_interrupt = 1'b0; assign stepped = 1'b0; // Verilator lint_off UNUSED wire unused_int_signals; assign unused_int_signals = &{ 1'b0, last_lock_insn }; // Verilator lint_on UNUSED // }}} end endgenerate generate if (OPT_USERMODE) begin : SET_GIE reg r_gie; initial r_gie = 1'b0; always @(posedge i_clk) if (i_reset) r_gie <= 1'b0; // Supervisor mode else if (w_switch_to_interrupt) r_gie <= 1'b0; // Supervisor mode else if (w_release_from_interrupt) r_gie <= 1'b1; // User mode assign gie = r_gie; end else begin : ZERO_GIE assign gie = 1'b0; end endgenerate // }}} // trap, ubreak // {{{ generate if (OPT_USERMODE) begin : SET_TRAP_N_UBREAK // {{{ reg r_trap; reg r_ubreak; // A trap is generated when the user writes to the CC // register to clear the GIE bit. This is how the supervisor // can tell that supervisor mode was entered by a request from // usermode. initial r_trap = 1'b0; always @(posedge i_clk) if ((i_reset)||(w_release_from_interrupt)) r_trap <= 1'b0; else if (wr_reg_ce && wr_write_ucc) begin if (!alu_gie) // The trap bit can only be cleared by the // supervisor. r_trap <= (r_trap)&&(wr_spreg_vl[CPU_TRAP_BIT]); else if (!wr_spreg_vl[CPU_GIE_BIT]) // && alu_gie // Execute a trap r_trap <= !dbgv; end // A user break is an indication of an exception. Something // went wrong. When entering supervisor mode, if this bit // is set the supervisor then knows something went wrong in // userspace that needs to be looked into. initial r_ubreak = 1'b0; always @(posedge i_clk) if (i_reset || w_release_from_interrupt) r_ubreak <= 1'b0; else if (op_gie && break_pending && w_switch_to_interrupt) // Breaks are set when a BREAK instruction is accepted // for execution from the OP stage, *and* when in user // mode r_ubreak <= 1'b1; else if ((!alu_gie || dbgv)&&(wr_reg_ce)&&(wr_write_ucc)) // Allow the supervisor or debug port to clear this // register--but not to set it r_ubreak <= (ubreak)&&(wr_spreg_vl[CPU_BREAK_BIT]); assign trap = r_trap; assign ubreak = r_ubreak; // }}} end else begin : NO_USERTRAP assign trap = 1'b0; assign ubreak = 1'b0; end endgenerate // }}} // ill_err_i, ill_err_u: Illegal instruction flags // {{{ initial ill_err_i = 1'b0; always @(posedge i_clk) if (i_reset) ill_err_i <= 1'b0; else if (dbgv && wr_write_scc) // Only the debug interface (or a CPU reset) can clear the // supervisor's illegal instruction flag ill_err_i <= (ill_err_i)&&(wr_spreg_vl[CPU_ILL_BIT]); else if (!alu_gie && alu_illegal && !clear_pipeline) // The supervisor's illegal instruction flag is set in // supervisor (not user) mode, on trying to execute the illegal // instruction ill_err_i <= 1'b1; generate if (OPT_USERMODE) begin : SET_USER_ILLEGAL_INSN reg r_ill_err_u; // // The user's illegal instruction exception flag. Used and // cleared by the supervisor. // initial r_ill_err_u = 1'b0; always @(posedge i_clk) // The bit is automatically cleared on release from interrupt // or reset if (i_reset || w_release_from_interrupt) r_ill_err_u <= 1'b0; else if ((!alu_gie || dbgv)&&(wr_reg_ce)&&(wr_write_ucc)) // Either the supervisor or the debugger can clear this // bit. (Neither can set it) r_ill_err_u <=((ill_err_u)&&(wr_spreg_vl[CPU_ILL_BIT])); else if (alu_gie && alu_illegal && !clear_pipeline) // This flag is set if the CPU ever attempts to execute // an illegal instruction while in user mode. r_ill_err_u <= 1'b1; assign ill_err_u = r_ill_err_u; end else begin : NO_USER_ILL assign ill_err_u = 1'b0; end endgenerate // }}} // ibus_err_flag, ubus_err_flag : Bus error flags // {{{ // Supervisor/interrupt bus error flag -- this will crash the CPU if // ever set. initial ibus_err_flag = 1'b0; always @(posedge i_clk) if (i_reset) ibus_err_flag <= 1'b0; else if ((dbgv)&&(wr_write_scc)) ibus_err_flag <= (ibus_err_flag)&&(wr_spreg_vl[CPU_BUSERR_BIT]); else if ((i_bus_err)&&(!alu_gie)) ibus_err_flag <= 1'b1; // User bus error flag -- if ever set, it will cause an interrupt to // supervisor mode. generate if (OPT_USERMODE) begin : SET_USER_BUSERR reg r_ubus_err_flag; initial r_ubus_err_flag = 1'b0; always @(posedge i_clk) if ((i_reset)||(w_release_from_interrupt)) r_ubus_err_flag <= 1'b0; else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) r_ubus_err_flag <= (ubus_err_flag)&&(wr_spreg_vl[CPU_BUSERR_BIT]); else if ((i_bus_err)&&(alu_gie)) r_ubus_err_flag <= 1'b1; assign ubus_err_flag = r_ubus_err_flag; end else begin : NO_USER_BUSERR assign ubus_err_flag = 1'b0; end endgenerate // }}} // idiv_err_flag, udiv_err_flag : Divide by zero error flags // {{{ generate if (OPT_DIV != 0) begin : DIVERR // {{{ reg r_idiv_err_flag; // Supervisor/interrupt divide (by zero) error flag -- this will // crash the CPU if ever set. This bit is thus available for us // to be able to tell if/why the CPU crashed. initial r_idiv_err_flag = 1'b0; always @(posedge i_clk) if (i_reset) r_idiv_err_flag <= 1'b0; else if ((dbgv)&&(wr_write_scc)) r_idiv_err_flag <= (r_idiv_err_flag)&&(wr_spreg_vl[CPU_DIVERR_BIT]); else if ((div_error)&&(!alu_gie)) r_idiv_err_flag <= 1'b1; assign idiv_err_flag = r_idiv_err_flag; if (OPT_USERMODE) begin : USER_DIVERR reg r_udiv_err_flag; // User divide (by zero) error flag -- if ever set, it will // cause a sudden switch interrupt to supervisor mode. initial r_udiv_err_flag = 1'b0; always @(posedge i_clk) if ((i_reset)||(w_release_from_interrupt)) r_udiv_err_flag <= 1'b0; else if (((!alu_gie)||(dbgv))&&(wr_reg_ce) &&(wr_write_ucc)) r_udiv_err_flag <= (r_udiv_err_flag)&&(wr_spreg_vl[CPU_DIVERR_BIT]); else if ((div_error)&&(alu_gie)) r_udiv_err_flag <= 1'b1; assign udiv_err_flag = r_udiv_err_flag; end else begin : NO_USER_DIVERR assign udiv_err_flag = 1'b0; end // }}} end else begin : NO_DIVERR // {{{ assign idiv_err_flag = 1'b0; assign udiv_err_flag = 1'b0; // }}} end endgenerate // }}} // ifpu_err_flag, ufpu_err_flag : Floating point error flag(s) // {{{ generate if (IMPLEMENT_FPU !=0) begin : FPUERR // {{{ // Supervisor/interrupt floating point error flag -- this will // crash the CPU if ever set. reg r_ifpu_err_flag, r_ufpu_err_flag; initial r_ifpu_err_flag = 1'b0; always @(posedge i_clk) if (i_reset) r_ifpu_err_flag <= 1'b0; else if ((dbgv)&&(wr_write_scc)) r_ifpu_err_flag <= (r_ifpu_err_flag)&&(wr_spreg_vl[CPU_FPUERR_BIT]); else if ((fpu_error)&&(fpu_valid)&&(!alu_gie)) r_ifpu_err_flag <= 1'b1; // User floating point error flag -- if ever set, it will cause // a sudden switch interrupt to supervisor mode. initial r_ufpu_err_flag = 1'b0; always @(posedge i_clk) if ((i_reset)&&(w_release_from_interrupt)) r_ufpu_err_flag <= 1'b0; else if (((!alu_gie)||(dbgv))&&(wr_reg_ce) &&(wr_write_ucc)) r_ufpu_err_flag <= (r_ufpu_err_flag)&&(wr_spreg_vl[CPU_FPUERR_BIT]); else if ((fpu_error)&&(alu_gie)&&(fpu_valid)) r_ufpu_err_flag <= 1'b1; assign ifpu_err_flag = r_ifpu_err_flag; assign ufpu_err_flag = r_ufpu_err_flag; // }}} end else begin : NO_FPUERR // {{{ assign ifpu_err_flag = 1'b0; assign ufpu_err_flag = 1'b0; // }}} end endgenerate // }}} // ihalt_phase, uhalt_phase : If an instruction was broken when halting // {{{ generate if (OPT_CIS) begin : GEN_IHALT_PHASE reg r_ihalt_phase; initial r_ihalt_phase = 0; always @(posedge i_clk) if (i_reset) r_ihalt_phase <= 1'b0; else if ((!alu_gie)&&(alu_pc_valid)&&(!clear_pipeline)) r_ihalt_phase <= alu_phase; assign ihalt_phase = r_ihalt_phase; end else begin : GEN_IHALT_PHASE assign ihalt_phase = 1'b0; end endgenerate generate if ((!OPT_CIS) || (!OPT_USERMODE)) begin : GEN_UHALT_PHASE assign uhalt_phase = 1'b0; end else begin : GEN_UHALT_PHASE reg r_uhalt_phase; initial r_uhalt_phase = 1'b0; always @(posedge i_clk) if ((i_reset)||(w_release_from_interrupt)) r_uhalt_phase <= 1'b0; else if ((alu_gie)&&(alu_pc_valid)) r_uhalt_phase <= alu_phase; else if ((!alu_gie)&&(wr_reg_ce)&&(wr_write_pc) &&(wr_reg_id[4])) r_uhalt_phase <= wr_spreg_vl[1]; assign uhalt_phase = r_uhalt_phase; end endgenerate // }}} // ipc, upc: Program counters // {{{ // // Write backs to the PC register, and general increments of it // We support two: upc and ipc. If the instruction is normal, // we increment upc, if interrupt level we increment ipc. If // the instruction writes the PC, we write whichever PC is appropriate. // // Do we need to all our partial results from the pipeline? // What happens when the pipeline has gie and !gie instructions within // it? Do we clear both? What if a gie instruction tries to clear // a non-gie instruction? // upc generate if (OPT_USERMODE) begin : SET_USER_PC // {{{ reg [(AW+1):0] r_upc; always @(posedge i_clk) if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_pc)) r_upc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; else if ((alu_gie)&& (((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) ||(mem_pc_valid))) r_upc <= alu_pc; assign upc = r_upc; // }}} end else begin : NO_UPC // {{{ assign upc = {(AW+2){1'b0}}; // }}} end endgenerate // ipc // {{{ initial ipc = { RESET_BUS_ADDRESS, 2'b00 }; always @(posedge i_clk) if (i_reset) ipc <= { RESET_BUS_ADDRESS, 2'b00 }; else if ((wr_reg_ce)&&(!wr_reg_id[4])&&(wr_write_pc)) ipc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; else if ((!alu_gie)&&(!alu_phase)&& (((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) ||(mem_pc_valid))) ipc <= alu_pc; // }}} // }}} // pf_pc : the program counter used by the pre-fetch // {{{ // pfpcset, pfpcsrc // {{{ always @(*) begin pfpcset = 0; pfpcsrc = 0; if (i_reset) begin pfpcsrc = 0; pfpcset = 1; end else if ((dbgv)&&(wr_reg_ce)&&(wr_reg_id[4] == gie && wr_write_pc)) begin pfpcsrc = 1; // spreg pfpcset = 1; end else if ((w_switch_to_interrupt) ||((!gie)&&((o_clear_icache)||(dbg_clear_pipe)))) begin pfpcsrc = 2; // ipc pfpcset = 1; end else if ((w_release_from_interrupt)||((gie)&&((o_clear_icache)||(dbg_clear_pipe)))) begin pfpcsrc = 3; // upc pfpcset = 1; end else if ((wr_reg_ce)&&(wr_reg_id[4] == gie && wr_write_pc)) begin pfpcsrc = 1; // spreg pfpcset = 1; end else if ((dcd_early_branch_stb)&&(!clear_pipeline)) begin pfpcsrc = 4; // branch pfpcset = 1; end else if ((new_pc)||(o_pf_ready&& i_pf_valid)) begin pfpcsrc = 5; // PC increment pfpcset = 1; end end // }}} initial pf_pc = { RESET_BUS_ADDRESS, 2'b00 }; always @(posedge i_clk) if (pfpcset) case(pfpcsrc) 3'b000: pf_pc <= { RESET_BUS_ADDRESS, 2'b00 }; 3'b001: pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; 3'b010: pf_pc <= { ipc[(AW+1):2], 2'b00 }; 3'b011: pf_pc <= { upc[(AW+1):2], 2'b00 }; 3'b100: pf_pc <= { dcd_branch_pc[AW+1:2] + 1'b1, 2'b00 }; 3'b101: pf_pc <= { pf_pc[(AW+1):2] + 1'b1, 2'b00 }; default: pf_pc <= { RESET_BUS_ADDRESS, 2'b00 }; endcase /* if (i_reset) pf_pc <= { RESET_BUS_ADDRESS, 2'b00 }; else if ((dbg_clear_pipe)&&(wr_reg_ce)&&(wr_write_pc)) pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; else if ((w_switch_to_interrupt) ||((!gie)&&((o_clear_icache)||(dbg_clear_pipe)))) pf_pc <= { ipc[(AW+1):2], 2'b00 }; else if ((w_release_from_interrupt)||((gie)&&((o_clear_icache)||(dbg_clear_pipe)))) pf_pc <= { upc[(AW+1):2], 2'b00 }; else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; else if ((dcd_early_branch_stb)&&(!clear_pipeline)) pf_pc <= { dcd_branch_pc[AW+1:2] + 1'b1, 2'b00 }; else if ((new_pc)||((!pf_stalled)&&(i_pf_valid))) pf_pc <= { pf_pc[(AW+1):2] + 1'b1, 2'b00 }; */ // }}} initial last_write_to_cc = 1'b0; always @(posedge i_clk) if (i_reset) last_write_to_cc <= 1'b0; else last_write_to_cc <= (wr_reg_ce)&&(wr_write_cc); assign cc_write_hold = (wr_reg_ce && wr_write_cc)||(last_write_to_cc); // o_clear_icache // {{{ // If we aren't pipelined, or equivalently if we have no cache, these // instructions will get quietly (or not so quietly) ignored by the // optimizer. initial r_clear_icache = 1'b1; always @(posedge i_clk) if (i_reset) r_clear_icache <= 1'b0; else if (i_clear_cache && !o_dbg_stall) r_clear_icache <= 1'b1; else if ((wr_reg_ce)&&(wr_write_scc)) r_clear_icache <= wr_spreg_vl[CPU_CLRICACHE_BIT]; else r_clear_icache <= 1'b0; assign o_clear_icache = r_clear_icache; // }}} // o_clear_dcache // {{{ generate if (OPT_DCACHE) begin : CLEAR_DCACHE reg r_clear_dcache; initial r_clear_dcache = 1'b1; always @(posedge i_clk) if (i_reset) r_clear_dcache <= 1'b0; else if (i_clear_cache && !o_dbg_stall) r_clear_dcache <= 1'b1; else if ((wr_reg_ce)&&(wr_write_scc)) r_clear_dcache <= wr_spreg_vl[CPU_CLRDCACHE_BIT]; else r_clear_dcache <= 1'b0; assign o_clear_dcache = r_clear_dcache; end else begin : NOCLEAR_DCACHE assign o_clear_dcache = 1'b0; end endgenerate // }}} // new_pc : does the Prefetch need to clear the pipeline and start over? // {{{ initial new_pc = 1'b1; always @(posedge i_clk) if ((i_reset)||(o_clear_icache)||(dbg_clear_pipe)) new_pc <= 1'b1; else if (w_switch_to_interrupt) new_pc <= 1'b1; else if (w_release_from_interrupt) new_pc <= 1'b1; // else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) // Can't check for *this* PC here, since a user PC might be // loaded in the pipeline and hence rewritten. Thus, while // I hate to do it, we'll need to clear the pipeline on any // PC write else if ((wr_reg_ce)&&(alu_gie == wr_reg_id[4])&&(wr_write_pc)) new_pc <= 1'b1; else new_pc <= 1'b0; // }}} // // The debug write-back interface // {{{ // debug_pc // {{{ generate if (OPT_USERMODE) begin : DBGPC_FULL // {{{ always @(*) begin debug_pc = 0; if (!OPT_DBGPORT) begin // Empty block--if there's no debug port, we'll // leave this valu at zero to reduce power end else if (i_dbg_rreg[4]) // User mode debug_pc[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; else // Supervisor mode debug_pc[(AW+1):0] = { ipc[(AW+1):2], ihalt_phase, 1'b0 }; end // }}} end else begin : DBGPC_NO_USER // {{{ always @(*) begin debug_pc = 0; if (OPT_DBGPORT) debug_pc[(AW+1):0] = { ipc[AW+1:2], ihalt_phase, 1'b0 }; end // }}} end endgenerate // }}} // o_dbg_reg // {{{ generate if (!OPT_DBGPORT) begin : NO_DBGPORT assign o_dbg_reg = 0; // verilator lint_off UNUSED wire unused_dbgport; assign unused_dbgport = &{ 1'b0, i_dbg_rreg, debug_pc }; // verilator lint_on UNUSED end else if (OPT_USERMODE) begin : SETDBG // {{{ reg [31:0] pre_dbg_reg, r_dbg_reg; if (OPT_DISTRIBUTED_REGS) begin : GEN_DISTRIBUTED_RAM_DBG // {{{ always @(*) pre_dbg_reg = regset[i_dbg_rreg]; always @(posedge i_clk) begin r_dbg_reg <= pre_dbg_reg; if (i_dbg_rreg[3:0] == CPU_PC_REG) r_dbg_reg <= debug_pc; else if (i_dbg_rreg[3:0] == CPU_CC_REG) begin r_dbg_reg[15:0] <= (i_dbg_rreg[4]) ? w_uflags : w_iflags; r_dbg_reg[31:23] <= w_cpu_info; r_dbg_reg[CPU_GIE_BIT] <= i_dbg_rreg[4]; end end assign o_dbg_reg = r_dbg_reg; // }}} end else begin : GEN_BKRAM_DBG // {{{ reg [1:0] dbg_reg_sel; reg [31:0] pre_dbg_special; // First clock always @(posedge i_clk) begin dbg_reg_sel[1] <= (i_dbg_rreg[3:1] == 3'h7); dbg_reg_sel[0] <= i_dbg_rreg[0]; end always @(posedge i_clk) pre_dbg_reg <= regset[i_dbg_rreg]; always @(posedge i_clk) if (i_dbg_rreg[0]) pre_dbg_special <= debug_pc; else begin pre_dbg_special <= 0; pre_dbg_special[15:0] <= (i_dbg_rreg[4]) ? w_uflags : w_iflags; pre_dbg_special[31:23] <= w_cpu_info; pre_dbg_special[CPU_GIE_BIT] <= i_dbg_rreg[4]; end // Second clock always @(posedge i_clk) if (!dbg_reg_sel[1]) r_dbg_reg <= pre_dbg_reg; else if (dbg_reg_sel[0]) r_dbg_reg <= pre_dbg_special; else begin r_dbg_reg <= pre_dbg_special; r_dbg_reg[22:16] <= pre_dbg_reg[22:16]; end assign o_dbg_reg = r_dbg_reg; // }}} end // }}} end else begin : NO_USER_SETDBG // {{{ reg [31:0] r_dbg_reg, pre_dbg_reg; if (OPT_DISTRIBUTED_REGS) begin : GEN_DISTRIBUTED_RAM_DBG always @(*) pre_dbg_reg = regset[i_dbg_rreg[3:0]]; always @(posedge i_clk) begin r_dbg_reg <= pre_dbg_reg; if (i_dbg_rreg[3:0] == CPU_PC_REG) r_dbg_reg <= debug_pc; else if (i_dbg_rreg[3:0] == CPU_CC_REG) begin r_dbg_reg[15:0] <= w_iflags; r_dbg_reg[31:23] <= w_cpu_info; r_dbg_reg[CPU_GIE_BIT] <= 1'b0; end end end else begin : GEN_BKRAM_DBG // {{{ reg [1:0] dbg_reg_sel; reg [31:0] pre_dbg_special; // First clock always @(posedge i_clk) begin dbg_reg_sel[1] <= (i_dbg_rreg[3:1] == 3'h7); dbg_reg_sel[0] <= i_dbg_rreg[0]; end always @(posedge i_clk) pre_dbg_reg <= regset[i_dbg_rreg[3:0]]; always @(posedge i_clk) if (i_dbg_rreg[0]) pre_dbg_special <= debug_pc; else begin pre_dbg_special <= 0; pre_dbg_special[15:0] <= w_iflags; pre_dbg_special[31:23] <= w_cpu_info; pre_dbg_special[CPU_GIE_BIT] <= 1'b0; end // Second clock always @(posedge i_clk) if (!dbg_reg_sel[1]) r_dbg_reg <= pre_dbg_reg; else if (dbg_reg_sel[0]) r_dbg_reg <= pre_dbg_special; else begin r_dbg_reg <= pre_dbg_special; r_dbg_reg[22:16] <= pre_dbg_reg[22:16]; end assign o_dbg_reg = r_dbg_reg; // }}} end assign o_dbg_reg = r_dbg_reg; // }}} end endgenerate // }}} always @(posedge i_clk) o_dbg_cc <= { i_bus_err, gie, sleep }; // r_halted // {{{ generate if (OPT_PIPELINED) begin : GEN_HALT_PIPELINED // {{{ initial r_halted = OPT_START_HALTED; always @(posedge i_clk) if (i_reset) r_halted <= OPT_START_HALTED; else if (!i_halt) r_halted <= 1'b0; else if (r_halted) r_halted <= 1'b1; else r_halted <= (!alu_phase)&&(!o_bus_lock)&&( // To be halted, any long lasting instruction // must be completed. (i_pf_valid)&&(!i_mem_busy)&&(!alu_busy) &&(!div_busy)&&(!fpu_busy) // Operations must either be valid, or illegal &&((dcd_valid)||(dcd_illegal))); // }}} end else begin : GEN_HALT_NOPIPE // {{{ initial r_halted = OPT_START_HALTED; always @(posedge i_clk) if (i_reset) r_halted <= OPT_START_HALTED; else if (!i_halt) r_halted <= 1'b0; else if (r_halted) r_halted <= 1'b1; else r_halted <= (!alu_phase) // To be halted, any long lasting instruction // must be completed. &&(i_pf_valid)&&(!i_mem_busy)&&(!alu_busy) &&(!div_busy)&&(!fpu_busy); // }}} end endgenerate // }}} // o_dbg_stall // {{{ initial r_dbg_stall = 1'b1; always @(posedge i_clk) if (i_reset) r_dbg_stall <= 1'b1; else if (!r_halted || (wr_reg_ce && wr_reg_id[3:1] == 3'h7)) r_dbg_stall <= 1'b1; else r_dbg_stall <= (OPT_DBGPORT && i_dbg_we && !o_dbg_stall); assign o_dbg_stall = OPT_DBGPORT && (!r_halted || r_dbg_stall); // }}} // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Accounting outputs // {{{ // // // Produce accounting outputs: Account for any CPU stalls, so we can // later evaluate how well we are doing. // // assign o_op_stall = (master_ce)&&(op_stall); assign o_pf_stall = (master_ce)&&(!i_pf_valid); assign o_i_count = (alu_pc_valid)&&(!clear_pipeline); // }}} //////////////////////////////////////////////////////////////////////// // // The debug scope output // {{{ //////////////////////////////////////////////////////////////////////// // // generate if (OPT_TRACE_PORT) begin : GEN_DEBUG_PORT localparam [1:0] DBGSRC_FLAGS = 2'b00, DBGSRC_WRITEBACK = 2'b01, DBGSRC_JUMP = 2'b10; reg [31:0] r_debug; reg debug_trigger, dbg_mem_we; wire [27:0] debug_flags; reg [1:0] dbgsrc; // Verilator lint_off UNUSED wire [27:0] dbg_pc, dbg_wb_addr; // Verilator lint_on UNUSED initial debug_trigger = 1'b0; always @(posedge i_clk) debug_trigger <= (!i_halt)&&(o_break); always @(posedge i_clk) if (o_mem_ce) dbg_mem_we <= o_mem_op[0]; assign debug_flags = { master_ce, i_halt, o_break, sleep, gie, ibus_err_flag, trap, ill_err_i, o_clear_icache, i_pf_valid, i_pf_illegal, dcd_ce, dcd_valid, dcd_stalled, op_ce, op_valid, op_pipe, alu_ce, alu_busy, alu_wR, alu_illegal, alu_wF, mem_ce, dbg_mem_we, i_mem_busy, i_mem_pipe_stalled, (new_pc), (dcd_early_branch) }; if (AW-1 < 27) begin : GEN_SHORT_DBGPC assign dbg_pc[(AW-1):0] = pf_pc[(AW+1):2]; assign dbg_pc[27:AW] = 0; assign dbg_wb_addr[(AW-1):0] = 0; assign dbg_wb_addr[27:AW] = 0; end else // if (AW-1 >= 27) begin : GEN_WIDE_DBGPC assign dbg_pc[27:0] = pf_pc[29:2]; assign dbg_wb_addr = 0; end always @(posedge i_clk) begin dbgsrc <= 0; if ((i_halt)||(!master_ce)||(debug_trigger)||(o_break)) dbgsrc <= DBGSRC_FLAGS; else if ((i_mem_valid)||((!clear_pipeline)&&(!alu_illegal) &&(((alu_wR)&&(alu_valid)) ||(div_valid)||(fpu_valid)))) dbgsrc <= DBGSRC_WRITEBACK; else if (clear_pipeline) dbgsrc <= DBGSRC_JUMP; else dbgsrc <= DBGSRC_FLAGS; end always @(posedge i_clk) casez(dbgsrc) DBGSRC_FLAGS: r_debug <= { debug_trigger, 3'b101, debug_flags }; DBGSRC_WRITEBACK: r_debug <= { debug_trigger, 1'b0, wr_reg_id[3:0], wr_gpreg_vl[25:0]}; DBGSRC_JUMP: r_debug <= { debug_trigger, 3'b100, dbg_pc }; default: r_debug <= 32'h0; endcase assign o_debug = r_debug; end else begin : NO_TRACE_PORT assign o_debug = 32'h0; end endgenerate // }}} //////////////////////////////////////////////////////////////////////// // // (Optional) Hardware profiler support // {{{ //////////////////////////////////////////////////////////////////////// // // generate if (OPT_PROFILER) begin : GEN_PROFILER reg prof_stb; reg [AW+1:0] prof_addr; reg [31:0] prof_ticks; initial prof_stb = 1'b0; always @(posedge i_clk) if (i_reset || clear_pipeline) prof_stb <= 1'b0; else prof_stb <= (alu_pc_valid || mem_pc_valid); initial prof_addr = 0; always @(posedge i_clk) if (i_reset || clear_pipeline) prof_addr <= RESET_ADDRESS[AW+1:0]; else if (alu_pc_valid || mem_pc_valid) prof_addr <= alu_pc; initial prof_ticks = 0; always @(posedge i_clk) if (i_reset) prof_ticks <= 0; else if (!i_halt) prof_ticks <= prof_ticks + 1; assign o_prof_stb = prof_stb; assign o_prof_addr = prof_addr; assign o_prof_ticks = prof_ticks; end else begin : NO_PROFILER assign o_prof_stb = 1'b0; assign o_prof_addr = 0; assign o_prof_ticks = 0; end endgenerate // }}} //////////////////////////////////////////////////////////////////////// // // (Optional) Verilator $display simulation dumping support // {{{ //////////////////////////////////////////////////////////////////////// // // // The following is somewhat useful for debugging under Icarus Verilog. // In that case, the VCD file can tell you what's going on at any // specific moment, but it doesn't put the register set into the VCD // file, nor does it give you any insight into the current contents of // the memory, or how they got that way. The following is intended to // be a first step towards that end. // // As currently envisioned, we track values written to the registers, // and values read and written to memory. The program counter offered // is typically off by 4 from the actual instruction address being // completed. It may be off by more in the case of memory instructions. // I haven't (yet) tried to make the PC given match the instruction // address--so that may be an item to do later. // // You can activate (or de-activate) these instructions via the // OPT_SIM_DEBUG flag below. If set to one, the simulation debuggin // statements will be output as the CPU executes. localparam OPT_SIM_DEBUG = 1'b0; generate if (OPT_SIM_DEBUG) begin : GEN_SIM_DEBUG reg [31:0] nstime; initial nstime = 0; always @(posedge i_clk) nstime <= nstime + 10; always @(posedge i_clk) if (!i_reset && o_mem_ce) begin if (o_mem_reg[4] && !o_mem_op[0]) begin case(o_mem_op[2:1]) // 3'b000: // 3'b001: 2'b01: $display("MEM: %8d LW uR%1d <- @%08x", nstime, o_mem_reg[3:0], o_mem_addr); 2'b10: $display("MEM: %8d LH uR%1d <- @%08x", nstime, o_mem_reg[3:0], o_mem_addr); 2'b11: $display("MEM: %8d LB uR%1d <- @%08x", nstime, o_mem_reg[3:0], o_mem_addr); default: $display("MEM: %8d Unknown MEM op: %d\n", nstime, o_mem_op); endcase end else case(o_mem_op[2:0]) // 3'b000: // 3'b001: 3'b010: $display("MEM: %8d LW sR%1d <- @%08x", nstime, o_mem_reg, o_mem_addr); 3'b011: $display("MEM: %8d SW 0x%08x -> @%08x", nstime, o_mem_data, o_mem_addr); 3'b100: $display("MEM: %8d LH sR%1d <- @%08x", nstime, o_mem_reg, o_mem_addr); 3'b101: $display("MEM: %8d SH 0x%08x -> @%04x", nstime, o_mem_data[15:0], o_mem_addr); 3'b110: $display("MEM: %8d LB sR%1d <- @%08x", nstime, o_mem_reg, o_mem_addr); 3'b111: $display("MEM: %8d SB 0x%08x -> @%02x", nstime, o_mem_data[7:0], o_mem_addr); default: $display("MEM: %8d Unknown MEM op: %d\n", nstime, o_mem_op); endcase end always @(posedge i_clk) if (!i_reset && i_bus_err) begin $display("MEM: %8d BUS ERROR!!", nstime); end always @(posedge i_clk) if (!i_reset && wr_reg_ce) begin if (i_mem_valid) begin if (i_mem_wreg[4]) $display("MEM: %8d Load 0x%08x -> uR%1d", nstime, i_mem_result, i_mem_wreg[3:0]); else $display("MEM: %8d Load 0x%08x -> sR%1d", nstime, i_mem_result, i_mem_wreg[3:0]); end if (wr_reg_id[4] && OPT_USERMODE) begin if (wr_reg_id[3:0] == CPU_PC_REG) $display("REG: %8d uPC <- 0x%08x [0x%08x]", nstime, wr_spreg_vl, (gie) ? upc : ipc); else if (wr_reg_id[3:0] == CPU_CC_REG) $display("REG: %8d uCC <- 0x%08x [0x%08x]", nstime, wr_spreg_vl, (gie) ? upc : ipc); else if (wr_reg_id == 4'hd) $display("REG: %8d uSP <- 0x%08x [0x%08x]", nstime, wr_gpreg_vl, (gie) ? upc : ipc); else $display("REG: %8d uR%1x <- 0x%08x [0x%08x]", nstime, wr_reg_id[3:0], wr_gpreg_vl, (gie) ? upc : ipc); end else begin if (wr_reg_id[3:0] == CPU_PC_REG) $display("REG: %8d sPC <- 0x%08x [0x%08x]", nstime, wr_spreg_vl, ipc); else if (wr_reg_id[3:0] == CPU_CC_REG) $display("REG: %8d sCC <- 0x%08x [0x%08x]", nstime, wr_spreg_vl, ipc); else if (wr_reg_id[3:0] == 4'hd) $display("REG: %8d sSP <- 0x%08x [0x%08x]", nstime, wr_gpreg_vl, ipc); else $display("REG: %8d sR%1x <- 0x%08x [0x%08x]", nstime, wr_reg_id[3:0], wr_gpreg_vl, ipc); end end end endgenerate // }}} // Make verilator happy // {{{ // verilator coverage_off // verilator lint_off UNUSED wire unused; assign unused = &{ 1'b0, fpu_ce, wr_spreg_vl[1:0], ipc[1:0], upc[1:0], pf_pc[1:0], dcd_rA, dcd_pipe, dcd_zI, dcd_A_stall, dcd_B_stall, dcd_F_stall, op_Rcc, op_pipe, op_lock, i_mem_pipe_stalled, prelock_stall, dcd_F, w_clken }; generate if (AW+2 < 32) begin : UNUSED_AW wire generic_ignore; assign generic_ignore = &{ 1'b0, wr_spreg_vl[31:(AW+2)] }; end if (!OPT_USERMODE) begin : UNUSED_USERMODE wire unused_usermode; assign unused_usermode = &{ 1'b0, alu_reg[4], i_mem_wreg[4], i_dbg_rreg[4] }; end endgenerate // verilator lint_on UNUSED // verilator coverage_on // }}} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // Formal properties // {{{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// `ifdef FORMAL // Declarations // {{{ `ifdef ZIPCPU `define ASSUME assume `else `define ASSUME assert `endif `define ASSERT assert // // wire [1+1+4+15+6+4+13+AW+1+32+4+23-1:0] f_dcd_data; wire fc_op_prepipe; wire [6:0] fc_alu_Aid; wire fc_alu_wR, fc_alu_M, fc_alu_prepipe; reg f_alu_phase; reg f_past_valid; reg [2:0] f_dbg_pc_seq, f_dbg_cc_seq, f_dbg_reg_seq; wire fc_op_illegal, fc_op_wF, fc_op_ALU, fc_op_M, fc_op_DV, fc_op_FP, fc_op_break, fc_op_lock, fc_op_wR, fc_op_rA, fc_op_rB, fc_op_sim; wire [6:0] fc_op_Rid, fc_op_Aid, fc_op_Bid; wire [31:0] fc_op_I; wire [3:0] fc_op_cond; wire [3:0] fc_op_op; wire [22:0] fc_op_sim_immv; wire f_op_insn; //f_alu_insn,f_wb_insn reg f_op_phase, f_op_early_branch; reg f_op_zI; reg f_op_branch; wire [31:0] f_Bv; reg [31:0] f_Av, f_pre_Bv; reg f_alu_branch; wire [31:0] f_dcd_mem_addr; wire [AW-1:0] f_next_mem, f_op_mem_addr; wire [4+AW+2+7+4-1:0] f_op_data; wire fc_alu_illegal, fc_alu_wF, fc_alu_ALU, fc_alu_DV, fc_alu_FP, fc_alu_break, fc_alu_lock, fc_alu_rA, fc_alu_rB, fc_alu_sim; wire [6:0] fc_alu_Rid, fc_alu_Bid; wire [31:0] fc_alu_I; wire [3:0] fc_alu_cond; wire [3:0] fc_alu_op; wire [22:0] fc_alu_sim_immv; wire [F_LGDEPTH-1:0] f_mem_outstanding; wire f_mem_gie, f_mem_pc, f_read_cycle, f_exwrite_cycle; wire [4:0] f_last_reg, f_addr_reg; initial f_past_valid = 1'b0; always @(posedge i_clk) f_past_valid <= 1'b1; `ifndef VERIFIC initial assume(i_reset); `endif always @(posedge i_clk) if (!f_past_valid) assume(i_reset); // }}} //////////////////////////////////////////////////////////////////////// // // The debugging interface // {{{ //////////////////////////////////////////////////////////////////////// // // fdebug #( .OPT_START_HALTED(OPT_START_HALTED), .OPT_DISTRIBUTED_RAM(OPT_DISTRIBUTED_REGS) ) fdbg ( // {{{ .i_clk(i_clk), .i_reset(i_reset), .i_cpu_reset(i_reset), .i_halt(i_halt), .i_halted(r_halted), .i_clear_cache(i_clear_cache), .i_dbg_we(i_dbg_we), .i_dbg_reg(i_dbg_wreg), .i_dbg_data(i_dbg_data), .i_dbg_stall(o_dbg_stall), .i_dbg_break(o_break), .i_dbg_cc(o_dbg_cc) // , .i_dbg_rreg(i_dbg_rreg), // .i_dbg_rdata(o_dbg_rdata), // }}} ); always @(*) if (i_halt && r_halted) begin `ASSERT(!alu_ce); `ASSERT(!alu_phase); `ASSERT(!div_ce); `ASSERT(!o_mem_ce); `ASSERT(f_mem_outstanding == 0); end initial f_dbg_pc_seq = 0; always @(posedge i_clk) if (i_reset) f_dbg_pc_seq <= 0; else begin f_dbg_pc_seq[0] <= i_dbg_we && !o_dbg_stall && (i_dbg_wreg == { gie, CPU_PC_REG }); f_dbg_pc_seq[2:1] <= f_dbg_pc_seq[1:0]; end always @(posedge i_clk) begin if (f_dbg_pc_seq[0]) begin `ASSERT(dbgv && alu_reg == { gie, CPU_PC_REG }); end if (f_dbg_pc_seq[1]) begin `ASSERT(clear_pipeline); `ASSERT(o_pf_request_address == $past({ i_dbg_data[31:2], 2'b00 },2)); end end initial f_dbg_cc_seq = 0; always @(posedge i_clk) if (i_reset) f_dbg_cc_seq <= 0; else begin f_dbg_cc_seq[0] <= i_dbg_we && !o_dbg_stall && (i_dbg_wreg == { gie, CPU_CC_REG }); f_dbg_cc_seq[2:1] <= f_dbg_cc_seq[1:0]; end always @(posedge i_clk) if (f_dbg_cc_seq[0]) begin `ASSERT(wr_reg_ce); `ASSERT(wr_reg_id == $past(i_dbg_wreg)); `ASSERT(wr_spreg_vl == $past(i_dbg_data)); end initial f_dbg_reg_seq = 0; always @(posedge i_clk) if (i_reset) f_dbg_reg_seq <= 0; else begin f_dbg_reg_seq[0] <= i_dbg_we && !o_dbg_stall && (i_dbg_rreg[3:1] != 3'h7 ); f_dbg_reg_seq[2:1] <= f_dbg_reg_seq[1:0]; end always @(posedge i_clk) begin if (f_dbg_reg_seq[0] && !i_reset) begin `ASSERT(dbgv && alu_reg == $past(i_dbg_wreg)); `ASSERT($past(i_dbg_rreg[3:1]) != 3'h7); `ASSERT(dbg_val == $past(i_dbg_data)); `ASSERT(wr_reg_ce); `ASSERT(wr_gpreg_vl == $past(i_dbg_data)); `ASSERT(wr_reg_id == $past(i_dbg_wreg)); end // if (f_dbg_reg_seq[1]) // begin // end end // }}} //////////////////////////////////////////////////////////////////////// // // Reset checks // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if ((!f_past_valid)||($past(i_reset))) begin // Initial assertions `ASSERT(!i_pf_valid); `ASSERT(!dcd_phase); `ASSERT(!op_phase); `ASSERT(!alu_phase); // `ASSERT(!i_pf_valid); `ASSERT(!dcd_valid); `ASSERT(!op_valid); `ASSERT(!op_valid_mem); `ASSERT(!op_valid_div); `ASSERT(!op_valid_alu); `ASSERT(!op_valid_fpu); // `ASSERT(!alu_valid); `ASSERT(!alu_busy); // `ASSERT(!i_mem_valid); `ASSERT(!i_mem_rdbusy); `ASSERT(!i_bus_err); // `ASSERT(!div_valid); `ASSERT(!div_busy); `ASSERT(!div_error); // `ASSERT(!fpu_valid); `ASSERT(!fpu_busy); `ASSERT(!fpu_error); // `ASSERT(!ill_err_i); `ASSERT(!ill_err_u); `ASSERT(!idiv_err_flag); `ASSERT(!udiv_err_flag); `ASSERT(!ibus_err_flag); `ASSERT(!ubus_err_flag); `ASSERT(!ifpu_err_flag); `ASSERT(!ufpu_err_flag); `ASSERT(!ihalt_phase); `ASSERT(!uhalt_phase); end always @(*) begin if (i_pf_valid) `ASSERT(f_past_valid); if (dcd_valid) `ASSERT(f_past_valid); if (alu_pc_valid) `ASSERT(f_past_valid); if (i_mem_valid) `ASSERT(f_past_valid); if (div_valid) `ASSERT(f_past_valid); if (fpu_valid) `ASSERT(f_past_valid); if (w_op_valid) `ASSERT(f_past_valid); // if (i_mem_busy) `ASSERT(f_past_valid); if (i_mem_rdbusy) `ASSERT(f_past_valid); if (div_busy) `ASSERT(f_past_valid); if (fpu_busy) `ASSERT(f_past_valid); end // }}} //////////////////////////////////////////////////////////////////////// // // Pipeline signaling check // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if (clear_pipeline) begin // `ASSERT(!alu_ce); `ASSERT(!mem_ce); end always @(posedge i_clk) if ((f_past_valid)&&($past(clear_pipeline))) begin `ASSERT(!alu_busy); `ASSERT(!div_busy); `ASSERT(!i_mem_rdbusy); `ASSERT(!fpu_busy); // `ASSERT(!alu_valid); `ASSERT(!div_valid); `ASSERT(!fpu_valid); end always @(*) if (dcd_ce) `ASSERT((op_ce)||(!dcd_valid)); always @(*) if ((op_ce)&&(!clear_pipeline)) `ASSERT((adf_ce_unconditional)||(mem_ce)||(!op_valid)); // // Make sure the dcd stage is never permanently stalled always @(posedge i_clk) if ((f_past_valid)&&(!$past(alu_wR))&&(!$past(alu_wF)) &&($past(f_past_valid,2))&&(!$past(alu_wR,2))&&(!$past(alu_wF)) &&(!op_valid)&&(master_ce) &&(!clear_pipeline)&&(!i_reset) &&(!div_busy)&&(!div_valid) &&(!i_mem_busy)&&(!i_mem_valid)&&(!i_bus_err) &&(!alu_busy)&&(!alu_pc_valid)&&(!alu_valid) &&(!fpu_busy)&&(!fpu_valid)&&(!fpu_error) &&(!op_break)&&(!o_break) &&(!w_switch_to_interrupt) &&(!ibus_err_flag)&&(!ill_err_i)&&(!idiv_err_flag)) begin if (OPT_PIPELINED) `ASSERT(dcd_ce || cc_invalid_for_dcd || i_halt); if (!dcd_valid) `ASSERT(dcd_ce); end always @(posedge i_clk) if (OPT_PIPELINED && !i_halt && !i_reset && (wr_flags_ce || (wr_reg_ce && wr_reg_id == { op_gie, CPU_CC_REG }))) `ASSERT(cc_invalid_for_dcd); always @(posedge i_clk) if (!f_past_valid || !OPT_PIPELINED) begin `ASSERT(!cc_invalid_for_dcd); end else if (alu_busy) begin `ASSERT(cc_invalid_for_dcd == (alu_wF || alu_reg == { gie, CPU_CC_REG })); end else if (i_mem_rdbusy || i_bus_err) begin `ASSERT(i_bus_err || f_exwrite_cycle || cc_invalid_for_dcd == (alu_reg == { gie, CPU_CC_REG })); end else if (!clear_pipeline && cc_invalid_for_dcd) begin `ASSERT(alu_illegal || wr_flags_ce || ((i_mem_valid || i_bus_err) && ($past(i_mem_rdbusy && f_last_reg == { gie, CPU_CC_REG }))) || (wr_flags_ce || (wr_reg_ce && wr_reg_id == { op_gie, CPU_CC_REG })) || ($past(wr_flags_ce) || $past(wr_reg_ce && wr_reg_id == { op_gie, CPU_CC_REG }))); end // // Make sure the ops stage is never permanently stalled always @(*) if ((op_valid)&&(master_ce)&&(!clear_pipeline)&&(!i_reset) &&(!div_busy)&&(!div_valid) &&(!i_mem_busy)&&(!i_mem_valid)&&(!i_bus_err) &&(!alu_busy)&&(!alu_pc_valid) &&(!fpu_busy)&&(!fpu_valid)&&(!fpu_error) &&(!op_break)&&(!o_break) &&(!w_switch_to_interrupt) &&(!alu_illegal) && (!prelock_stall) &&(!step || !stepped) &&(!ibus_err_flag)&&(!ill_err_i)&&(!idiv_err_flag)) `ASSERT(adf_ce_unconditional | mem_ce); // always @(posedge i_clk) // if (f_past_valid&& $past(op_valid && dcd_valid && i_pf_valid // && !op_ce)) // `ASSERT(!prelock_stall); // // Make sure that, following an op_ce && op_valid, op_valid is only // true if dcd_valid was as well always @(posedge i_clk) if ((f_past_valid)&&($past(op_ce && op_valid && !dcd_valid))) begin if ($past(dcd_early_branch)) begin `ASSERT(!dcd_early_branch); end else `ASSERT(!op_valid); end // // Same for the next step always @(posedge i_clk) if ((f_past_valid)&&($past(op_valid && (mem_ce ||adf_ce_unconditional))) &&(!$past(dcd_valid))) begin if ($past(dcd_early_branch)) begin `ASSERT(!dcd_early_branch); end else `ASSERT(!op_valid); end // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the Program counter // {{{ //////////////////////////////////////////////////////////////////////// // // always @(*) if (i_pf_valid) `ASSERT(i_pf_instruction_pc[1:0]==2'b00); always @(*) if ((dcd_valid)&&(!dcd_illegal)) `ASSERT((!dcd_pc[1])||(dcd_phase)); always @(*) `ASSERT(!op_pc[0]); always @(*) `ASSERT(!alu_pc[0]); // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the prefetch (output) stage // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if ((!clear_pipeline)&&(i_pf_valid)) `ASSERT(pf_gie == gie); always @(*) if ((i_pf_valid)&&(!clear_pipeline)) `ASSERT(pf_gie == gie); ffetch #(.ADDRESS_WIDTH(ADDRESS_WIDTH), .OPT_CONTRACT(1'b0), .OPT_ALIGNED(1'b1)) chkifetch( .i_clk(i_clk), .i_reset(i_reset), .cpu_new_pc(o_pf_new_pc), .cpu_clear_cache(o_clear_icache), .cpu_pc(o_pf_request_address), .pf_valid(i_pf_valid), .cpu_ready(o_pf_ready), .pf_pc(i_pf_instruction_pc), .pf_insn(i_pf_instruction), .pf_illegal(i_pf_illegal)); // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the decode stage // {{{ //////////////////////////////////////////////////////////////////////// // // assign f_dcd_data = { dcd_phase, dcd_opn, dcd_A, dcd_B, dcd_R, // 4+15 dcd_Acc, dcd_Bcc, dcd_Apc, dcd_Bpc, dcd_Rcc, dcd_Rpc,//6 dcd_F, // 4 dcd_wR, dcd_rA, dcd_rB, dcd_ALU, dcd_M, dcd_DIV, dcd_FP, dcd_wF, dcd_gie, dcd_break, dcd_lock, dcd_pipe, dcd_ljmp, dcd_pc, // AW+1 dcd_I, // 32 dcd_zI, // true if dcd_I == 0 dcd_illegal, dcd_early_branch, dcd_sim, dcd_sim_immv }; always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&(!$past(clear_pipeline)) &&(!$past(o_clear_icache)) &&($past(dcd_valid))&&($past(dcd_stalled)) &&(!clear_pipeline)) begin `ASSERT((!OPT_PIPELINED)||(dcd_valid)); `ASSERT((!dcd_valid && OPT_LOWPOWER) || $stable(f_dcd_data)); `ASSERT((!dcd_valid && OPT_LOWPOWER) || $stable(f_dcd_insn_word)); end always @(*) if ((dcd_valid || dcd_phase)&&(!clear_pipeline)) `ASSERT(f_dcd_insn_gie == dcd_gie); always @(posedge i_clk) if ((dcd_valid)&&(!dcd_illegal)&&(!clear_pipeline)) begin `ASSERT(dcd_gie == gie); if ((gie)||(dcd_phase)) begin `ASSERT((!dcd_wR)||(dcd_R[4]==dcd_gie)); `ASSERT((!dcd_rA)||(dcd_A[4]==dcd_gie)); `ASSERT((!dcd_rB)||(dcd_B[4]==dcd_gie)); end else if ((!dcd_early_branch)&&((dcd_M) ||(dcd_DIV)||(dcd_FP)||(!dcd_wR))) `ASSERT(!dcd_gie); if ((dcd_ALU)&&(dcd_opn==CPU_MOV_OP)) begin `ASSERT(((!dcd_rA)&&(dcd_wR)) ||((!dcd_rA)&&(!dcd_rB)&&(!dcd_wR))); end else if (dcd_ALU) `ASSERT( (gie == dcd_R[4]) &&(gie == dcd_A[4]) &&((!dcd_rB)||(gie == dcd_B[4])) &&(dcd_gie == gie)); end always @(*) if ((op_valid)&&(op_rA)&&(op_Aid[3:1] == 3'h7)&&(!clear_pipeline) &&(op_Aid[4:0] != { gie, 4'hf})) `ASSERT(!pending_sreg_write); always @(*) if ((op_valid)&&(op_rB)&&(op_Bid[3:1] == 3'h7)&&(!clear_pipeline) &&(op_Bid[4:0] != { gie, 4'hf})) `ASSERT(!pending_sreg_write); always @(*) if ((dcd_valid)&&(!clear_pipeline)) `ASSERT(dcd_gie == gie); // // // Piped Memory assertions // // always @(*) if ((dcd_valid)&&(dcd_M)&&(dcd_pipe)&&(!dcd_illegal)&&(!alu_illegal) &&(!break_pending)&&(!clear_pipeline)) begin if (op_valid_mem) begin `ASSERT(op_opn[0] == dcd_opn[0]); `ASSERT((!dcd_rB) ||(op_Bid[4:0] == dcd_B[4:0])); `ASSERT(op_rB == dcd_rB); end `ASSERT(dcd_B[4] == dcd_gie); end always @(*) if (op_valid_mem && op_pipe && i_mem_busy) `ASSERT(f_read_cycle == !op_opn[0]); always @(*) if ((dcd_valid)&&(!dcd_M)) `ASSERT((dcd_illegal)||(!dcd_pipe)); assign f_dcd_mem_addr = w_op_BnI+dcd_I; always @(posedge i_clk) if ((f_past_valid)&&($past(dcd_early_branch))&&(!dcd_early_branch) &&(dcd_valid)) `ASSERT(!dcd_pipe); always @(*) if ((dcd_valid)&&(dcd_early_branch)) `ASSERT(!dcd_M); always @(*) if ((dcd_valid)&&(!dcd_illegal)&&(!fc_op_prepipe)) `ASSERT(!dcd_pipe); always @(*) if ((dcd_valid)&&(dcd_pipe)&&(w_op_valid)) begin // `ASSERT((dcd_A[3:1] != 3'h7)||(dcd_opn[0])); `ASSERT(dcd_B[3:1] != 3'h7); `ASSERT(dcd_rB); `ASSERT(dcd_M); `ASSERT(dcd_B == op_Bid); if (op_valid) `ASSERT((op_valid_mem)||(op_illegal)); if (op_valid_mem) begin `ASSERT((dcd_I[AW+1:3] == 0) ||(!alu_busy)||(!div_busy) ||(!alu_wR)||(alu_reg != dcd_B)); `ASSERT((!op_wR)||(op_Aid != op_Bid)); end end // // Decode option processing // // OPT_CIS ... the compressed instruction set always @(*) if ((!OPT_CIS)&&(dcd_valid)) begin `ASSERT(!dcd_phase); `ASSERT(dcd_pc[1:0] == 2'b0); end always @(*) if ((dcd_valid)&&(dcd_phase)) `ASSERT(f_dcd_insn_word[31]); // OPT_EARLY_BRANCHING always @(*) if (!OPT_EARLY_BRANCHING) `ASSERT((!dcd_early_branch) &&(!dcd_early_branch_stb) &&(!dcd_ljmp)); // OPT_DIV always @(*) if ((dcd_DIV)&&(dcd_valid)&&(!dcd_illegal)) `ASSERT(dcd_wR); // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the op stage // {{{ //////////////////////////////////////////////////////////////////////// // // assign f_op_data = { op_valid_mem, op_valid_alu, op_valid_div, op_valid_fpu, // The Av and Bv values can change while we are stalled in the // op stage--that's why we are stalled there // r_op_Av, r_op_Bv, // 32 ea op_pc[AW+1:2], // AW op_wR, op_wF, r_op_F, // 7 op_illegal, op_break, op_lock, op_pipe }; always @(posedge i_clk) if ((f_past_valid)&&($past(op_valid))&&(!$past(i_reset)) &&(!$past(clear_pipeline))) begin if (($past(op_valid_mem))&&($past(mem_stalled))) `ASSERT($stable(f_op_data[AW+16:1])&&(!$rose(op_pipe))); if (($past(op_valid_div))&&($past(div_busy))) `ASSERT($stable(f_op_data)); end f_idecode #( // {{{ .OPT_MPY((OPT_MPY!=0)? 1'b1:1'b0), .OPT_SHIFTS(OPT_SHIFTS), .OPT_DIVIDE(OPT_DIV), .OPT_FPU(IMPLEMENT_FPU), .OPT_LOCK(OPT_LOCK), .OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), .OPT_SIM(1'b0), .OPT_USERMODE(OPT_USERMODE), .OPT_LOWPOWER(OPT_LOWPOWER), .OPT_CIS(OPT_CIS) // }}} ) f_insn_decode_op( // {{{ f_op_insn_word, f_op_phase, op_gie, fc_op_illegal, fc_op_Rid, fc_op_Aid, fc_op_Bid, fc_op_I, fc_op_cond, fc_op_wF, fc_op_op, fc_op_ALU, fc_op_M, fc_op_DV, fc_op_FP, fc_op_break, fc_op_lock, fc_op_wR, fc_op_rA, fc_op_rB, fc_op_prepipe, fc_op_sim, fc_op_sim_immv // }}} ); initial f_op_early_branch = 1'b0; always @(posedge i_clk) if (op_ce && (!OPT_MEMPIPE || dcd_valid || dcd_illegal || dcd_early_branch)) begin f_op_insn_word <= f_dcd_insn_word; f_op_phase <= dcd_phase; f_op_early_branch <= dcd_early_branch; f_op_zI <= dcd_zI; end initial f_op_branch = 1'b0; always @(posedge i_clk) if ((i_reset)||(clear_pipeline)) f_op_branch <= 1'b0; else if (op_ce) f_op_branch <= (dcd_early_branch)||dcd_ljmp; else if ((adf_ce_unconditional)||(mem_ce)) f_op_branch <= 1'b0; always @(*) if (!OPT_EARLY_BRANCHING) begin `ASSERT(!f_op_branch); end else if ((f_op_early_branch)&&(op_valid)) `ASSERT(f_op_branch); always @(posedge i_clk) if (op_valid &&(f_op_branch || !fc_op_illegal)&& !clear_pipeline) begin // {{{ if (f_op_branch) begin // {{{ `ASSERT(!op_valid_alu); `ASSERT(!op_valid_mem); `ASSERT(!op_valid_div); `ASSERT(!op_valid_fpu); `ASSERT(!op_illegal); `ASSERT(!op_rA); `ASSERT(!op_rB); `ASSERT(!op_wR); `ASSERT(!op_wF); `ASSERT(op_opn == CPU_MOV_OP); // }}} end if (op_illegal) begin // {{{ `ASSERT(!op_valid_mem); `ASSERT(!op_valid_div); `ASSERT(!op_valid_fpu); `ASSERT( op_valid_alu); `ASSERT((!OPT_PIPELINED)||(!op_rA)); `ASSERT((!OPT_PIPELINED)||(!op_rB)); `ASSERT(!f_op_branch); // }}} end else begin if (!f_op_branch) begin // {{{ `ASSERT(fc_op_ALU == op_valid_alu); `ASSERT(fc_op_M == op_valid_mem); `ASSERT(fc_op_DV == op_valid_div); `ASSERT(fc_op_FP == op_valid_fpu); `ASSERT(fc_op_rA == op_rA); `ASSERT(fc_op_rB == op_rB); `ASSERT(fc_op_wF == op_wF); `ASSERT(fc_op_Rid[4:0] == op_R); `ASSERT(f_op_zI == (fc_op_I == 0)); `ASSERT(fc_op_wF == op_wF); `ASSERT(fc_op_lock == op_lock); if (!OPT_PIPELINED && step && stepped) begin `ASSERT(!op_break); end else begin `ASSERT(fc_op_break == op_break); end `ASSERT(fc_op_I == 0 || !i_mem_rdbusy || f_exwrite_cycle || !fc_op_rB || (fc_op_Bid[4:0] != f_last_reg && (f_mem_outstanding <= 1) && (!fc_op_M || !op_pipe)) || fc_op_Bid[4:0] == f_addr_reg); if (!i_halt && !i_reset && !f_exwrite_cycle) `ASSERT((!wr_reg_ce) ||(wr_reg_id != fc_op_Bid[4:0]) ||(!op_rB)||(fc_op_I == 0)); `ASSERT(fc_op_sim == op_sim); `ASSERT(fc_op_sim_immv == op_sim_immv); case(fc_op_cond[2:0]) 3'h0: `ASSERT(op_F == 8'h00); // Always 3'h1: `ASSERT(op_F == 8'h11); // Z 3'h2: `ASSERT(op_F == 8'h44); // LT 3'h3: `ASSERT(op_F == 8'h22); // C 3'h4: `ASSERT(op_F == 8'h88); // V 3'h5: `ASSERT(op_F == 8'h10); // NE 3'h6: `ASSERT(op_F == 8'h40); // GE (!N) 3'h7: `ASSERT(op_F == 8'h20); // NC endcase if ((fc_op_wR)&&(fc_op_Rid[4:0] == { gie, CPU_PC_REG})) begin `ASSERT(!op_phase); end else `ASSERT(f_op_phase == op_phase); // }}} end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } `ASSERT((!op_wR)||(fc_op_Rid[4:0] == op_R)); `ASSERT(((!op_wR)&&(!op_rA))||(fc_op_Aid[4:0] == op_Aid[4:0])); `ASSERT((!op_rB)||(fc_op_Bid[4:0] == op_Bid)); // // if ((!alu_illegal)&&(!ill_err_i)&&(!clear_pipeline)) if (f_op_early_branch) begin // {{{ `ASSERT(op_opn == CPU_MOV_OP); `ASSERT(!op_wR); `ASSERT(!op_wF); `ASSERT(f_op_branch); // }}} end else begin // {{{ `ASSERT(fc_op_op == op_opn); `ASSERT(fc_op_wR == op_wR); // }}} end end if (!OPT_PIPELINED_BUS_ACCESS) `ASSERT((!i_mem_rdbusy)||(i_mem_wreg != fc_op_Bid) ||(!fc_op_rB)||(fc_op_I == 0)); // }}} end else if (op_valid && !clear_pipeline && fc_op_illegal) begin // {{{ `ASSERT(op_illegal); `ASSERT(op_valid_alu); `ASSERT(!f_op_branch); // }}} end always @(*) if ((op_valid)&&(op_illegal)) begin `ASSERT(!op_valid_div); `ASSERT(!op_valid_fpu); `ASSERT(!op_valid_mem); end // always @(*) // if (!op_valid) // `ASSERT(!op_break); always @(*) if ((!OPT_CIS)&&(op_valid)) begin `ASSERT((!op_phase)||(op_illegal)); `ASSERT(op_pc[1:0] == 2'b0); end always @(*) if ((!OPT_LOCK)&&(op_valid)) `ASSERT((!op_lock)||(op_illegal)); always @(*) if (!OPT_EARLY_BRANCHING) `ASSERT(!f_op_early_branch); always @(*) if (op_ce) `ASSERT((dcd_valid)||(dcd_illegal)||(dcd_early_branch)); always @(*) if ((f_past_valid)&&(!f_op_zI)&&(i_mem_rdbusy)&&(op_valid)&&(op_rB)) `ASSERT((!OPT_DCACHE)||(OPT_MEMPIPE) ||(i_mem_wreg != op_Bid)); always @(posedge i_clk) if ((op_valid)&&(op_rB)&&(!f_op_zI)&&((i_mem_rdbusy)||(i_mem_valid)) &&(i_mem_wreg != {gie, CPU_PC_REG})) begin if (!OPT_MEMPIPE) begin `ASSERT(fc_alu_Aid[4:0] == i_mem_wreg); `ASSERT(i_mem_wreg != op_Bid); end end always @(posedge i_clk) if (i_mem_rdbusy) begin `ASSERT(fc_alu_M); `ASSERT(!OPT_PIPELINED||fc_alu_wR || (OPT_LOCK && f_mem_pc)); end always @(*) if ((op_valid)&&(!clear_pipeline)) `ASSERT(op_gie == gie); always @(*) if ((op_valid_alu)&&(!op_illegal)) begin if ((op_opn != CPU_SUB_OP) &&(op_opn != CPU_AND_OP) &&(op_opn != CPU_MOV_OP)) begin `ASSERT(op_wR); end if ((op_opn != CPU_MOV_OP)&&(op_opn != CPU_BREV_OP)) `ASSERT(op_rA); end always @(posedge i_clk) if ((op_valid)&&(!op_illegal) &&(!alu_illegal)&&(!ill_err_i)&&(!clear_pipeline)) begin `ASSERT(op_gie == gie); if ((gie)||(op_phase)) begin `ASSERT((!op_wR)||(op_R[4] == gie)); `ASSERT((!op_rA)||(op_Aid[4] == gie)); `ASSERT((!op_rB)||(op_Bid[4] == gie)); end else if (((op_valid_mem) ||(op_valid_div)||(op_valid_fpu) ||((op_valid_alu)&&(op_opn!=CPU_MOV_OP)))) begin `ASSERT((!op_wR)||(op_R[4] == gie)); `ASSERT((!op_rA)||(op_Aid[4] == gie)); `ASSERT((!op_rB)||(op_Bid[4] == gie)); end end always @(posedge i_clk) if ((!op_valid)&&(!$past(op_illegal)) &&(!clear_pipeline)&&(!pending_interrupt)) `ASSERT(!op_illegal); always @(*) begin if (alu_ce) `ASSERT(adf_ce_unconditional); if (div_ce) `ASSERT(adf_ce_unconditional); if (fpu_ce) `ASSERT(adf_ce_unconditional); if ((op_valid)&&(op_illegal)) `ASSERT(op_valid_alu); end always @(*) if (mem_ce) `ASSERT((op_valid)&&(op_valid_mem)&&(!op_illegal)); always @(*) if (div_ce) `ASSERT(op_valid_div); always @(*) if ((ibus_err_flag)||(ill_err_i)||(idiv_err_flag)) begin `ASSERT(master_stall); `ASSERT(!mem_ce); `ASSERT(!alu_ce); `ASSERT(!div_ce); `ASSERT(!adf_ce_unconditional); end always @(posedge i_clk) if ((adf_ce_unconditional)||(mem_ce)) `ASSERT(op_valid); always @(*) if ((op_valid_alu)&&(!adf_ce_unconditional)&&(!clear_pipeline)) `ASSERT(!op_ce); always @(*) if ((op_valid_div)&&(!adf_ce_unconditional)) `ASSERT(!op_ce); always @(posedge i_clk) if (alu_stall) `ASSERT(!alu_ce); always @(posedge i_clk) if (mem_stalled) `ASSERT(!o_mem_ce); always @(posedge i_clk) if (div_busy) `ASSERT(!div_ce); always @(*) if ((!i_reset)&&(break_pending)&&(!clear_pipeline)) `ASSERT((op_valid)&&(op_break)); // }}} //////////////////////////////////////////////////////////////////////// // // Memory // {{{ //////////////////////////////////////////////////////////////////////// // // Op: Memory pipeline assertions // assign f_next_mem = o_mem_addr + 1'b1; assign f_op_mem_addr = op_Bv[AW+1:2]; fmem #( // {{{ .OPT_LOCK(OPT_LOCK), .F_LGDEPTH(F_LGDEPTH), .OPT_MAXDEPTH((OPT_PIPELINED && OPT_PIPELINED_BUS_ACCESS) ? 14:1), .OPT_AXI_LOCK(2) // Let the solver pick // }}} ) chkmemops( // {{{ .i_clk(i_clk), .i_sys_reset(i_reset), .i_cpu_reset(i_reset), // .i_stb(o_mem_ce), .i_pipe_stalled(i_mem_pipe_stalled), .i_clear_cache(o_clear_dcache), .i_lock(o_bus_lock), .i_op(o_mem_op), .i_data(o_mem_data), .i_addr(o_mem_addr), .i_oreg(o_mem_reg), .i_areg(op_Bid), .i_busy(i_mem_busy),.i_rdbusy(i_mem_rdbusy), .i_valid(i_mem_valid), .i_err(i_bus_err), .i_wreg(i_mem_wreg), .i_result(i_mem_result), .f_outstanding(f_mem_outstanding), .f_pc(f_mem_pc), .f_gie(f_mem_gie), .f_read_cycle(f_read_cycle), .f_axi_write_cycle(f_exwrite_cycle), .f_last_reg(f_last_reg), .f_addr_reg(f_addr_reg) // }}} ); always @(*) if ((op_valid)&&(!fc_alu_prepipe)) `ASSERT((!op_valid_mem)||(!op_pipe)); // Validate op_opn and (some of) op_Bid // {{{ always @(*) if ((op_valid_mem)&&(op_pipe)) begin // {{{ if (i_mem_rdbusy) `ASSERT(op_opn[0] == f_exwrite_cycle); if (f_mem_outstanding > ((i_bus_err || i_mem_valid) ? 1:0)) `ASSERT(op_opn[0] != fc_alu_wR); // if ((i_mem_busy)&&(!i_mem_rdbusy && !i_mem_valid)) // `ASSERT(!o_mem_ce || op_opn[0] == 1'b1); if (i_mem_rdbusy) begin if (OPT_PIPELINED_BUS_ACCESS) begin end else if (OPT_DCACHE) `ASSERT(!i_mem_valid || i_mem_wreg != op_Bid); end // }}} end // }}} always @(posedge i_clk) if (op_valid_mem && op_pipe) begin // {{{ if ((i_mem_busy)&&(!OPT_DCACHE)) `ASSERT((f_op_mem_addr == o_mem_addr) ||(f_op_mem_addr == f_next_mem)); if (i_mem_valid) `ASSERT(op_Bid != i_mem_wreg); if (alu_busy||alu_valid) `ASSERT((!alu_wR)||(op_Bid != alu_reg)); if (f_past_valid) begin if ((i_mem_busy)&&(!OPT_DCACHE)) `ASSERT((op_Bv[(AW+1):2]==o_mem_addr[(AW-1):0]) ||(op_Bv[(AW+1):2]==o_mem_addr[(AW-1):0]+1'b1)); if ($past(mem_ce)) `ASSERT(op_Bid == $past(op_Bid)); end `ASSERT(op_Bid[3:1] != 3'h7); if ((i_mem_rdbusy||i_mem_valid) && !f_exwrite_cycle) begin if (!OPT_MEMPIPE) begin // {{{ `ASSERT(fc_alu_Aid[4:0] == i_mem_wreg); `ASSERT(i_mem_wreg != op_Bid); // }}} end else if (OPT_DCACHE) begin // {{{ `ASSERT(fc_alu_Aid[4:0] != op_Bid); // }}} end else // if (!OPT_DCACHE) begin // {{{ if ((i_mem_valid) ||($past(i_mem_rdbusy))) `ASSERT(i_mem_wreg != op_Bid); // }}} end end // }}} end always @(*) if ((dcd_valid)&&(dcd_pipe)) `ASSERT((op_Aid[3:1] != 3'h7)||(op_opn[0])); always @(*) if ((op_valid)&(!op_valid_mem)) `ASSERT((op_illegal)||(!op_pipe)); // Check f_addr_reg and f_last_reg // {{{ always @(*) if (OPT_MEMPIPE && (op_valid && op_rB) && !f_exwrite_cycle &&(!f_op_zI)&&(i_mem_rdbusy || i_mem_valid)) `ASSERT(f_last_reg != op_Bid); always @(*) if (i_mem_rdbusy && !f_exwrite_cycle) `ASSERT(f_last_reg == alu_reg); always @(*) if ((op_valid_mem)&&(op_pipe) && i_mem_valid) `ASSERT(op_Bid != i_mem_wreg); always @(*) if ((op_valid_mem)&&(op_pipe) && (i_mem_rdbusy || i_mem_valid)) `ASSERT(op_Bid == f_addr_reg); always @(*) if (i_mem_rdbusy) begin if (op_valid_mem && op_pipe) begin `ASSERT(op_Bid == f_addr_reg); `ASSERT(op_Bid != f_last_reg); if (dcd_M && dcd_pipe && !dcd_illegal && !dcd_early_branch) `ASSERT(op_Bid == dcd_B[4:0]); end else if (!op_valid_mem && dcd_M && dcd_pipe && !dcd_illegal && !dcd_early_branch) `ASSERT(dcd_B[4:0] == f_addr_reg); end // }}} always @(*) if (op_valid && !f_op_zI && i_mem_rdbusy && (f_last_reg != { gie, CPU_PC_REG })) `ASSERT(!op_rB || fc_op_Bid[4:0] == f_addr_reg ||((f_mem_outstanding == 1) && (fc_op_Bid[4:0] != f_last_reg))); // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the ALU stage // {{{ //////////////////////////////////////////////////////////////////////// // // always @(*) if ((alu_ce)&&(!clear_pipeline)) `ASSERT((op_valid_alu)&&(op_gie == gie)); always @(*) if ((mem_ce)&&(!clear_pipeline)) `ASSERT((op_valid_mem)&&(op_gie == gie)); always @(*) if ((div_ce)&&(!clear_pipeline)) `ASSERT((op_valid_div)&&(op_gie == gie)); always @(*) if ((!clear_pipeline)&&((i_mem_valid)||(div_valid)||(div_busy) ||(i_mem_rdbusy)||(alu_valid))) `ASSERT(alu_gie == gie); always @(*) if ((!OPT_CIS)&&(alu_pc_valid)) `ASSERT(alu_pc[1:0] == 2'b0); always @(*) if (!OPT_LOCK) `ASSERT((!o_bus_lock)&&(!prelock_stall)); always @(*) if (!OPT_DIV) `ASSERT((!dcd_DIV)&&(!op_valid_div)&&(!div_busy)&&(!div_valid)&&(!div_ce)); always @(*) if (OPT_MPY == 0) `ASSERT(alu_busy == 1'b0); always @(*) if (!clear_pipeline) begin if ((alu_valid)||(alu_illegal)) `ASSERT(alu_gie == gie); if (div_valid) `ASSERT(alu_gie == gie); end always @(*) if (alu_busy) begin `ASSERT(!i_mem_rdbusy); `ASSERT(!div_busy); `ASSERT(!fpu_busy); end else if (i_mem_rdbusy) begin `ASSERT(!div_busy); `ASSERT(!fpu_busy); end else if (div_busy) `ASSERT(!fpu_busy); always @(posedge i_clk) if ((div_valid)||(div_busy)) `ASSERT(alu_reg[3:1] != 3'h7); always @(posedge i_clk) if ((f_past_valid)&&(wr_reg_ce) &&((!$past(r_halted))||(!$past(i_dbg_we)))) `ASSERT(alu_gie == gie); // }}} //////////////////////////////////////////////////////////////////////// // // Contract checking : A operand // {{{ //////////////////////////////////////////////////////////////////////// // // always @(*) begin f_Av = regset[fc_op_Aid[4:0]]; if (fc_op_Aid[3:0] == CPU_PC_REG) begin if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) f_Av = wr_spreg_vl; else if (fc_op_Aid[4] == op_gie) f_Av = op_pc; // f_next_addr; else if (fc_op_Aid[3:0] == { 1'b1, CPU_PC_REG }) begin f_Av[31:(AW+1)] = 0; f_Av[(AW+1):0] = { upc, uhalt_phase, 1'b0 }; end end else if (fc_op_Aid[4:0] == { 1'b0, CPU_CC_REG }) begin f_Av = { w_cpu_info, regset[fc_op_Aid[4:0]][22:16], w_iflags }; if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) f_Av[22:16] = wr_spreg_vl[22:16]; end else if (fc_op_Aid[4:0] == { 1'b1, CPU_CC_REG }) begin f_Av = { w_cpu_info, regset[fc_op_Aid[4:0]][22:16], w_uflags }; if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) f_Av[22:16] = wr_spreg_vl[22:16]; end else if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) f_Av = wr_gpreg_vl; else f_Av = regset[fc_op_Aid[4:0]]; end // }}} //////////////////////////////////////////////////////////////////////// // // Contract checking : B operand // {{{ //////////////////////////////////////////////////////////////////////// // // // The PRE-logic always @(*) begin f_pre_Bv = regset[fc_op_Bid[4:0]]; // if (fc_op_Bid[3:0] == CPU_PC_REG) begin // Can always read your own address if (fc_op_Bid[4] == op_gie) f_pre_Bv = { {(30-AW){1'b0}}, op_pc[(AW+1):2], 2'b00 }; // f_next_addr; else // if (fc_op_Bid[4]) // Supervisor or user may read the users PC reg begin f_pre_Bv = 0; f_pre_Bv[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) f_pre_Bv = wr_spreg_vl; end end else if (fc_op_Bid[3:0] == CPU_CC_REG) begin f_pre_Bv = { w_cpu_info, regset[fc_op_Bid[4:0]][22:16], w_uflags }; // if ((fc_op_Bid[4] == op_gie)&&(!fc_op_Bid[4])) f_pre_Bv[15:0] = (gie || fc_op_Bid[4]) ? w_uflags : w_iflags; if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) f_pre_Bv[22:16] = wr_spreg_vl[22:16]; end else if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) f_pre_Bv = wr_gpreg_vl; else f_pre_Bv = regset[fc_op_Bid[4:0]]; end // The actual calculation of B assign f_Bv = (fc_op_rB) ? ((fc_op_Bid[5]) ? ( { f_pre_Bv }+{ fc_op_I[29:0],2'b00 }) : (f_pre_Bv + fc_op_I)) : fc_op_I; // }}} //////////////////////////////////////////////////////////////////////// // // CONTRACT: The operands to an ALU/MEM/DIV operation must be valid. // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if (!i_reset && op_valid && !op_illegal && (!clear_pipeline && !dbg_clear_pipe) && (!i_halt || !dbgv) &&((!wr_reg_ce)||(wr_reg_id!= { gie, CPU_PC_REG })) &&(!f_op_branch)) begin if ((fc_op_rA)&&(fc_op_Aid[3:1] != 3'h7)) `ASSERT(f_Av == op_Av); if (!fc_op_rB || fc_op_Bid[4:0] != { gie, CPU_CC_REG } || !OPT_PIPELINED) begin `ASSERT(f_Bv == op_Bv); end else if (f_op_zI) `ASSERT(((f_Bv ^ op_Bv) & 32'hffff_c0ff) == 0); end // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the ALU stage // {{{ //////////////////////////////////////////////////////////////////////// // // // alu_valid // alu_ce // alu_stall // ALU stage assertions always @(posedge i_clk) if ((alu_ce)||(mem_ce)||(div_ce)||(fpu_ce)) begin f_alu_insn_word <= f_op_insn_word; f_alu_phase <= f_op_phase; end initial f_alu_branch = 1'b0; always @(posedge i_clk) if ((adf_ce_unconditional)||(mem_ce)) f_alu_branch <= f_op_branch; else f_alu_branch <= 1'b0; f_idecode #( // {{{ .OPT_MPY((OPT_MPY!=0)? 1'b1:1'b0), .OPT_SHIFTS(OPT_SHIFTS), .OPT_DIVIDE(OPT_DIV), .OPT_FPU(IMPLEMENT_FPU), .OPT_LOCK(OPT_LOCK), .OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), .OPT_SIM(1'b0), .OPT_USERMODE(OPT_USERMODE), .OPT_LOWPOWER(OPT_LOWPOWER), .OPT_CIS(OPT_CIS) // }}} ) f_insn_decode_alu( // {{{ f_alu_insn_word, f_alu_phase, alu_gie, fc_alu_illegal, fc_alu_Rid, fc_alu_Aid, fc_alu_Bid, fc_alu_I, fc_alu_cond, fc_alu_wF, fc_alu_op, fc_alu_ALU, fc_alu_M, fc_alu_DV, fc_alu_FP, fc_alu_break, fc_alu_lock, fc_alu_wR, fc_alu_rA, fc_alu_rB, fc_alu_prepipe, fc_alu_sim, fc_alu_sim_immv // }}} ); always @(posedge i_clk) if (!wr_reg_ce) begin if (f_alu_branch) begin `ASSERT((!div_valid)&&(!div_busy)); `ASSERT((!fpu_valid)&&(!fpu_busy)); `ASSERT(!i_mem_rdbusy); `ASSERT(!alu_busy); end else begin `ASSERT(fc_alu_sim == alu_sim); `ASSERT(fc_alu_sim_immv == alu_sim_immv); if (!fc_alu_DV) `ASSERT((!div_valid)&&(!div_busy)&&(!div_error)); if (!fc_alu_M) `ASSERT(!i_mem_rdbusy); if (!fc_alu_ALU) `ASSERT(!alu_busy); if (!fc_alu_FP) `ASSERT((!fpu_busy)&&(!fpu_error)); if (alu_busy) `ASSERT((fc_alu_op[3:1] == 3'h5) ||(fc_alu_op[3:0] == 4'hc)); if ((alu_busy)||(div_busy)||(fpu_busy)) begin `ASSERT(!i_mem_rdbusy); `ASSERT((clear_pipeline) ||(fc_alu_Rid[4:0] == alu_reg)); if (alu_busy) `ASSERT(fc_alu_wF == alu_wF); if ((fc_alu_Rid[3:1] == 3'h7)&&(alu_wR) &&(fc_alu_Rid[4:0] != { gie, 4'hf })) `ASSERT(pending_sreg_write); end else if (i_mem_rdbusy) begin if ($past(i_mem_rdbusy)) `ASSERT(fc_alu_Rid[4] == f_mem_gie); end //if ((div_busy)||(fpu_busy)) // `ASSERT(alu_wR); //else if ((alu_busy)&&(alu_wR)) `ASSERT(fc_alu_wR); if (alu_busy || i_mem_rdbusy || div_busy) begin if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, CPU_PC_REG})) begin `ASSERT(!alu_phase); end else `ASSERT(f_alu_phase == alu_phase); end end end else if (!dbgv) // && wr_reg_ce begin `ASSERT(fc_alu_DV || (!div_valid)&&(!div_error)); `ASSERT(fc_alu_ALU|| !alu_valid); `ASSERT(fc_alu_M || !i_mem_valid); `ASSERT(fc_alu_FP || (!fpu_valid)&&(!fpu_error)); `ASSERT((!alu_busy)&&(!div_busy)&&(!fpu_busy)); if ((!alu_illegal)&&(fc_alu_cond[3])&&(fc_alu_wR)&&(fc_alu_ALU)) `ASSERT(alu_wR); if (!i_mem_valid) `ASSERT(fc_alu_Rid[4:0] == alu_reg); if (f_exwrite_cycle) begin `ASSERT(!alu_wR); end else `ASSERT((!alu_wR)||(fc_alu_wR == alu_wR)); if (alu_valid) `ASSERT(fc_alu_wF == alu_wF); if (!fc_alu_wF) `ASSERT(!wr_flags_ce); `ASSERT(!f_alu_branch); end always @(posedge i_clk) if (f_mem_pc && i_mem_rdbusy) begin // {{{ `ASSERT(!OPT_PIPELINED || cc_invalid_for_dcd || !fc_alu_wR || fc_alu_Rid[4:0] != { gie, CPU_CC_REG }); `ASSERT(!mem_ce); if (OPT_PIPELINED && fc_alu_Rid[4:0] != { gie, CPU_PC_REG } && (!OPT_LOCK || fc_alu_wR)) `ASSERT(pending_sreg_write); if ((!OPT_DCACHE)||(!OPT_MEMPIPE)) begin `ASSERT(!fc_alu_prepipe); end else if ((i_mem_rdbusy && !f_exwrite_cycle) &&(!$past(mem_ce))&&(!$past(mem_ce,2))) `ASSERT(!fc_alu_prepipe); // }}} end else if (i_mem_rdbusy) begin `ASSERT(!pending_sreg_write); // assert(!cc_invalid_for_dcd); end // Ongoing memory operation check // {{{ always @(posedge i_clk) if (i_mem_rdbusy) begin // In pipelined mode, this is an ongoing load operation // Otherwise, mem_rdbusy == i_mem_busy and we have no idea // what type of operation we are in `ASSERT(!fc_alu_illegal); `ASSERT(fc_alu_M); `ASSERT(gie == alu_reg[4]); if (fc_alu_rB) `ASSERT(fc_alu_Bid[4:0] == f_addr_reg); if (!f_exwrite_cycle) begin `ASSERT(alu_reg == f_last_reg); if (alu_reg == f_addr_reg) `ASSERT(!op_pipe); end if (fc_alu_cond[3]) `ASSERT(fc_alu_Rid[4:0] == alu_reg); if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, CPU_PC_REG})) begin `ASSERT(!alu_phase); end else `ASSERT(f_alu_phase == alu_phase); if (f_exwrite_cycle) begin `ASSERT(!fc_alu_wR); end else `ASSERT(fc_alu_wR); end else if ((i_mem_busy)&&(fc_alu_M) &&(f_mem_outstanding > ((i_mem_valid || i_bus_err) ? 1:0)) &&(!i_bus_err)) begin // Ongoing store operation // {{{ `ASSERT(!fc_alu_illegal); `ASSERT(fc_alu_M); `ASSERT(!fc_alu_wR); // }}} end // }}} always @(*) if (!fc_alu_wR && i_mem_rdbusy) begin `ASSERT(OPT_LOCK); `ASSERT(f_mem_outstanding == 1); `ASSERT(f_mem_pc); `ASSERT(!f_read_cycle); `ASSERT(!cc_invalid_for_dcd); `ASSERT(f_exwrite_cycle); // `ASSERT(i_mem_wreg[3:0] == 4'hf); `ASSERT(fc_alu_M); `ASSERT(!pending_sreg_write); `ASSERT(!alu_wR); end else if (i_mem_rdbusy) begin `ASSERT(fc_alu_wR); `ASSERT(!f_exwrite_cycle); `ASSERT(f_read_cycle); end // }}} //////////////////////////////////////////////////////////////////////// // // Assertions about the writeback stage // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if ((f_past_valid)&&($past(i_reset))&&($past(gie) != gie)) `ASSERT(clear_pipeline); always @(*) if (!IMPLEMENT_FPU) begin `ASSERT(!ifpu_err_flag); `ASSERT(!ufpu_err_flag); end always @(posedge i_clk) if (dbgv) begin `ASSERT(wr_index == 3'h0); `ASSERT(wr_gpreg_vl == $past(i_dbg_data)); `ASSERT(wr_spreg_vl == $past(i_dbg_data)); end always @(*) if (i_mem_rdbusy || i_mem_valid) begin `ASSERT(wr_index == 3'h1); if (i_mem_valid) `ASSERT(wr_gpreg_vl == i_mem_result); end always @(*) if (alu_busy || alu_valid) begin `ASSERT(wr_index == 3'h2); if (alu_valid) begin `ASSERT(wr_gpreg_vl == alu_result); `ASSERT(wr_spreg_vl == alu_result); end end always @(*) if (div_busy || div_valid || div_error) begin `ASSERT(wr_index == 3'h3); if (div_valid) `ASSERT(wr_gpreg_vl == div_result); end always @(*) if (fpu_busy || fpu_valid || fpu_error) begin `ASSERT(wr_index == 3'h4); if (fpu_valid) `ASSERT(wr_gpreg_vl == fpu_result); end always @(posedge i_clk) if ((f_past_valid)&&(r_halted)) begin `ASSERT(!div_busy); `ASSERT(!i_mem_rdbusy); `ASSERT(!alu_busy); `ASSERT(!div_valid); `ASSERT(!i_mem_valid); `ASSERT(!alu_valid); end always @(*) if (((wr_reg_ce)||(wr_flags_ce))&&(!dbgv)) `ASSERT(!alu_illegal); always @(*) if (!fc_alu_wR && (!OPT_LOCK || !f_mem_pc)) `ASSERT(!i_mem_rdbusy); always @(*) if (alu_illegal) `ASSERT(!i_mem_rdbusy); always @(posedge i_clk) if (wr_reg_ce && !$past(i_dbg_we)) begin if (!fc_alu_wR) `ASSERT(OPT_LOCK && f_exwrite_cycle); // Since writes are asynchronous, they can create errors later `ASSERT((!i_bus_err)||(!i_mem_valid)); `ASSERT(!fpu_error); `ASSERT(!div_error); end // }}} //////////////////////////////////////////////////////////////////////// // // Clock enable checking // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if (!OPT_CLKGATE) begin assert(w_clken); assert(o_clken); end else if (!f_past_valid) begin end // clken can be anything on the first clock else if ($past(i_reset)) begin assert(w_clken != OPT_START_HALTED); end else if ($past((!i_halt || !r_halted) && (!sleep || i_interrupt))) begin assert(w_clken); end else if ($past((i_mem_busy && f_mem_outstanding > 0) || o_mem_ce)) begin assert(w_clken); end else if (i_mem_busy || div_busy || alu_busy || fpu_busy) begin assert(o_clken); end else if ($past((i_interrupt || pending_interrupt) && !i_halt)) begin assert(o_clken); end else if (!sleep && !r_halted) begin assert(o_clken); end // always @(posedge i_clk) // if (f_past_valid && i_mem_busy) // assert(!r_halted); always @(posedge i_clk) if (!OPT_CLKGATE) begin assert(o_clken); end else if (!f_past_valid) begin end else if (i_dbg_we || i_clear_cache || f_mem_outstanding > 0) begin assert(o_clken); end else if (!i_halt && (i_interrupt || !sleep)) assert(o_clken); always @(posedge i_clk) if (!i_reset && sleep) begin assert(!wr_reg_ce || dbgv); assert(!i_mem_rdbusy); end always @(posedge i_clk) if (!i_reset && r_halted) assert(!i_mem_rdbusy); // }}} //////////////////////////////////////////////////////////////////////// // // Low power checking // {{{ //////////////////////////////////////////////////////////////////////// // // // In low power mode, *nothing* should change unless we are doing // something. generate if (OPT_LOWPOWER && OPT_PIPELINED) begin : F_OPT_LOWPOWER_CHECK wire [70:0] op_state; // // OP* registers can only be expected to hold steady if // pipelined. If not pipelined, many of these registers // become aliases for the decode registers which will // (of course) change from one clock to the next. // assign op_state = { op_valid_mem, op_valid_div, op_valid_alu, op_valid_fpu, op_opn, op_R, op_Rcc, op_Aid, op_Bid, op_pc, r_op_break, op_illegal }; always @(posedge i_clk) if (f_past_valid && !$past(i_reset) && !$past(clear_pipeline) && ($past(op_valid && !adf_ce_unconditional && !mem_ce) ||($past(!op_valid && !dcd_ljmp) && !op_valid))) begin assert($stable(op_state)); end always @(posedge i_clk) if (f_past_valid && !op_valid) begin assert(r_op_F == 7'h00); assert(!op_wR); assert(!op_wF); assert(!op_rA); assert(!op_rB); assert(r_op_Av == 0); assert(r_op_Bv == 0); end end endgenerate // }}} //////////////////////////////////////////////////////////////////////// // // Step properties // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if (f_past_valid && !i_reset && $past(!gie)) assert(!stepped); always @(posedge i_clk) if (f_past_valid && step && stepped) begin assert(!break_pending); assert(!o_bus_lock); end always @(posedge i_clk) if (f_past_valid && !i_reset && !o_bus_lock && !$past(o_bus_lock) && !$past(o_dbg_stall) && !o_dbg_stall) begin if (step && !stepped) begin `ASSERT(!i_mem_rdbusy); `ASSERT(!div_busy); `ASSERT(!alu_busy); `ASSERT(!fpu_busy); end if (wr_reg_ce || wr_flags_ce) `ASSERT(!step || stepped); end always @(posedge i_clk) if (!i_reset &&(i_mem_rdbusy|| div_busy|| alu_busy || fpu_busy)) assert(!r_halted); always @(posedge i_clk) if (f_past_valid && !$past(i_reset) && i_mem_busy) assert(!$rose(r_halted)); always @(posedge i_clk) if (step && stepped) begin assert(!adf_ce_unconditional); assert(!div_ce); assert(!mem_ce); assert(!fpu_ce); end // }}} //////////////////////////////////////////////////////////////////////// // // Ad-hoc (unsorted) properties // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&($past(i_mem_rdbusy)) &&(!$past(i_mem_valid) ||($past(i_mem_wreg[3:1] != 3'h7)))) `ASSERT(!i_mem_valid || i_mem_wreg[4] == alu_gie); always @(posedge i_clk) if (i_mem_valid) `ASSERT(i_mem_wreg[4] == alu_gie); always @(posedge i_clk) if (i_mem_rdbusy) `ASSERT(alu_gie == f_mem_gie); always @(posedge i_clk) if (f_mem_outstanding > 0) `ASSERT(alu_gie == f_mem_gie); // Break instructions are not allowed to move past the op stage always @(*) if ((break_pending)||(op_break)) `ASSERT((!alu_ce)&&(!mem_ce)&&(!div_ce)&&(!fpu_ce)); always @(*) if (op_break) `ASSERT((!alu_ce)&&(!mem_ce)&&(!div_ce)&&(!fpu_ce)); always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset)) &&($past(break_pending))&&(!break_pending)) `ASSERT((clear_pipeline)||($past(clear_pipeline))); always @(*) if ((o_break)||((alu_valid)&&(alu_illegal))) begin `ASSERT(!alu_ce); `ASSERT(!mem_ce); `ASSERT(!div_ce); `ASSERT(!fpu_ce); `ASSERT(!i_mem_rdbusy); // The following two shouldn't be true, but will be true // following a bus error if (!i_bus_err) begin `ASSERT(!alu_busy); `ASSERT(!div_busy); `ASSERT(!fpu_busy); end end always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&(!$past(clear_pipeline))&& ($past(div_busy))&&(!clear_pipeline)) begin `ASSERT($stable(alu_reg)); `ASSERT(alu_reg[4] == alu_gie); `ASSERT($stable(alu_pc)); `ASSERT($stable(alu_phase)); end always @(posedge i_clk) if ((f_past_valid)&&(!$past(i_reset))&&(!i_reset) &&(!$past(clear_pipeline))&&(!clear_pipeline) &&(($past(div_busy))||($past(i_mem_rdbusy)))) `ASSERT($stable(alu_gie)); always @(posedge i_clk) if (i_mem_rdbusy && f_mem_outstanding > 0) `ASSERT(!new_pc); always @(posedge i_clk) if ((wr_reg_ce)&&((wr_write_cc)||(wr_write_pc))) `ASSERT(wr_spreg_vl == wr_gpreg_vl); always @(posedge i_clk) if ((f_past_valid)&&(!$past(clear_pipeline))&&(!$past(i_reset)) &&($past(op_valid))&&($past(op_illegal))&&(!op_illegal)) `ASSERT(alu_illegal); always @(*) if ((OPT_PIPELINED)&&(alu_valid)&&(alu_wR)&&(!clear_pipeline) &&(alu_reg[3:1] == 3'h7) &&(alu_reg[4:0] != { gie, CPU_PC_REG })) `ASSERT(pending_sreg_write); always @(posedge i_clk) if ((OPT_PIPELINED)&&(i_mem_valid || i_mem_rdbusy) &&(f_last_reg[3:1] == 3'h7) &&(!f_exwrite_cycle && f_last_reg[4:0] != { gie, CPU_PC_REG })) begin `ASSERT(pending_sreg_write); end else if ((OPT_PIPELINED)&&(OPT_DCACHE) && i_mem_valid &&($past(i_mem_rdbusy)) &&($past(i_mem_rdbusy,2))) `ASSERT((i_mem_wreg[3:1] != 3'h7) ||f_exwrite_cycle ||(i_mem_wreg == { gie, CPU_PC_REG}) ||(pending_sreg_write)); always @(*) if ((op_valid_alu)||(op_valid_div)||(op_valid_mem)||(op_valid_fpu)) `ASSERT(op_valid); always @(*) if (!OPT_PIPELINED) begin if (op_valid) begin `ASSERT(!dcd_valid); `ASSERT(!i_mem_busy || f_mem_outstanding == 0); `ASSERT(!alu_busy); `ASSERT(!div_busy); `ASSERT((!wr_reg_ce)||(dbgv)); `ASSERT(!wr_flags_ce); end end always @(posedge i_clk) if ((!OPT_PIPELINED)&&(f_past_valid)) begin if (op_valid) `ASSERT($stable(f_dcd_insn_word)); end always @(posedge i_clk) if ((alu_ce)||(div_ce)||(fpu_ce)) `ASSERT(adf_ce_unconditional); always @(posedge i_clk) if ((!clear_pipeline)&&(master_ce)&&(op_ce)&&(op_valid)) begin if (op_valid_mem) begin `ASSERT((mem_ce)||(!set_cond)); end else begin `ASSERT(!master_stall); if ((set_cond)&&(op_valid_div)) `ASSERT(div_ce||pending_sreg_write); if (!op_valid_alu) `ASSERT(!alu_ce); end end // }}} //////////////////////////////////////////////////////////////////////// // // Debug port read properties // {{{ //////////////////////////////////////////////////////////////////////// // // generate if (OPT_DBGPORT) begin : CHK_DBGPORT_READS reg [31:0] f_dbg_check; // o_dbg_reg -- Reading from the debugging interface if (OPT_DISTRIBUTED_REGS) begin : F_CHK_DISTRIBUTED_RAM // {{{ always @(posedge i_clk) if ((f_past_valid)&&($past(i_halt))&&(!$past(i_dbg_we))) begin if ($past(i_dbg_rreg[3:1]) != 3'h7) `ASSERT(o_dbg_reg == $past(regset[i_dbg_rreg])); if ($past(i_dbg_rreg[4:0]) == 5'h0f) `ASSERT(o_dbg_reg[AW+1:0] == $past({ ipc[(AW+1):2], ihalt_phase, 1'b0})); if ($past(i_dbg_rreg[4:0]) == 5'h1f) `ASSERT(o_dbg_reg[AW+1:0] == $past({ upc[(AW+1):2], uhalt_phase, 1'b0})); if ($past(i_dbg_rreg[4:0]) == 5'h0e) begin `ASSERT(o_dbg_reg[15:6] == $past(w_iflags[15:6])); `ASSERT(o_dbg_reg[ 4:0] == $past(w_iflags[ 4:0])); end if ($past(i_dbg_rreg[4:0]) == 5'h1e) begin `ASSERT(o_dbg_reg[15:6] == $past(w_uflags[15:6])); `ASSERT(o_dbg_reg[ 4:0] == $past(w_uflags[ 4:0])); end if ($past(i_dbg_rreg[3:0]) == 4'he) begin `ASSERT(o_dbg_reg[15] == 1'b0); `ASSERT(o_dbg_reg[31:23] == w_cpu_info); `ASSERT(o_dbg_reg[CPU_GIE_BIT] == $past(i_dbg_rreg[4])); end end // }}} end else begin : F_CHK_NO_DISTRIBUTED_RAM // {{{ always @(posedge i_clk) if (f_past_valid&&$past(f_past_valid) && $past(i_halt)&&$past(i_halt,2) &&(!$past(i_dbg_we))) begin if ($past(i_dbg_rreg[3:1],2) != 3'h7) `ASSERT(o_dbg_reg == $past(regset[i_dbg_rreg],2)); end // }}} end if (OPT_USERMODE) begin : CHK_USER always @(*) begin f_dbg_check = regset[i_dbg_rreg]; case(i_dbg_rreg) 5'h0e: begin f_dbg_check[15:0] = w_iflags; f_dbg_check[31:23] = w_cpu_info; f_dbg_check[CPU_GIE_BIT] <= 1'b0; end 5'h0f: f_dbg_check[(AW+1):0] = { ipc[(AW+1):2], ihalt_phase, 1'b0 }; 5'h1e: begin f_dbg_check[15:0] = w_uflags; f_dbg_check[31:23] = w_cpu_info; f_dbg_check[CPU_GIE_BIT] <= 1'b1; end 5'h1f: f_dbg_check[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; default: f_dbg_check = regset[i_dbg_rreg]; endcase end end else begin : NO_USER always @(*) begin if (OPT_DISTRIBUTED_REGS) f_dbg_check = regset[i_dbg_rreg]; else f_dbg_check = 0; case(i_dbg_rreg[3:0]) 4'h0e: begin f_dbg_check[15:0] = w_iflags; f_dbg_check[31:23] = w_cpu_info; f_dbg_check[CPU_GIE_BIT] <= 1'b0; end 4'h0f: f_dbg_check[(AW+1):0] = { ipc[(AW+1):2], ihalt_phase, 1'b0 }; default: f_dbg_check = regset[i_dbg_rreg[3:0]]; endcase end end if (OPT_DISTRIBUTED_REGS) begin always @(posedge i_clk) if (!i_reset && !$past(i_reset)) assert(o_dbg_reg == $past(f_dbg_check)); end else begin always @(posedge i_clk) if (!i_reset && !$past(i_reset) && !$past(i_reset,2)) assert(o_dbg_reg == $past(f_dbg_check,2)); end end endgenerate // }}} //////////////////////////////////////////////////////////////////////// // // Cover statements // {{{ //////////////////////////////////////////////////////////////////////// // always @(posedge i_clk) begin cover(!i_reset); cover(!i_halt); cover(!i_reset && !i_halt); end always @(posedge i_clk) if (!i_halt && !i_reset) begin cover(i_pf_valid && i_pf_illegal); cover(i_pf_valid && !i_pf_illegal); cover(dcd_valid && dcd_illegal); cover(dcd_valid && !dcd_illegal); cover(op_valid && !op_illegal); cover(op_valid && op_illegal); cover(op_valid && !op_illegal && op_ce); cover(op_valid && op_illegal && op_ce); cover(alu_valid); cover(div_valid); cover(o_mem_ce); cover(i_mem_valid); cover(wr_reg_ce); cover(gie); end always @(posedge i_clk) if (!i_halt && !i_reset && !$past(i_reset)) begin cover(i_interrupt && !alu_phase && !o_bus_lock); cover(alu_illegal); cover( gie && w_switch_to_interrupt); cover(!gie && w_release_from_interrupt); // Cover an illegal instruction cover(alu_illegal); cover(alu_illegal && !clear_pipeline); // Cover a break instruction cover((master_ce)&&(break_pending)&&(!break_en)); cover(o_break); // if (master_ce && (break_en || break_pending)) // `ASSERT(!wr_reg_ce); cover(gie); cover(step); cover(step && w_switch_to_interrupt); cover(step && w_switch_to_interrupt && !i_interrupt); end always @(posedge i_clk) if (!i_halt && !i_reset && !$past(i_reset)) begin cover(step); cover(step && wr_reg_ce); cover($fell(step) && $stable(!i_reset && !i_halt)); cover($past(step && !i_reset && !i_halt)); cover($past(step && !i_reset && !i_halt,2)); cover(!o_bus_lock && alu_ce && step); cover(user_step && !gie); cover(user_step && gie); cover(user_step && gie && wr_reg_ce); // Cover a division by zero cover(!OPT_DIV || div_busy); cover(!OPT_DIV || div_error); cover(!OPT_DIV || div_valid); end generate if (OPT_DIV) begin : F_DIVIDE always @(posedge i_clk) if (!i_halt && !i_reset && !$past(i_reset)) begin // Cover a division by zero cover(div_busy); cover(div_error); cover(div_valid); end end endgenerate generate if (OPT_LOCK) begin : F_CVR_LOCK always @(posedge i_clk) if (f_past_valid && !i_reset && !$past(i_reset || clear_pipeline)) begin cover($rose(o_bus_lock)); cover($fell(o_bus_lock)); cover($fell(o_bus_lock) && !$past(i_bus_err || div_error || alu_illegal)); end end else begin always @(*) `ASSERT(!o_bus_lock); end endgenerate always @(posedge i_clk) if (!i_halt && !i_reset && !$past(i_reset) && wr_reg_ce) begin `ASSERT(!alu_illegal); end always @(posedge i_clk) if (!i_halt && !i_reset && wr_reg_ce) begin cover(o_bus_lock); // Cover the switch to interrupt cover(i_interrupt); cover(i_interrupt && !alu_phase); cover(i_interrupt && !o_bus_lock); cover((i_interrupt)&&(!alu_phase)&&(!o_bus_lock)); // Cover a "step" instruction // cover(((alu_pc_valid)||(mem_pc_valid)) &&(step)&&(!alu_phase)&&(!o_bus_lock)); // `ASSERT(!(((alu_pc_valid)||(mem_pc_valid)) // &&(step)&&(!alu_phase)&&(!o_bus_lock))); // Cover a bus error cover(i_bus_err); // Cover a TRAP instruction to the CC register cover(!alu_gie && !wr_spreg_vl[CPU_GIE_BIT] &&(wr_reg_id[4])&&(wr_write_cc)); // Cover an AXI lock return branch cover(i_mem_valid && f_exwrite_cycle && !f_read_cycle); end // Cover all the various reasons to switch to an interrupt // {{{ always @(posedge i_clk) if ((f_past_valid && !i_reset && !$past(i_reset) && !i_halt) && gie) begin cover((pending_interrupt) &&(!alu_phase)&&(!o_bus_lock)&&(!i_mem_busy)); cover(div_error); // cover(fpu_error); cover(i_bus_err); cover(wr_reg_ce && !wr_spreg_vl[CPU_GIE_BIT] && wr_reg_id[4] && wr_write_cc); if (!clear_pipeline && !w_switch_to_interrupt) begin cover(pending_interrupt); cover(i_interrupt); cover(mem_ce && step); cover(break_pending && !adf_ce_unconditional); cover(adf_ce_unconditional && op_illegal); cover(adf_ce_unconditional && step); end `ASSERT(!adf_ce_unconditional || !break_pending); end // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Careless assumptions // {{{ //////////////////////////////////////////////////////////////////////// // // // Once asserted, an interrupt will stay asserted while the CPU is // in user mode always @(posedge i_clk) if ((f_past_valid)&&($past(i_interrupt && (gie || !i_mem_busy)))) assume(i_interrupt); // }}} `endif // FORMAL // }}} endmodule