6119 lines
156 KiB
Verilog
6119 lines
156 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|