OpenCores
URL https://opencores.org/ocsvn/zipcpu/zipcpu/trunk

Subversion Repositories zipcpu

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /zipcpu/trunk/rtl/core
    from Rev 209 to Rev 205
    Reverse comparison

Rev 209 → Rev 205

/iscachable.v File deleted
/slowmpy.v File deleted
/README.md File deleted
/mpyop.v File deleted
/cpuops.v
4,8 → 4,9
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: This is the ZipCPU ALU function. It handles all of the
// instruction opcodes 0-13. (14-15 are divide opcodes).
// Purpose: This supports the instruction set reordering of operations
// created by the second generation instruction set, as well as
// the new operations of POPC (population count) and BREV (bit reversal).
//
//
// Creator: Dan Gisselquist, Ph.D.
13,7 → 14,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
36,53 → 37,37
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
`include "cpudefs.v"
//
module cpuops(i_clk,i_reset, i_stb, i_op, i_a, i_b, o_c, o_f, o_valid,
module cpuops(i_clk,i_rst, i_ce, i_op, i_a, i_b, o_c, o_f, o_valid,
o_busy);
parameter IMPLEMENT_MPY = `OPT_MULTIPLY;
parameter [0:0] OPT_SHIFTS = 1'b1;
input wire i_clk, i_reset, i_stb;
input wire [3:0] i_op;
input wire [31:0] i_a, i_b;
parameter IMPLEMENT_MPY = `OPT_MULTIPLY;
input i_clk, i_rst, i_ce;
input [3:0] i_op;
input [31:0] i_a, i_b;
output reg [31:0] o_c;
output wire [3:0] o_f;
output reg o_valid;
output wire o_busy;
 
genvar k;
 
// Shift register pre-logic
wire [32:0] w_lsr_result, w_asr_result, w_lsl_result;
generate if (OPT_SHIFTS)
begin : IMPLEMENT_SHIFTS
wire signed [32:0] w_pre_asr_input, w_pre_asr_shifted;
assign w_pre_asr_input = { i_a, 1'b0 };
assign w_pre_asr_shifted = w_pre_asr_input >>> i_b[4:0];
assign w_asr_result = (|i_b[31:5])? {(33){i_a[31]}}
: w_pre_asr_shifted;// ASR
assign w_lsr_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00
:((i_b[5])?{32'h0,i_a[31]}
wire signed [32:0] w_pre_asr_input, w_pre_asr_shifted;
assign w_pre_asr_input = { i_a, 1'b0 };
assign w_pre_asr_shifted = w_pre_asr_input >>> i_b[4:0];
assign w_asr_result = (|i_b[31:5])? {(33){i_a[31]}}
: w_pre_asr_shifted;// ASR
assign w_lsr_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00
:((i_b[5])?{32'h0,i_a[31]}
: ( { i_a, 1'b0 } >> (i_b[4:0]) ));// LSR
assign w_lsl_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00
:((i_b[5])?{i_a[0], 32'h0}
: ({1'b0, i_a } << i_b[4:0])); // LSL
 
: ( { i_a, 1'b0 } >> (i_b[4:0]) ));// LSR
assign w_lsl_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00
:((i_b[5])?{i_a[0], 32'h0}
: ({1'b0, i_a } << i_b[4:0])); // LSL
end else begin : NO_SHIFTS
 
assign w_asr_result = { i_a[31], i_a[31:0] };
assign w_lsr_result = { 1'b0, i_a[31:0] };
assign w_lsl_result = { i_a[31:0], 1'b0 };
 
end endgenerate
 
//
// Bit reversal pre-logic
wire [31:0] w_brev_result;
genvar k;
generate
for(k=0; k<32; k=k+1)
begin : bit_reversal_cpuop
93,48 → 78,241
wire z, n, v;
reg c, pre_sign, set_ovfl, keep_sgn_on_ovfl;
always @(posedge i_clk)
if (i_stb) // 1 LUT
set_ovfl<=(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP
||((i_op==4'h2)&&(i_a[31] == i_b[31])) // ADD
||(i_op == 4'h6) // LSL
||(i_op == 4'h5)); // LSR
 
if (i_ce) // 1 LUT
set_ovfl<=(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP
||((i_op==4'h2)&&(i_a[31] == i_b[31])) // ADD
||(i_op == 4'h6) // LSL
||(i_op == 4'h5)); // LSR
always @(posedge i_clk)
if (i_stb) // 1 LUT
keep_sgn_on_ovfl<=
(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP
||((i_op==4'h2)&&(i_a[31] == i_b[31]))); // ADD
if (i_ce) // 1 LUT
keep_sgn_on_ovfl<=
(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP
||((i_op==4'h2)&&(i_a[31] == i_b[31]))); // ADD
 
wire [63:0] mpy_result; // Where we dump the multiply result
wire mpyhi; // Return the high half of the multiply
reg mpyhi; // Return the high half of the multiply
wire mpybusy; // The multiply is busy if true
wire mpydone; // True if we'll be valid on the next clock;
 
// A 4-way multiplexer can be done in one 6-LUT.
// A 16-way multiplexer can therefore be done in 4x 6-LUT's with
// the Xilinx multiplexer fabric that follows.
// the Xilinx multiplexer fabric that follows.
// Given that we wish to apply this multiplexer approach to 33-bits,
// this will cost a minimum of 132 6-LUTs.
 
wire this_is_a_multiply_op;
assign this_is_a_multiply_op = (i_stb)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc));
assign this_is_a_multiply_op = (i_ce)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc));
 
//
// Pull in the multiply logic from elsewhere
//
`ifdef FORMAL
`define MPYOP abs_mpy
generate
if (IMPLEMENT_MPY == 0)
begin // No multiply support.
assign mpy_result = 63'h00;
end else if (IMPLEMENT_MPY == 1)
begin // Our single clock option (no extra clocks)
wire signed [63:0] w_mpy_a_input, w_mpy_b_input;
assign w_mpy_a_input = {{(32){(i_a[31])&(i_op[0])}},i_a[31:0]};
assign w_mpy_b_input = {{(32){(i_b[31])&(i_op[0])}},i_b[31:0]};
assign mpy_result = w_mpy_a_input * w_mpy_b_input;
assign mpybusy = 1'b0;
assign mpydone = 1'b0;
always @(*) mpyhi = 1'b0; // Not needed
end else if (IMPLEMENT_MPY == 2)
begin // Our two clock option (ALU must pause for 1 clock)
reg signed [63:0] r_mpy_a_input, r_mpy_b_input;
always @(posedge i_clk)
begin
r_mpy_a_input <={{(32){(i_a[31])&(i_op[0])}},i_a[31:0]};
r_mpy_b_input <={{(32){(i_b[31])&(i_op[0])}},i_b[31:0]};
end
 
assign mpy_result = r_mpy_a_input * r_mpy_b_input;
assign mpybusy = 1'b0;
 
reg mpypipe;
initial mpypipe = 1'b0;
always @(posedge i_clk)
if (i_rst)
mpypipe <= 1'b0;
else
mpypipe <= (this_is_a_multiply_op);
 
assign mpydone = mpypipe; // this_is_a_multiply_op;
always @(posedge i_clk)
if (this_is_a_multiply_op)
mpyhi = i_op[1];
end else if (IMPLEMENT_MPY == 3)
begin // Our three clock option (ALU pauses for 2 clocks)
reg signed [63:0] r_smpy_result;
reg [63:0] r_umpy_result;
reg signed [31:0] r_mpy_a_input, r_mpy_b_input;
reg [1:0] mpypipe;
reg [1:0] r_sgn;
 
initial mpypipe = 2'b0;
always @(posedge i_clk)
if (i_rst)
mpypipe <= 2'b0;
else
mpypipe <= { mpypipe[0], this_is_a_multiply_op };
 
// First clock
always @(posedge i_clk)
begin
r_mpy_a_input <= i_a[31:0];
r_mpy_b_input <= i_b[31:0];
r_sgn <= { r_sgn[0], i_op[0] };
end
 
// Second clock
`ifdef VERILATOR
wire signed [63:0] s_mpy_a_input, s_mpy_b_input;
wire [63:0] u_mpy_a_input, u_mpy_b_input;
 
assign s_mpy_a_input = {{(32){r_mpy_a_input[31]}},r_mpy_a_input};
assign s_mpy_b_input = {{(32){r_mpy_b_input[31]}},r_mpy_b_input};
assign u_mpy_a_input = {32'h00,r_mpy_a_input};
assign u_mpy_b_input = {32'h00,r_mpy_b_input};
always @(posedge i_clk)
r_smpy_result = s_mpy_a_input * s_mpy_b_input;
always @(posedge i_clk)
r_umpy_result = u_mpy_a_input * u_mpy_b_input;
`else
`define MPYOP mpyop
 
wire [31:0] u_mpy_a_input, u_mpy_b_input;
 
assign u_mpy_a_input = r_mpy_a_input;
assign u_mpy_b_input = r_mpy_b_input;
 
always @(posedge i_clk)
r_smpy_result = r_mpy_a_input * r_mpy_b_input;
always @(posedge i_clk)
r_umpy_result = u_mpy_a_input * u_mpy_b_input;
`endif
`MPYOP #(.IMPLEMENT_MPY(IMPLEMENT_MPY)) thempy(i_clk, i_reset, this_is_a_multiply_op, i_op[1:0],
i_a, i_b, mpydone, mpybusy, mpy_result, mpyhi);
 
always @(posedge i_clk)
if (this_is_a_multiply_op)
mpyhi = i_op[1];
assign mpybusy = mpypipe[0];
assign mpy_result = (r_sgn[1])?r_smpy_result:r_umpy_result;
assign mpydone = mpypipe[1];
 
// Results are then set on the third clock
end else // if (IMPLEMENT_MPY <= 4)
begin // The three clock option
reg [63:0] r_mpy_result;
reg [31:0] r_mpy_a_input, r_mpy_b_input;
reg r_mpy_signed;
reg [2:0] mpypipe;
 
// First clock, latch in the inputs
initial mpypipe = 3'b0;
always @(posedge i_clk)
begin
// mpypipe indicates we have a multiply in the
// pipeline. In this case, the multiply
// pipeline is a two stage pipeline, so we need
// two bits in the pipe.
if (i_rst)
mpypipe <= 3'h0;
else begin
mpypipe[0] <= this_is_a_multiply_op;
mpypipe[1] <= mpypipe[0];
mpypipe[2] <= mpypipe[1];
end
 
if (i_op[0]) // i.e. if signed multiply
begin
r_mpy_a_input <= {(~i_a[31]),i_a[30:0]};
r_mpy_b_input <= {(~i_b[31]),i_b[30:0]};
end else begin
r_mpy_a_input <= i_a[31:0];
r_mpy_b_input <= i_b[31:0];
end
// The signed bit really only matters in the
// case of 64 bit multiply. We'll keep track
// of it, though, and pretend in all other
// cases.
r_mpy_signed <= i_op[0];
 
if (this_is_a_multiply_op)
mpyhi = i_op[1];
end
 
assign mpybusy = |mpypipe[1:0];
assign mpydone = mpypipe[2];
 
// Second clock, do the multiplies, get the "partial
// products". Here, we break our input up into two
// halves,
//
// A = (2^16 ah + al)
// B = (2^16 bh + bl)
//
// and use these to compute partial products.
//
// AB = (2^32 ah*bh + 2^16 (ah*bl + al*bh) + (al*bl)
//
// Since we're following the FOIL algorithm to get here,
// we'll name these partial products according to FOIL.
//
// The trick is what happens if A or B is signed. In
// those cases, the real value of A will not be given by
// A = (2^16 ah + al)
// but rather
// A = (2^16 ah[31^] + al) - 2^31
// (where we have flipped the sign bit of A)
// and so ...
//
// AB= (2^16 ah + al - 2^31) * (2^16 bh + bl - 2^31)
// = 2^32(ah*bh)
// +2^16 (ah*bl+al*bh)
// +(al*bl)
// - 2^31 (2^16 bh+bl + 2^16 ah+al)
// - 2^62
// = 2^32(ah*bh)
// +2^16 (ah*bl+al*bh)
// +(al*bl)
// - 2^31 (2^16 bh+bl + 2^16 ah+al + 2^31)
//
reg [31:0] pp_f, pp_l; // F and L from FOIL
reg [32:0] pp_oi; // The O and I from FOIL
reg [32:0] pp_s;
always @(posedge i_clk)
begin
pp_f<=r_mpy_a_input[31:16]*r_mpy_b_input[31:16];
pp_oi<=r_mpy_a_input[31:16]*r_mpy_b_input[15: 0]
+ r_mpy_a_input[15: 0]*r_mpy_b_input[31:16];
pp_l<=r_mpy_a_input[15: 0]*r_mpy_b_input[15: 0];
// And a special one for the sign
if (r_mpy_signed)
pp_s <= 32'h8000_0000-(
r_mpy_a_input[31:0]
+ r_mpy_b_input[31:0]);
else
pp_s <= 33'h0;
end
 
// Third clock, add the results and produce a product
always @(posedge i_clk)
begin
r_mpy_result[15:0] <= pp_l[15:0];
r_mpy_result[63:16] <=
{ 32'h00, pp_l[31:16] }
+ { 15'h00, pp_oi }
+ { pp_s, 15'h00 }
+ { pp_f, 16'h00 };
end
 
assign mpy_result = r_mpy_result;
// Fourth clock -- results are clocked into writeback
end
endgenerate // All possible multiply results have been determined
 
//
// The master ALU case statement
//
always @(posedge i_clk)
if (i_stb)
if (i_ce)
begin
pre_sign <= (i_a[31]);
c <= 1'b0;
155,19 → 333,16
default: o_c <= i_b; // MOV, LDI
endcase
end else // if (mpydone)
// set the output based upon the multiply result
o_c <= (mpyhi)?mpy_result[63:32]:mpy_result[31:0];
 
reg r_busy;
initial r_busy = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_busy <= 1'b0;
else if (IMPLEMENT_MPY > 1)
r_busy <= ((i_stb)&&(this_is_a_multiply_op))||mpybusy;
else
r_busy <= 1'b0;
 
if (i_rst)
r_busy <= 1'b0;
else
r_busy <= ((IMPLEMENT_MPY > 1)
&&(this_is_a_multiply_op))||mpybusy;
assign o_busy = (r_busy); // ||((IMPLEMENT_MPY>1)&&(this_is_a_multiply_op));
 
 
180,108 → 355,11
 
initial o_valid = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_valid <= 1'b0;
else if (IMPLEMENT_MPY <= 1)
o_valid <= (i_stb);
else
o_valid <=((i_stb)&&(!this_is_a_multiply_op))||(mpydone);
if (i_rst)
o_valid <= 1'b0;
else if (IMPLEMENT_MPY <= 1)
o_valid <= (i_ce);
else
o_valid <=((i_ce)&&(!this_is_a_multiply_op))||(mpydone);
 
`ifdef FORMAL
initial assume(i_reset);
reg f_past_valid;
 
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid = 1'b1;
 
`define ASSERT assert
`ifdef CPUOPS
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
// No request should be given us if/while we are busy
always @(posedge i_clk)
if (o_busy)
`ASSUME(!i_stb);
 
// Following any request other than a multiply request, we should
// respond in the next cycle
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_busy))&&(!$past(this_is_a_multiply_op)))
`ASSERT(!o_busy);
 
// Valid and busy can never both be asserted
always @(posedge i_clk)
`ASSERT((!o_valid)||(!r_busy));
 
// Following any busy, we should always become valid
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_busy))&&(!o_busy))
`ASSERT($past(i_reset) || o_valid);
 
// Check the shift values
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_stb)))
begin
if (($past(|i_b[31:6]))||($past(i_b[5:0])>6'd32))
begin
assert(($past(i_op)!=4'h5)
||({o_c,c}=={(33){1'b0}}));
assert(($past(i_op)!=4'h6)
||({c,o_c}=={(33){1'b0}}));
assert(($past(i_op)!=4'h7)
||({o_c,c}=={(33){$past(i_a[31])}}));
end else if ($past(i_b[5:0]==6'd32))
begin
assert(($past(i_op)!=4'h5)
||(o_c=={(32){1'b0}}));
assert(($past(i_op)!=4'h6)
||(o_c=={(32){1'b0}}));
assert(($past(i_op)!=4'h7)
||(o_c=={(32){$past(i_a[31])}}));
end if ($past(i_b)==0)
begin
assert(($past(i_op)!=4'h5)
||({o_c,c}=={$past(i_a), 1'b0}));
assert(($past(i_op)!=4'h6)
||({c,o_c}=={1'b0, $past(i_a)}));
assert(($past(i_op)!=4'h7)
||({o_c,c}=={$past(i_a), 1'b0}));
end if ($past(i_b)==1)
begin
assert(($past(i_op)!=4'h5)
||({o_c,c}=={1'b0, $past(i_a)}));
assert(($past(i_op)!=4'h6)
||({c,o_c}=={$past(i_a),1'b0}));
assert(($past(i_op)!=4'h7)
||({o_c,c}=={$past(i_a[31]),$past(i_a)}));
end if ($past(i_b)==2)
begin
assert(($past(i_op)!=4'h5)
||({o_c,c}=={2'b0, $past(i_a[31:1])}));
assert(($past(i_op)!=4'h6)
||({c,o_c}=={$past(i_a[30:0]),2'b0}));
assert(($past(i_op)!=4'h7)
||({o_c,c}=={{(2){$past(i_a[31])}},$past(i_a[31:1])}));
end if ($past(i_b)==31)
begin
assert(($past(i_op)!=4'h5)
||({o_c,c}=={31'b0, $past(i_a[31:30])}));
assert(($past(i_op)!=4'h6)
||({c,o_c}=={$past(i_a[1:0]),31'b0}));
assert(($past(i_op)!=4'h7)
||({o_c,c}=={{(31){$past(i_a[31])}},$past(i_a[31:30])}));
end
end
`endif
endmodule
//
// iCE40 NoMPY,w/Shift NoMPY,w/o Shift
// SB_CARRY 64 64
// SB_DFFE 3 3
// SB_DFFESR 1 1
// SB_DFFSR 33 33
// SB_LUT4 748 323
/dblfetch.v
6,24 → 6,35
//
// Purpose: This is one step beyond the simplest instruction fetch,
// prefetch.v. dblfetch.v uses memory pipelining to fetch two
// (or more) instruction words in one bus cycle. If the CPU consumes
// either of these before the bus cycle completes, a new request will be
// made of the bus. In this way, we can keep the CPU filled in spite
// of a (potentially) slow memory operation. The bus request will end
// when both requests have been sent and both result locations are empty.
// instruction words in one cycle, figuring that the unpipelined CPU can't
// go through both at once, but yet recycles itself fast enough for the
// next instruction that would follow. It is designed to be a touch
// faster than the single instruction prefetch, although not as fast as
// the prefetch and cache found elsewhere.
//
// This routine is designed to be a touch faster than the single
// instruction prefetch (prefetch.v), although not as fast as the
// prefetch and cache approach found elsewhere (pfcache.v).
// There are some gotcha's in this logic, however. For example, it's
// illegal to switch devices mid-transaction, since the second device
// might have different timing. I.e. the first device might take 8
// clocks to create an ACK, and the second device might take 2 clocks, the
// acks might therefore come on top of each other, or even out of order.
// But ... in order to keep logic down, we keep track of the PC in the
// o_wb_addr register. Hence, this register gets changed on any i_new_pc.
// The i_pc value associated with i_new_pc will only be valid for one
// clock, hence we can't wait to change. To keep from violating the WB
// rule, therefore, we *must* immediately stop requesting any transaction,
// and then terminate the bus request as soon as possible.
//
// 20180222: Completely rebuilt.
// This has consequences in terms of logic used, leaving this routine
// anything but simple--even though the number of wires affected by
// this is small (o_wb_cyc, o_wb_stb, and last_ack).
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017-2019, Gisselquist Technology, LLC
// Copyright (C) 2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
47,29 → 58,27
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module dblfetch(i_clk, i_reset, i_new_pc, i_clear_cache,
i_stall_n, i_pc, o_insn, o_pc, o_valid,
module dblfetch(i_clk, i_rst, i_new_pc, i_clear_cache,
i_stall_n, i_pc, o_i, o_pc, o_v,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data,
o_illegal);
parameter ADDRESS_WIDTH=30, AUX_WIDTH = 1;
localparam AW=ADDRESS_WIDTH, DW = 32;
input wire i_clk, i_reset, i_new_pc, i_clear_cache,
parameter ADDRESS_WIDTH=32, AUX_WIDTH = 1;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst, i_new_pc, i_clear_cache,
i_stall_n;
input wire [(AW+1):0] i_pc;
output reg [(DW-1):0] o_insn;
output reg [(AW+1):0] o_pc;
output reg o_valid;
input [(AW-1):0] i_pc;
output reg [31:0] o_i;
output reg [(AW-1):0] o_pc;
output wire o_v;
// Wishbone outputs
output reg o_wb_cyc, o_wb_stb;
output wire o_wb_we;
output reg [(AW-1):0] o_wb_addr;
output wire [(DW-1):0] o_wb_data;
output wire [31:0] o_wb_data;
// And return inputs
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(DW-1):0] i_wb_data;
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
// And ... the result if we got an error
output reg o_illegal;
 
76,67 → 85,87
assign o_wb_we = 1'b0;
assign o_wb_data = 32'h0000;
 
reg last_stb, invalid_bus_cycle;
reg last_ack, last_stb, invalid_bus_cycle;
 
reg [(DW-1):0] cache_word;
reg cache_valid;
reg [1:0] inflight;
reg cache_illegal;
reg [31:0] cache [0:1];
reg cache_read_addr, cache_write_addr;
reg [1:0] cache_valid;
 
initial o_wb_cyc = 1'b0;
initial o_wb_stb = 1'b0;
always @(posedge i_clk)
if ((i_reset)||((o_wb_cyc)&&(i_wb_err)))
if ((i_rst)||(i_wb_err))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
// last_stb <= 1'b0;
// last_ack <= 1'b0;
end else if (o_wb_cyc)
begin
if ((!o_wb_stb)||(!i_wb_stall))
o_wb_stb <= (!last_stb);
if ((o_wb_stb)&&(!i_wb_stall))
begin
// last_stb <= 1'b1;
o_wb_stb <= !last_stb;
end
// if (i_wb_ack)
// last_ack <= 1'b1;
if ((i_new_pc)||(invalid_bus_cycle))
o_wb_stb <= 1'b0;
 
// Relase the bus on the second ack
if (((i_wb_ack)&&(!o_wb_stb)&&(inflight<=1))
||((!o_wb_stb)&&(inflight == 0))
// Or any new transaction request
||((i_new_pc)||(i_clear_cache)))
if ((i_wb_ack)&&(
// Relase the bus on the second ack
(last_ack)
// Or on the first ACK, if we've been told
// we have an invalid bus cycle
||((o_wb_stb)&&(i_wb_stall)&&(last_stb)&&(
(i_new_pc)||(invalid_bus_cycle)))
))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end
 
end else if ((i_new_pc)||(invalid_bus_cycle)
||((o_valid)&&(i_stall_n)&&(!o_illegal)))
if ((!last_stb)&&(i_wb_stall)&&((i_new_pc)||(invalid_bus_cycle)))
// Also release the bus with no acks, if we
// haven't made any requests
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end
end else if ((invalid_bus_cycle)
||((o_v)&&(i_stall_n)&&(cache_read_addr))) // Initiate a bus cycle
begin
// Initiate a bus cycle if ... the last bus cycle was
// aborted (bus error or new_pc), we've been given a
// new PC to go get, or we just exhausted our one
// instruction cache
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
// last_stb <= 1'b0;
// last_ack <= 1'b0;
end
 
initial inflight = 2'b00;
initial last_stb = 1'b0;
always @(posedge i_clk)
if (!o_wb_cyc)
inflight <= 2'b00;
else begin
case({ ((o_wb_stb)&&(!i_wb_stall)), i_wb_ack })
2'b01: inflight <= inflight - 1'b1;
2'b10: inflight <= inflight + 1'b1;
// If neither ack nor request, then no change. Likewise
// if we have both an ack and a request, there's no change
// in the number of requests in flight.
default: begin end
endcase
end
if ((o_wb_cyc)&&(o_wb_stb)&&(!i_wb_stall))
last_stb <= 1'b1;
else if (!o_wb_cyc)
last_stb <= 1'b0;
 
always @(*)
last_stb = (inflight != 2'b00)||((o_valid)&&(!i_stall_n));
initial last_ack = 1'b0;
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
last_ack <= 1'b1;
else if ((o_wb_cyc)&&(o_wb_stb)&&(i_wb_stall)&&(
(i_new_pc)||(invalid_bus_cycle)))
last_ack <= 1'b1;
else if ((o_wb_cyc)&&(o_wb_stb)&&(!i_wb_stall)&&(!last_stb)&&(
(i_new_pc)||(invalid_bus_cycle)))
last_ack <= 1'b1;
else if (!o_wb_cyc)
last_ack <= 1'b0;
 
initial invalid_bus_cycle = 1'b0;
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_new_pc))
if (i_rst)
invalid_bus_cycle <= 1'b0;
else if ((i_new_pc)||(i_clear_cache))
invalid_bus_cycle <= 1'b1;
else if (!o_wb_cyc)
invalid_bus_cycle <= 1'b0;
144,573 → 173,60
initial o_wb_addr = {(AW){1'b1}};
always @(posedge i_clk)
if (i_new_pc)
o_wb_addr <= i_pc[AW+1:2];
else if ((o_wb_stb)&&(!i_wb_stall))
o_wb_addr <= i_pc;
else if ((o_wb_stb)&&(!i_wb_stall)&&(!invalid_bus_cycle))
o_wb_addr <= o_wb_addr + 1'b1;
 
//////////////////
//
// Now for the immediate output word to the CPU
//
//////////////////
 
initial o_valid = 1'b0;
initial cache_write_addr = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_new_pc)||(i_clear_cache))
o_valid <= 1'b0;
else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
o_valid <= 1'b1;
else if (i_stall_n)
o_valid <= cache_valid;
if (!o_wb_cyc)
cache_write_addr <= 1'b0;
else if ((o_wb_cyc)&&(i_wb_ack))
cache_write_addr <= cache_write_addr + 1'b1;
 
always @(posedge i_clk)
if ((!o_valid)||(i_stall_n))
begin
if (cache_valid)
o_insn <= cache_word;
else
o_insn <= i_wb_data;
end
if ((o_wb_cyc)&&(i_wb_ack))
cache[cache_write_addr] <= i_wb_data;
 
initial o_pc[1:0] = 2'b00;
initial cache_read_addr = 1'b0;
always @(posedge i_clk)
if (i_new_pc)
o_pc <= i_pc;
else if ((o_valid)&&(i_stall_n))
o_pc[AW+1:2] <= o_pc[AW+1:2] + 1'b1;
if ((i_new_pc)||(invalid_bus_cycle)
||((o_v)&&(cache_read_addr)&&(i_stall_n)))
cache_read_addr <= 1'b0;
else if ((o_v)&&(i_stall_n))
cache_read_addr <= 1'b1;
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_new_pc)||(i_clear_cache))
o_illegal <= 1'b0;
else if ((!o_valid)||(i_stall_n))
begin
if (cache_valid)
o_illegal <= (o_illegal)||(cache_illegal);
else if ((o_wb_cyc)&&(i_wb_err))
o_illegal <= 1'b1;
end
 
 
//////////////////
//
// Now for the output/cached word
//
//////////////////
 
initial cache_valid = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_new_pc)||(i_clear_cache))
cache_valid <= 1'b0;
if ((i_new_pc)||(invalid_bus_cycle))
cache_valid <= 2'b00;
else begin
if ((o_valid)&&(o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
cache_valid <= (!i_stall_n)||(cache_valid);
else if (i_stall_n)
cache_valid <= 1'b0;
if ((o_v)&&(i_stall_n))
cache_valid[cache_read_addr] <= 1'b0;
if ((o_wb_cyc)&&(i_wb_ack))
cache_valid[cache_write_addr] <= 1'b1;
end
 
initial o_i = {(32){1'b1}};
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
cache_word <= i_wb_data;
 
initial cache_illegal = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache)||(i_new_pc))
cache_illegal <= 1'b0;
else if ((o_wb_cyc)&&(i_wb_err)&&(o_valid)&&(!i_stall_n))
cache_illegal <= 1'b1;
//
// Some of these properties can be done in yosys-smtbmc, *or* Verilator
//
// Ver1lator is different from yosys, however, in that Verilator doesn't support
// the $past() directive. Further, any `assume`'s turn into `assert()`s
// within Verilator. We can use this to help prove that the properties
// of interest truly hold, and that any contracts we create or assumptions we
// make truly hold in practice (i.e. in simulation).
//
`ifdef FORMAL
`define VERILATOR_FORMAL
`else
`ifdef VERILATOR
//
// Define VERILATOR_FORMAL here to have Verilator check your formal properties
// during simulation. assert() and assume() statements will both have the
// same effect within VERILATOR of causing your simulation to suddenly end.
//
// I have this property commented because it only works on the newest versions
// of Verilator (3.9 something and later), and I tend to still use Verilator
// 3.874.
//
// `define VERILATOR_FORMAL
`endif
`endif
 
`ifdef VERILATOR_FORMAL
// Keep track of a flag telling us whether or not $past()
// will return valid results
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid = 1'b1;
 
// Keep track of some alternatives to $past that can still be used
// in a VERILATOR environment
reg f_past_reset, f_past_clear_cache, f_past_o_valid,
f_past_stall_n;
 
initial f_past_reset = 1'b1;
initial f_past_clear_cache = 1'b0;
initial f_past_o_valid = 1'b0;
initial f_past_stall_n = 1'b1;
always @(posedge i_clk)
begin
f_past_reset <= i_reset;
f_past_clear_cache <= i_clear_cache;
f_past_o_valid <= o_valid;
f_past_stall_n <= i_stall_n;
end
`endif
 
`ifdef FORMAL
//
//
// Generic setup
//
//
`ifdef DBLFETCH
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
/////////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
/////////////////////////////////////////////////
 
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
 
//
// Assume that resets, new-pc commands, and clear-cache commands
// are never more than pulses--one clock wide at most.
//
// It may be that the CPU treats us differently. We'll only restrict
// our solver to this here.
/*
always @(posedge i_clk)
if (f_past_valid)
begin
if (f_past_reset)
restrict(!i_reset);
if ($past(i_new_pc))
restrict(!i_new_pc);
end
*/
 
//
// Assume we start from a reset condition
initial assume(i_reset);
 
/////////////////////////////////////////////////
//
//
// Wishbone bus properties
//
//
/////////////////////////////////////////////////
 
localparam F_LGDEPTH=2;
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
 
//
// Add a bunch of wishbone-based asserts
fwb_master #(.AW(AW), .DW(DW), .F_LGDEPTH(F_LGDEPTH),
.F_MAX_STALL(2),
.F_MAX_REQUESTS(0), .F_OPT_SOURCE(1),
.F_OPT_RMW_BUS_OPTION(1),
.F_OPT_DISCONTINUOUS(0))
f_wbm(i_clk, i_reset,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
`endif
 
//
// Now, apply the following to Verilator *or* yosys-smtbmc
//
`ifdef VERILATOR_FORMAL
/////////////////////////////////////////////////
//
//
// Assumptions about our interaction with the CPU
//
//
/////////////////////////////////////////////////
 
// Assume that any reset is either accompanied by a new address,
// or a new address immediately follows it.
always @(posedge i_clk)
if ((f_past_valid)&&(f_past_reset))
assume(i_new_pc);
 
always @(posedge i_clk)
if (f_past_clear_cache)
assume(!i_clear_cache);
 
//
//
// The bottom two bits of the PC address register are always zero.
// They are there to make examining traces easier, but I expect
// the synthesis tool to remove them.
//
always @(*)
assume(i_pc[1:0] == 2'b00);
 
// Some things to know from the CPU ... there will always be a
// i_new_pc request following any reset
always @(posedge i_clk)
if ((f_past_valid)&&(f_past_reset))
assume(i_new_pc);
 
// There will also be a i_new_pc request following any request to clear
// the cache.
always @(posedge i_clk)
if ((f_past_valid)&&(f_past_clear_cache))
assume(i_new_pc);
 
always @(posedge i_clk)
if (f_past_clear_cache)
assume(!i_clear_cache);
 
always @(*)
assume(i_pc[1:0] == 2'b00);
`endif
 
`ifdef FORMAL
//
// Let's make some assumptions about how long it takes our phantom
// (i.e. assumed) CPU to respond.
//
// This delay needs to be long enough to flush out any potential
// errors, yet still short enough that the formal method doesn't
// take forever to solve.
//
`ifdef DBLFETCH
localparam F_CPU_DELAY = 4;
reg [4:0] f_cpu_delay;
 
// Now, let's look at the delay the CPU takes to accept an instruction.
always @(posedge i_clk)
// If no instruction is ready, then keep our counter at zero
if ((!o_valid)||(i_stall_n))
f_cpu_delay <= 0;
if ((i_stall_n)&&(o_wb_cyc)&&(i_wb_ack))
o_i <= i_wb_data;
else
// Otherwise, count the clocks the CPU takes to respond
f_cpu_delay <= f_cpu_delay + 1'b1;
o_i <= cache[cache_read_addr];
 
initial o_pc = 0;
always @(posedge i_clk)
assume(f_cpu_delay < F_CPU_DELAY);
`endif
if (i_new_pc)
o_pc <= i_pc;
else if ((o_v)&&(i_stall_n))
o_pc <= o_pc + 1'b1;
 
assign o_v = cache_valid[cache_read_addr];
 
 
/////////////////////////////////////////////////
//
//
// Assertions about our outputs
//
//
/////////////////////////////////////////////////
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_stb))&&(!$past(i_wb_stall))
&&(!$past(i_new_pc)))
assert(o_wb_addr <= $past(o_wb_addr)+1'b1);
if ((o_wb_cyc)&&(i_wb_err))
o_illegal <= 1'b1;
else if ((!o_wb_cyc)&&((i_new_pc)||(invalid_bus_cycle)))
o_illegal <= 1'b0;
 
//
// Assertions about our return responses to the CPU
//
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(i_new_pc))&&(!$past(i_clear_cache))
&&($past(o_valid))&&(!$past(i_stall_n)))
begin
assert($stable(o_pc));
assert($stable(o_insn));
assert($stable(o_valid));
assert($stable(o_illegal));
end
 
// The same is true of the cache as well.
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(i_new_pc))&&(!$past(i_clear_cache))
&&($past(o_valid))&&(!$past(i_stall_n))
&&($past(cache_valid)))
begin
assert($stable(cache_valid));
assert($stable(cache_word));
assert($stable(cache_illegal));
end
 
// Consider it invalid to present the CPU with the same instruction
// twice in a row. Any effort to present the CPU with the same
// instruction twice in a row must go through i_new_pc, and thus a
// new bus cycle--hence the assertion below makes sense.
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_new_pc))
&&($past(o_valid))&&($past(i_stall_n)))
assert(o_pc[AW+1:2] == $past(o_pc[AW+1:2])+1'b1);
 
 
//
// As with i_pc[1:0], the bottom two bits of the address are unused.
// Let's assert here that they remain zero.
always @(*)
assert(o_pc[1:0] == 2'b00);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(i_new_pc))
&&(!$past(i_clear_cache))
&&($past(o_wb_cyc))&&($past(i_wb_err)))
assert( ((o_valid)&&(o_illegal))
||((cache_valid)&&(cache_illegal)) );
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_illegal))&&(o_illegal))
assert(o_valid);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(cache_illegal))&&(!cache_valid))
assert(!cache_illegal);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_new_pc)))
assert(!o_valid);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_clear_cache))
&&($past(o_valid))&&(!o_valid)&&(!o_illegal))
assert((o_wb_cyc)||(invalid_bus_cycle));
 
/////////////////////////////////////////////////
//
//
// Our "contract" with the CPU
//
//
/////////////////////////////////////////////////
//
// For any particular address, that address is associated with an
// instruction and a flag regarding whether or not it is illegal.
//
// Any attempt to return to the CPU a value from this address,
// must return the value and the illegal flag.
//
(* anyconst *) reg [AW-1:0] f_const_addr;
(* anyconst *) reg [DW-1:0] f_const_insn;
(* anyconst *) reg f_const_illegal;
 
//
// While these wires may seem like overkill, and while they make the
// following logic perhaps a bit more obscure, these predicates make
// it easier to follow the complex logic on a scope. They don't
// affect anything synthesized.
//
wire f_this_addr, f_this_pc, f_this_req, f_this_data,
f_this_insn;
 
assign f_this_addr = (o_wb_addr == f_const_addr);
assign f_this_pc = (o_pc == { f_const_addr, 2'b00 });
assign f_this_req = (i_pc == { f_const_addr, 2'b00 });
assign f_this_data = (i_wb_data == f_const_insn);
assign f_this_insn = (o_insn == f_const_insn);
 
 
//
//
// Here's our contract:
//
// Any time we return a value for the address above, it *must* be
// the "right" value.
//
always @(*)
if ((o_valid)&&(f_this_pc))
begin
if (f_const_illegal)
assert(o_illegal);
if (!o_illegal)
assert(f_this_insn);
end
 
//
// The contract will only work if we assume the return from the
// bus at this address will be the right return.
wire f_this_return;
assign f_this_return = (o_wb_addr - f_outstanding == f_const_addr);
always @(*)
if ((o_wb_cyc)&&(f_this_return))
begin
if (i_wb_ack)
assume(i_wb_data == f_const_insn);
 
if (f_const_illegal)
assume(!i_wb_ack);
else
assume(!i_wb_err);
end
 
//
// Here is a corrollary to our contract. Anything in the one-word
// cache must also match the contract as well.
//
always @(*)
if ((o_pc[AW+1:2] + 1'b1 == f_const_addr)&&(cache_valid))
begin
if (!cache_illegal)
assert(cache_word == f_const_insn);
 
if (f_const_illegal)
assert(cache_illegal);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(cache_illegal))&&(!cache_valid))
assert(!cache_illegal);
 
////////////////////////////////////////////////////////
//
//
// Additional assertions necessary to pass induction
//
//
////////////////////////////////////////////////////////
//
// We have only a one word cache. Hence, we shouldn't be asking
// for more data any time we have nowhere to put it.
always @(*)
if (o_wb_stb)
assert((!cache_valid)||(i_stall_n));
 
always @(*)
if ((o_valid)&&(cache_valid))
assert((f_outstanding == 0)&&(!o_wb_stb));
 
always @(*)
if ((o_valid)&&(!i_stall_n))
assert(f_outstanding < 2);
 
always @(*)
if ((!o_valid)||(i_stall_n))
assert(f_outstanding <= 2);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(!$past(o_wb_stb))
&&(o_wb_cyc))
assert(inflight != 0);
 
always @(*)
if ((o_wb_cyc)&&(i_wb_ack))
assert(!cache_valid);
 
always @(posedge i_clk)
if (o_wb_cyc)
assert(inflight == f_outstanding);
 
wire [AW-1:0] this_return_address,
next_pc_address;
assign this_return_address = o_wb_addr - f_outstanding;
assign next_pc_address = o_pc[AW+1:2] + 1'b1;
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))
&&(!$past(i_reset))
&&(!$past(i_new_pc))
&&(!$past(i_clear_cache))
&&(!$past(invalid_bus_cycle))
&&(($past(i_wb_ack))||($past(i_wb_err)))
&&((!$past(o_valid))||($past(i_stall_n)))
&&(!$past(cache_valid)))
assert(o_pc[AW+1:2] == $past(this_return_address));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(!o_valid)&&(!$past(i_new_pc))
&&(o_wb_cyc))
assert(o_pc[AW+1:2] == this_return_address);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))
&&(!$past(cache_valid))&&(cache_valid))
assert(next_pc_address == $past(this_return_address));
 
 
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc))
begin
if ((o_valid)&&(!cache_valid))
assert(this_return_address == next_pc_address);
else if (!o_valid)
assert(this_return_address == o_pc[AW+1:2]);
end else if ((f_past_valid)&&(!invalid_bus_cycle)
&&(!o_wb_cyc)&&(o_valid)&&(!o_illegal)
&&(!cache_valid))
assert(o_wb_addr == next_pc_address);
 
 
always @(*)
if (invalid_bus_cycle)
assert(!o_wb_cyc);
always @(*)
if (cache_valid)
assert(o_valid);
 
/////////////////////////////////////////////////////
//
//
// Cover statements
//
//
/////////////////////////////////////////////////////
 
always @(posedge i_clk)
cover((f_past_valid)&&($past(f_nacks)==3)
&&($past(i_wb_ack))&&($past(o_wb_cyc)));
 
 
/////////////////////////////////////////////////////
//
//
// Temporary simplifications
//
//
/////////////////////////////////////////////////////
 
// always @(*)
// assume((!i_wb_err)&&(!f_const_illegal));
 
 
`endif // FORMAL
endmodule
//
// Usage: (this) (prior) (old) (S6)
// Cells 374 387 585 459
// FDRE 135 108 203 171
// LUT1 2 3 2
// LUT2 9 3 4 5
// LUT3 98 76 104 71
// LUT4 2 0 2 2
// LUT5 3 35 35 3
// LUT6 6 5 10 43
// MUXCY 58 62 93 62
// MUXF7 1 0 2 3
// MUXF8 0 1 1
// RAM64X1D 0 32 32 32
// XORCY 60 64 96 64
//
/div.v
8,10 → 8,10
// for both signed and unsigned divide.
//
// Steps:
// i_reset The DIVide unit starts in idle. It can also be placed into an
// i_rst The DIVide unit starts in idle. It can also be placed into an
// idle by asserting the reset input.
//
// i_wr When i_reset is asserted, a divide begins. On the next clock:
// i_wr When i_rst is asserted, a divide begins. On the next clock:
//
// o_busy is set high so everyone else knows we are at work and they can
// wait for us to complete.
71,7 → 71,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
95,17 → 95,15
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
// `include "cpudefs.v"
//
module div(i_clk, i_reset, i_wr, i_signed, i_numerator, i_denominator,
module div(i_clk, i_rst, i_wr, i_signed, i_numerator, i_denominator,
o_busy, o_valid, o_err, o_quotient, o_flags);
parameter BW=32, LGBW = 5;
input wire i_clk, i_reset;
input i_clk, i_rst;
// Input parameters
input wire i_wr, i_signed;
input wire [(BW-1):0] i_numerator, i_denominator;
input i_wr, i_signed;
input [(BW-1):0] i_numerator, i_denominator;
// Output parameters
output reg o_busy, o_valid, o_err;
output reg [(BW-1):0] o_quotient;
115,10 → 113,11
// before we are valid, so it can't be o_busy ...
//
reg r_busy;
reg [BW-1:0] r_divisor;
reg [(2*BW-2):0] r_dividend;
reg [(2*BW-2):0] r_divisor;
reg [(BW-1):0] r_dividend;
wire [(BW):0] diff; // , xdiff[(BW-1):0];
assign diff = r_dividend[2*BW-2:BW-1] - r_divisor;
assign diff = r_dividend - r_divisor[(BW-1):0];
// assign xdiff= r_dividend - { 1'b0, r_divisor[(BW-1):1] };
 
reg r_sign, pre_sign, r_z, r_c, last_bit;
reg [(LGBW-1):0] r_bit;
131,12 → 130,12
// or equivalently when we discover we are dividing by zero.
initial r_busy = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_busy <= 1'b0;
else if (i_wr)
r_busy <= 1'b1;
else if ((last_bit)||(zero_divisor))
r_busy <= 1'b0;
if (i_rst)
r_busy <= 1'b0;
else if (i_wr)
r_busy <= 1'b1;
else if ((last_bit)||(zero_divisor))
r_busy <= 1'b0;
 
// o_busy is very similar to r_busy, save for some key differences.
// Primary among them is that o_busy needs to (possibly) be true
146,18 → 145,32
// identical.
initial o_busy = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_busy <= 1'b0;
else if (i_wr)
o_busy <= 1'b1;
else if (((last_bit)&&(!r_sign))||(zero_divisor))
o_busy <= 1'b0;
else if (!r_busy)
o_busy <= 1'b0;
if (i_rst)
o_busy <= 1'b0;
else if (i_wr)
o_busy <= 1'b1;
else if (((last_bit)&&(~r_sign))||(zero_divisor))
o_busy <= 1'b0;
else if (~r_busy)
o_busy <= 1'b0;
 
// If we are asked to divide by zero, we need to halt. The sooner
// we halt and report the error, the better. Hence, here we look
// for a zero divisor while being busy. The always above us will then
// look at this and halt a divide in the middle if we are trying to
// divide by zero.
//
// Note that this works off of the 2BW-1 length vector. If we can
// simplify that, it should simplify our logic as well.
initial zero_divisor = 1'b0;
always @(posedge i_clk)
if (i_wr)
zero_divisor <= (i_denominator == 0);
// zero_divisor <= (r_divisor == 0)&&(r_busy);
if (i_rst)
zero_divisor <= 1'b0;
else if (i_wr)
zero_divisor <= (i_denominator == 0);
else if (!r_busy)
zero_divisor <= 1'b0;
 
// o_valid is part of the ZipCPU protocol. It will be set to true
// anytime our answer is valid and may be used by the calling module.
170,19 → 183,17
// it on an i_wr signal.
initial o_valid = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(o_valid))
o_valid <= 1'b0;
else if ((r_busy)&&(zero_divisor))
o_valid <= 1'b1;
else if (r_busy)
begin
if (last_bit)
o_valid <= (!r_sign);
end else if (r_sign)
begin
o_valid <= 1'b1;
end else
o_valid <= 1'b0;
if (i_rst)
o_valid <= 1'b0;
else if (r_busy)
begin
if ((last_bit)||(zero_divisor))
o_valid <= (zero_divisor)||(!r_sign);
end else if (r_sign)
begin
o_valid <= (!zero_divisor); // 1'b1;
end else
o_valid <= 1'b0;
 
// Division by zero error reporting. Anytime we detect a zero divisor,
// we set our output error, and then hold it until we are valid and
189,12 → 200,12
// everything clears.
initial o_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_err <= 1'b0;
else if ((r_busy)&&(zero_divisor))
o_err <= 1'b1;
else
o_err <= 1'b0;
if((i_rst)||(o_valid))
o_err <= 1'b0;
else if (((r_busy)||(r_sign))&&(zero_divisor))
o_err <= 1'b1;
else
o_err <= 1'b0;
 
// r_bit
//
202,31 → 213,26
// ranges from 31 down to zero. On any write, we set ourselves to
// 5'h1f. Otherwise, while we are busy (but not within the pre-sign
// adjustment stage), we subtract one from our value on every clock.
initial r_bit = 0;
always @(posedge i_clk)
if (i_reset)
r_bit <= 0;
else if ((r_busy)&&(!pre_sign))
r_bit <= r_bit + 1'b1;
else
r_bit <= 0;
if ((r_busy)&&(!pre_sign))
r_bit <= r_bit + {(LGBW){1'b1}};
else
r_bit <= {(LGBW){1'b1}};
 
// last_bit
//
// This logic replaces a lot of logic that was inside our giant state
// machine with ... something simpler. In particular, we'll use this
// logic to determine if we are processing our last bit. The only trick
// is, this bit needs to be set whenever (r_busy) and (r_bit == -1),
// hence we need to set on (r_busy) and (r_bit == -2) so as to be set
// logic to determine we are processing our last bit. The only trick
// is, this bit needs to be set whenever (r_busy) and (r_bit == 0),
// hence we need to set on (r_busy) and (r_bit == 1) so as to be set
// when (r_bit == 0).
initial last_bit = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_bit <= 1'b0;
else if (r_busy)
last_bit <= (r_bit == {(LGBW){1'b1}}-1'b1);
else
last_bit <= 1'b0;
if (r_busy)
last_bit <= (r_bit == {{(LGBW-1){1'b0}},1'b1});
else
last_bit <= 1'b0;
 
// pre_sign
//
235,59 → 241,67
// be true for the one clock, and then it must clear itself.
initial pre_sign = 1'b0;
always @(posedge i_clk)
if (i_reset)
pre_sign <= 1'b0;
else
pre_sign <= (i_wr)&&(i_signed)&&((i_numerator[BW-1])||(i_denominator[BW-1]));
if (i_wr)
pre_sign <= i_signed;
else
pre_sign <= 1'b0;
 
// As a result of our operation, we need to set the flags. The most
// difficult of these is the "Z" flag indicating that the result is
// zero. Here, we'll use the same logic that sets the low-order
// bit to clear our zero flag, and leave the zero flag set in all
// other cases.
// other cases. Well ... not quite. If we need to flip the sign of
// our value, then we can't quite clear the zero flag ... yet.
always @(posedge i_clk)
if (i_wr)
r_z <= 1'b1;
else if ((r_busy)&&(!pre_sign)&&(!diff[BW]))
r_z <= 1'b0;
if((r_busy)&&(r_divisor[(2*BW-2):(BW)] == 0)&&(!diff[BW]))
// If we are busy, the upper bits of our divisor are
// zero (i.e., we got the shift right), and the top
// (carry) bit of the difference is zero (no overflow),
// then we could subtract our divisor from our dividend
// and hence we add a '1' to the quotient, while setting
// the zero flag to false.
r_z <= 1'b0;
else if ((!r_busy)&&(!r_sign))
r_z <= 1'b1;
 
// r_dividend
// This is initially the numerator. On a signed divide, it then becomes
// the absolute value of the numerator. We'll subtract from this value
// the divisor for every output bit we are looking for--just as with
// traditional long division.
// the divisor shifted as appropriate for every output bit we are
// looking for--just as with traditional long division.
always @(posedge i_clk)
if (pre_sign)
begin
// If we are doing a signed divide, then take the
// absolute value of the dividend
if (r_dividend[BW-1])
if (pre_sign)
begin
r_dividend[2*BW-2:0] <= {(2*BW-1){1'b1}};
r_dividend[BW-1:0] <= -r_dividend[BW-1:0];
end
end else if (r_busy)
begin
r_dividend <= { r_dividend[2*BW-3:0], 1'b0 };
if (!diff[BW])
r_dividend[2*BW-2:BW] <= diff[(BW-2):0];
end else if (!r_busy)
// Once we are done, and r_busy is no longer high, we'll
// always accept new values into our dividend. This
// guarantees that, when i_wr is set, the new value
// is already set as desired.
r_dividend <= { 31'h0, i_numerator };
// If we are doing a signed divide, then take the
// absolute value of the dividend
if (r_dividend[BW-1])
r_dividend <= -r_dividend;
// The begin/end block is important so we don't lose
// the fact that on an else we don't do anything.
end else if((r_busy)&&(r_divisor[(2*BW-2):(BW)]==0)&&(!diff[BW]))
// This is the condition whereby we set a '1' in our
// output quotient, and we subtract the (current)
// divisor from our dividend. (The difference is
// already kept in the diff vector above.)
r_dividend <= diff[(BW-1):0];
else if (!r_busy)
// Once we are done, and r_busy is no longer high, we'll
// always accept new values into our dividend. This
// guarantees that, when i_wr is set, the new value
// is already set as desired.
r_dividend <= i_numerator;
 
initial r_divisor = 0;
always @(posedge i_clk)
if (i_reset)
r_divisor <= 0;
else if ((pre_sign)&&(r_busy))
begin
if (r_divisor[BW-1])
r_divisor <= -r_divisor;
end else if (!r_busy)
r_divisor <= i_denominator;
if (pre_sign)
begin
if (r_divisor[(2*BW-2)])
r_divisor[(2*BW-2):(BW-1)]
<= -r_divisor[(2*BW-2):(BW-1)];
end else if (r_busy)
r_divisor <= { 1'b0, r_divisor[(2*BW-2):1] };
else
r_divisor <= { i_denominator, {(BW-1){1'b0}} };
 
// r_sign
// is a flag for our state machine control(s). r_sign will be set to
300,38 → 314,31
// up to something.
initial r_sign = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_sign <= 1'b0;
else if (pre_sign)
r_sign <= ((r_divisor[(BW-1)])^(r_dividend[(BW-1)]));
else if (r_busy)
r_sign <= (r_sign)&&(!zero_divisor);
else
r_sign <= 1'b0;
if (pre_sign)
r_sign <= ((r_divisor[(2*BW-2)])^(r_dividend[(BW-1)]));
else if (r_busy)
r_sign <= (r_sign)&&(!zero_divisor);
else
r_sign <= 1'b0;
 
initial o_quotient = 0;
always @(posedge i_clk)
if (i_reset)
o_quotient <= 0;
else if (r_busy)
begin
o_quotient <= { o_quotient[(BW-2):0], 1'b0 };
if (!diff[BW])
o_quotient[0] <= 1'b1;
end else if (r_sign)
o_quotient <= -o_quotient;
else
o_quotient <= 0;
if (r_busy)
begin
o_quotient <= { o_quotient[(BW-2):0], 1'b0 };
if ((r_divisor[(2*BW-2):(BW)] == 0)&&(!diff[BW]))
begin
o_quotient[0] <= 1'b1;
end
end else if (r_sign)
o_quotient <= -o_quotient;
else
o_quotient <= 0;
 
// Set Carry on an exact divide
// Perhaps nothing uses this, but ... well, I suppose we could remove
// this logic eventually, just ... not yet.
initial r_c = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_c <= 1'b0;
else
r_c <= (r_busy)&&(diff == 0);
r_c <= (r_busy)&&((diff == 0)||(r_dividend == 0));
 
// The last flag: Negative. This flag is set assuming that the result
// of the divide was negative (i.e., the high order bit is set). This
342,215 → 349,4
assign w_n = o_quotient[(BW-1)];
 
assign o_flags = { 1'b0, w_n, r_c, r_z };
 
`ifdef FORMAL
reg f_past_valid;
initial f_past_valid = 0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
 
`ifdef DIV
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
initial `ASSUME(i_reset);
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
assert(!o_busy);
assert(!o_valid);
assert(!o_err);
//
assert(!r_busy);
// assert(!zero_divisor);
assert(r_bit==0);
assert(!last_bit);
assert(!pre_sign);
// assert(!r_z);
// assert(r_dividend==0);
assert(o_quotient==0);
assert(!r_c);
assert(r_divisor==0);
 
`ASSUME(!i_wr);
end
 
always @(*)
if (o_busy)
`ASSUME(!i_wr);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(o_busy))&&(!o_busy))
begin
assert(o_valid);
end
 
// A formal methods section
//
// This section isn't yet complete. For now, it is just
// a description of things I think should be in here ... not
// yet a description of what it would take to prove
// this divide (yet).
always @(*)
if (o_err)
assert(o_valid);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_wr)))
assert(!pre_sign);
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr))&&($past(i_signed))
&&(|$past({i_numerator[BW-1],i_denominator[BW-1]})))
assert(pre_sign);
 
// always @(posedge i_clk)
// if ((f_past_valid)&&(!$past(pre_sign)))
// assert(!r_sign);
reg [BW:0] f_bits_set;
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr)))
assert(o_busy);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid)))
assert(!o_valid);
 
always @(*)
if ((o_valid)&&(!o_err))
assert(r_z == ((o_quotient == 0)? 1'b1:1'b0));
else if (o_busy)
assert(r_z == (((o_quotient&f_bits_set[BW-1:0]) == 0)? 1'b1: 1'b0));
 
always @(*)
if ((o_valid)&&(!o_err))
assert(w_n == o_quotient[BW-1]);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(r_busy))&&(!$past(i_wr)))
assert(!o_busy);
always @(posedge i_clk)
assert((!o_busy)||(!o_valid));
 
always @(*)
if(r_busy)
assert(o_busy);
 
always @(posedge i_clk)
if (i_reset)
f_bits_set <= 0;
else if (i_wr)
f_bits_set <= 0;
else if ((r_busy)&&(!pre_sign))
f_bits_set <= { f_bits_set[BW-1:0], 1'b1 };
 
always @(posedge i_clk)
if (r_busy)
assert(((1<<r_bit)-1) == f_bits_set);
 
always @(*)
if ((o_valid)&&(!o_err))
assert((!f_bits_set[BW])&&(&f_bits_set[BW-1:0]));
 
 
/*
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(r_busy))
&&($past(r_divisor[2*BW-2:BW])==0))
begin
if ($past(r_divisor) == 0)
assert(o_err);
else if ($past(pre_sign))
begin
if ($past(r_dividend[BW-1]))
assert(r_dividend == -$past(r_dividend));
if ($past(r_divisor[(2*BW-2)]))
begin
assert(r_divisor[(2*BW-2):(BW-1)]
== -$past(r_divisor[(2*BW-2):(BW-1)]));
assert(r_divisor[BW-2:0] == 0);
end
end else begin
if (o_quotient[0])
assert(r_dividend == $past(diff));
else
assert(r_dividend == $past(r_dividend));
 
// r_divisor should shift down on every step
assert(r_divisor[2*BW-2]==0);
assert(r_divisor[2*BW-3:0]==$past(r_divisor[2*BW-2:1]));
end
if ($past(r_dividend) >= $past(r_divisor[BW-1:0]))
assert(o_quotient[0]);
else
assert(!o_quotient[0]);
end
*/
 
always @(*)
if (r_busy)
assert((f_bits_set & r_dividend[BW-1:0])==0);
 
always @(*)
if (r_busy)
assert((r_divisor == 0) == zero_divisor);
 
`ifdef VERIFIC
// Verify unsigned division
assert property (@(posedge i_clk)
disable iff (i_reset)
(i_wr)&&(i_denominator != 0)&&(!i_signed)
|=> ((!o_err)&&(!o_valid)&&(o_busy)&&(!r_sign)&&(!pre_sign)
throughout (r_bit == 0)
##1 ((r_bit == $past(r_bit)+1)&&({1'b0,r_bit}< BW-1))
[*0:$]
##1 ({ 1'b0, r_bit } == BW-1))
##1 (!o_err)&&(o_valid));
 
// Verify division by zero
assert property (@(posedge i_clk)
disable iff (i_reset)
(i_wr)&&(i_denominator == 0)
|=> (zero_divisor throughout
(!o_err)&&(!o_valid)&&(pre_sign) [*0:1]
##1 ((r_busy)&&(!o_err)&&(!o_valid))
##1 ((o_err)&&(o_valid))));
 
 
`endif // VERIFIC
`endif
endmodule
//
// How much logic will this divide use, now that it's been updated to
// a different (long division) algorithm?
//
// iCE40 stats (Updated) (Original)
// Number of cells: 700 820
// SB_CARRY 125 125
// SB_DFF 1
// SB_DFFE 33 1
// SB_DFFESR 37
// SB_DFFESS 31
// SB_DFFSR 40 40
// SB_LUT4 433 553
//
// Xilinx stats (Updated) (Original)
// Number of cells: 758 831
// FDRE 142 142
// LUT1 97 97
// LUT2 69 174
// LUT3 6 5
// LUT4 1 6
// LUT5 68 35
// LUT6 94 98
// MUXCY 129 129
// MUXF7 12 8
// MUXF8 6 3
// XORCY 134 134
 
/idecode.v
19,7 → 19,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
43,60 → 43,41
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define CPU_SP_REG 4'hd
`define CPU_CC_REG 4'he
`define CPU_PC_REG 4'hf
//
`define CISBIT 31
`define CISIMMSEL 23
`define IMMSEL 18
`include "cpudefs.v"
//
//
//
module idecode(i_clk, i_reset, i_ce, i_stalled,
module idecode(i_clk, i_rst, i_ce, i_stalled,
i_instruction, i_gie, i_pc, i_pf_valid,
i_illegal,
o_valid,
o_phase, o_illegal,
o_pc,
o_dcdR, o_dcdA, o_dcdB,
o_preA, o_preB,
o_I, o_zI,
o_pc, o_gie,
o_dcdR, o_dcdA, o_dcdB, o_I, o_zI,
o_cond, o_wF,
o_op, o_ALU, o_M, o_DV, o_FP, o_break, o_lock,
o_wR, o_rA, o_rB,
o_early_branch, o_early_branch_stb, o_branch_pc, o_ljmp,
o_early_branch, o_branch_pc, o_ljmp,
o_pipe,
o_sim, o_sim_immv
`ifdef FORMAL
, f_insn_word, f_insn_gie
`endif
);
parameter ADDRESS_WIDTH=24;
parameter [0:0] OPT_MPY = 1'b1;
parameter [0:0] OPT_EARLY_BRANCHING = 1'b1;
parameter [0:0] OPT_PIPELINED = 1'b1;
parameter [0:0] OPT_DIVIDE = (OPT_PIPELINED);
parameter [0:0] OPT_FPU = 1'b0;
parameter [0:0] OPT_CIS = 1'b1;
parameter [0:0] OPT_LOCK = (OPT_PIPELINED);
parameter [0:0] OPT_OPIPE = (OPT_PIPELINED);
parameter [0:0] OPT_SIM = 1'b0;
parameter [0:0] OPT_NO_USERMODE = 1'b0;
localparam AW = ADDRESS_WIDTH;
//
input wire i_clk, i_reset, i_ce, i_stalled;
input wire [31:0] i_instruction;
input wire i_gie;
input wire [(AW+1):0] i_pc;
input wire i_pf_valid, i_illegal;
parameter ADDRESS_WIDTH=24, IMPLEMENT_MPY=1, EARLY_BRANCHING=1,
IMPLEMENT_DIVIDE=1, IMPLEMENT_FPU=0, AW = ADDRESS_WIDTH;
input i_clk, i_rst, i_ce, i_stalled;
input [31:0] i_instruction;
input i_gie;
input [(AW-1):0] i_pc;
input i_pf_valid, i_illegal;
output wire o_valid, o_phase;
output reg o_illegal;
output reg [(AW+1):0] o_pc;
output reg [AW:0] o_pc;
output reg o_gie;
output reg [6:0] o_dcdR, o_dcdA, o_dcdB;
output wire [4:0] o_preA, o_preB;
output wire [31:0] o_I;
output reg o_zI;
output reg [3:0] o_cond;
103,24 → 84,30
output reg o_wF;
output reg [3:0] o_op;
output reg o_ALU, o_M, o_DV, o_FP, o_break;
output reg o_lock;
output wire o_lock;
output reg o_wR, o_rA, o_rB;
output wire o_early_branch, o_early_branch_stb;
output wire [(AW+1):0] o_branch_pc;
output wire o_early_branch;
output wire [(AW-1):0] o_branch_pc;
output wire o_ljmp;
output wire o_pipe;
output reg o_sim /* verilator public_flat */;
output reg [22:0] o_sim_immv /* verilator public_flat */;
`ifdef FORMAL
output reg [31:0] f_insn_word;
output reg f_insn_gie;
 
wire dcdA_stall, dcdB_stall, dcdF_stall;
wire o_dcd_early_branch;
wire [(AW-1):0] o_dcd_branch_pc;
reg o_dcdI, o_dcdIz;
`ifdef OPT_PIPELINED
reg r_lock;
`endif
`ifdef OPT_PIPELINED_BUS_ACCESS
reg r_pipe;
`endif
 
 
wire [4:0] w_op;
wire w_ldi, w_mov, w_cmptst, w_ldilo, w_ALU, w_brev,
w_noop, w_lock, w_sim, w_break, w_special, w_add,
w_mpy;
w_noop, w_lock;
wire [4:0] w_dcdR, w_dcdB, w_dcdA;
wire w_dcdR_pc, w_dcdR_cc;
wire w_dcdA_pc, w_dcdA_cc;
130,103 → 117,91
wire w_wR, w_rA, w_rB, w_wR_n;
wire w_ljmp, w_ljmp_dly, w_cis_ljmp;
wire [31:0] iword;
wire pf_valid;
 
assign pf_valid = (i_pf_valid)&&(!o_early_branch_stb);
 
 
reg [14:0] r_nxt_half;
 
generate if (OPT_CIS)
begin : SET_IWORD
 
assign iword = (o_phase)
`ifdef OPT_CIS
reg [15:0] r_nxt_half;
assign iword = (o_phase)
// set second half as a NOOP ... but really
// shouldn't matter
? { 1'b1, r_nxt_half[14:0], i_instruction[15:0] }
? { r_nxt_half[15:0], i_instruction[15:0] }
: i_instruction;
end else begin : CLR_IWORD
assign iword = { 1'b0, i_instruction[30:0] };
`else
assign iword = { 1'b0, i_instruction[30:0] };
`endif
 
// verilator lint_off UNUSED
wire [14:0] unused_nxt_half;
assign unused_nxt_half = r_nxt_half;
// verilator lint_on UNUSED
end endgenerate
 
generate
if (OPT_EARLY_BRANCHING)
if (EARLY_BRANCHING != 0)
begin
if (OPT_CIS)
begin : CIS_EARLY_BRANCHING
`ifdef OPT_CIS
reg r_pre_ljmp;
always @(posedge i_clk)
if ((i_rst)||(o_early_branch))
r_pre_ljmp <= 1'b0;
else if ((i_ce)&&(i_pf_valid))
r_pre_ljmp <= (!o_phase)&&(i_instruction[31])
&&(i_instruction[14:0] == 15'h7cf8);
else if (i_ce)
r_pre_ljmp <= 1'b0;
 
assign w_cis_ljmp = (o_phase)&&(iword[31:16] == 16'hfcf8);
 
end else begin : NOCIS_EARLY_BRANCH
 
assign w_cis_ljmp = 1'b0;
 
end
 
assign w_cis_ljmp = r_pre_ljmp;
`else
assign w_cis_ljmp = 1'b0;
`endif
// 0.1111.10010.000.1.1111.000000000...
// 0111.1100.1000.0111.11000....
assign w_ljmp = (iword == 32'h7c87c000);
 
end else begin : NO_EARLY_BRANCHING
 
end else begin
assign w_cis_ljmp = 1'b0;
assign w_ljmp = 1'b0;
end endgenerate
end
endgenerate
 
`ifdef OPT_CIS
`ifdef VERILATOR
wire [4:0] w_cis_op;
always @(iword)
if (!iword[31])
w_cis_op = w_op;
else case(iword[26:24])
3'h0: w_cis_op = 5'h00;
3'h1: w_cis_op = 5'h01;
3'h2: w_cis_op = 5'h02;
3'h3: w_cis_op = 5'h10;
3'h4: w_cis_op = 5'h12;
3'h5: w_cis_op = 5'h13;
3'h6: w_cis_op = 5'h18;
3'h7: w_cis_op = 5'h0d;
endcase
`else
reg [4:0] w_cis_op;
 
generate if (OPT_CIS)
begin : GEN_CIS_OP
 
always @(*)
if (!iword[`CISBIT])
w_cis_op = iword[26:22];
always @(iword,w_op)
if (!iword[31])
w_cis_op <= w_op;
else case(iword[26:24])
3'h0: w_cis_op = 5'h00; // ADD
3'h1: w_cis_op = 5'h01; // AND
3'h2: w_cis_op = 5'h02; // SUB
3'h3: w_cis_op = 5'h10; // BREV
3'h4: w_cis_op = 5'h12; // LW
3'h5: w_cis_op = 5'h13; // SW
3'h6: w_cis_op = 5'h18; // LDI
3'h7: w_cis_op = 5'h0d; // MOV
3'h0: w_cis_op <= 5'h00;
3'h1: w_cis_op <= 5'h01;
3'h2: w_cis_op <= 5'h02;
3'h3: w_cis_op <= 5'h10;
3'h4: w_cis_op <= 5'h12;
3'h5: w_cis_op <= 5'h13;
3'h6: w_cis_op <= 5'h18;
3'h7: w_cis_op <= 5'h0d;
endcase
`endif
`else
wire [4:0] w_cis_op;
assign w_cis_op = w_op;
`endif
 
end else begin : GEN_NOCIS_OP
 
always @(*)
w_cis_op = w_op;
 
end endgenerate
 
// Decode instructions
assign w_op= iword[26:22];
assign w_mov = (w_cis_op == 5'h0d);
assign w_ldi = (w_cis_op[4:1] == 4'hc);
assign w_brev = (w_cis_op == 5'h08);
assign w_mpy = (w_cis_op[4:1] == 4'h5)||(w_cis_op[4:0]==5'h0c);
assign w_brev = (w_cis_op == 5'h8);
assign w_cmptst = (w_cis_op[4:1] == 4'h8);
assign w_ldilo = (w_cis_op[4:0] == 5'h09);
assign w_ldilo = (w_cis_op[4:0] == 5'h9);
assign w_ALU = (!w_cis_op[4]) // anything with [4]==0, but ...
&&(w_cis_op[3:1] != 3'h7); // not the divide
assign w_add = (w_cis_op[4:0] == 5'h02);
assign w_mem = (w_cis_op[4:3] == 2'b10)&&(w_cis_op[2:1] !=2'b00);
assign w_sto = (w_mem)&&( w_cis_op[0]);
assign w_div = (!iword[`CISBIT])&&(w_op[4:1] == 4'h7);
assign w_fpu = (!iword[`CISBIT])&&(w_op[4:3] == 2'b11)
&&(w_dcdR[3:1] != 3'h7)&&(w_op[2:1] != 2'b00);
// If the result register is either CC or PC, and this would otherwise
// be a floating point instruction with floating point opcode of 0,
// then this is a NOOP.
assign w_special= (!iword[`CISBIT])&&((!OPT_FPU)||(w_dcdR[3:1]==3'h7))
&&(w_op[4:2] == 3'b111);
assign w_break = (w_special)&&(w_op[4:0]==5'h1c);
assign w_lock = (w_special)&&(w_op[4:0]==5'h1d);
assign w_sim = (w_special)&&(w_op[4:0]==5'h1e);
assign w_noop = (w_special)&&(w_op[4:0]==5'h1f);
 
 
// w_dcdR (4 LUTs)
237,14 → 212,25
// moves in iword[18] but only for the supervisor, and the other
// four bits encoded in the instruction.
//
assign w_dcdR = { ((!iword[`CISBIT])&&(!OPT_NO_USERMODE)&&(w_mov)&&(!i_gie))?iword[`IMMSEL]:i_gie,
assign w_dcdR = { ((!iword[31])&&(w_mov)&&(~i_gie))?iword[18]:i_gie,
iword[30:27] };
// 2 LUTs
//
// If the result register is either CC or PC, and this would otherwise
// be a floating point instruction with floating point opcode of 0,
// then this is a NOOP.
assign w_lock = (!iword[31])&&(w_op[4:0]==5'h1d)&&(
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7))
||(IMPLEMENT_FPU==0));
assign w_noop = (!iword[31])&&(w_op[4:0] == 5'h1f)&&(
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1] == 3'h7))
||(IMPLEMENT_FPU==0));
 
// dcdB - What register is used in the opB?
//
assign w_dcdB[4] = ((!iword[`CISBIT])&&(w_mov)&&(!OPT_NO_USERMODE)&&(!i_gie))?iword[13]:i_gie;
assign w_dcdB[3:0]= (iword[`CISBIT])
? (((!iword[`CISIMMSEL])&&(iword[26:25]==2'b10))
assign w_dcdB[4] = ((!iword[31])&&(w_mov)&&(~i_gie))?iword[13]:i_gie;
assign w_dcdB[3:0]= (iword[31])
? (((!iword[23])&&(iword[26:25]==2'b10))
? `CPU_SP_REG : iword[22:19])
: iword[17:14];
 
260,18 → 246,26
assign w_dcdB_pc = (w_rB)&&(w_dcdB[3:0] == `CPU_PC_REG);
assign w_dcdB_cc = (w_rB)&&(w_dcdB[3:0] == `CPU_CC_REG);
 
// Under what condition will we execute this
// instruction? Only the load immediate instruction
// is completely unconditional.
//
// Under what condition will we execute this instruction? Only the
// load immediate instruction and the CIS instructions are completely
// unconditional. Well ... not quite. The BREAK, LOCK, and SIM/NOOP
// instructions are also unconditional.
//
assign w_cond = ((w_ldi)||(w_special)||(iword[`CISBIT])) ? 4'h8 :
// 3+4 LUTs
assign w_cond = ((w_ldi)||(iword[31])) ? 4'h8 :
{ (iword[21:19]==3'h0), iword[21:19] };
 
// 1 LUT
assign w_mem = (w_cis_op[4:3] == 2'b10)&&(w_cis_op[2:1] !=2'b00);
assign w_sto = (w_mem)&&( w_cis_op[0]);
// 1 LUT
assign w_div = (!iword[31])&&(w_op[4:1] == 4'h7);
// 2 LUTs
assign w_fpu = (!iword[31])&&(w_op[4:3] == 2'b11)
&&(w_dcdR[3:1] != 3'h7)&&(w_op[2:1] != 2'b00);
//
// rA - do we need to read register A?
assign w_rA = // Floating point reads reg A
((w_fpu)&&(OPT_FPU))
((w_fpu)&&(w_cis_op[4:1] != 4'hf))
// Divide's read A
||(w_div)
// ALU ops read A,
284,24 → 278,25
// rB -- do we read a register for operand B? Specifically, do we
// add the registers value to the immediate to create opB?
assign w_rB = (w_mov)
||((!iword[`CISBIT])&&(iword[`IMMSEL])&&(!w_ldi)&&(!w_special))
||(( iword[`CISBIT])&&(iword[`CISIMMSEL])&&(!w_ldi))
||((!iword[31])&&(iword[18])&&(!w_ldi))
||(( iword[31])&&(iword[23])&&(!w_ldi))
// If using compressed instruction sets,
// we *always* read on memory operands.
||(( iword[`CISBIT])&&(w_mem));
 
||(( iword[31])&&(w_mem));
// wR -- will we be writing our result back?
// wR_n = !wR
// All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR
// 1 LUT: All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR
assign w_wR_n = (w_sto)
||(w_special)
||((!iword[31])&&(w_cis_op[4:3]==2'b11)
&&(w_cis_op[2:1]!=2'b00)
&&(w_dcdR[3:1]==3'h7))
||(w_cmptst);
assign w_wR = !w_wR_n;
assign w_wR = ~w_wR_n;
//
// wF -- do we write flags when we are done?
//
assign w_wF = (w_cmptst)
||((w_cond[3])&&(((w_fpu)&&(OPT_FPU))||(w_div)
||((w_cond[3])&&((w_fpu)||(w_div)
||((w_ALU)&&(!w_mov)&&(!w_ldilo)&&(!w_brev)
&&(w_dcdR[3:1] != 3'h7))));
 
318,33 → 313,27
// MOVE immediates have one less bit
:((w_mov) ?{ {(23-13){iword[12]}}, iword[12:0] }
// Normal Op-B immediate ... 18 or 14 bits
:((!iword[`IMMSEL]) ? { {(23-18){iword[17]}}, iword[17:0] }
:((~iword[18]) ? { {(23-18){iword[17]}}, iword[17:0] }
: { {(23-14){iword[13]}}, iword[13:0] }
));
 
generate if (OPT_CIS)
begin : GEN_CIS_IMMEDIATE
wire [7:0] w_halfbits;
assign w_halfbits = iword[`CISIMMSEL:16];
`ifdef OPT_CIS
wire [7:0] w_halfbits;
assign w_halfbits = iword[23:16];
 
wire [7:0] w_halfI;
assign w_halfI = (iword[26:24]==3'h6) ? w_halfbits[7:0] // 8'b for LDI
:(w_halfbits[7])?
{ {(6){w_halfbits[2]}}, w_halfbits[1:0]}
:{ w_halfbits[6], w_halfbits[6:0] };
assign w_I = (iword[`CISBIT])
? {{(23-8){w_halfI[7]}}, w_halfI }
: w_fullI;
 
end else begin : GEN_NOCIS_IMMEDIATE
 
assign w_I = w_fullI;
 
end endgenerate
 
wire [7:0] w_halfI;
assign w_halfI = (iword[26:24]==3'h6) ? w_halfbits[7:0]
:(w_halfbits[7])?
{ {(6){w_halfbits[2]}}, w_halfbits[1:0]}
:{ w_halfbits[6], w_halfbits[6:0] };
assign w_I = (iword[31])?{{(23-8){w_halfI[7]}}, w_halfI }:w_fullI;
`else
assign w_I = w_fullI;
`endif
assign w_Iz = (w_I == 0);
 
 
`ifdef OPT_CIS
//
// The o_phase parameter is special. It needs to let the software
// following know that it cannot break/interrupt on an o_phase asserted
352,276 → 341,213
// half of a CIS instruction. To do this, o_phase must be asserted
// when the first instruction half is valid, but not asserted on either
// a 32-bit instruction or the second half of a 2x16-bit instruction.
generate if (OPT_CIS)
begin : GEN_CIS_PHASE
reg r_phase;
 
// Phase is '1' on the first instruction of a two-part set
// But, due to the delay in processing, it's '1' when our
// output is valid for that first part, but that'll be the
// same time we are processing the second part ... so it may
// look to us like a '1' on the second half of processing.
 
// When no instruction is in the pipe, phase is zero
initial r_phase = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(w_ljmp_dly))
reg r_phase;
initial r_phase = 1'b0;
always @(posedge i_clk)
if ((i_rst) // When no instruction is in the pipe, phase is zero
||(o_early_branch)||(w_ljmp_dly))
r_phase <= 1'b0;
else if ((i_ce)&&(pf_valid))
begin
if (o_phase)
// CIS instructions only have two parts. On
// the second part (o_phase is true), return
// back to the first
r_phase <= 0;
else
r_phase <= (i_instruction[`CISBIT])&&(!i_illegal);
end else if (i_ce)
else if ((i_ce)&&(i_pf_valid))
r_phase <= (o_phase)? 1'b0
: ((i_instruction[31])&&(i_pf_valid));
else if (i_ce)
r_phase <= 1'b0;
// Phase is '1' on the first instruction of a two-part set
// But, due to the delay in processing, it's '1' when our output is
// valid for that first part, but that'll be the same time we
// are processing the second part ... so it may look to us like a '1'
// on the second half of processing.
 
assign o_phase = r_phase;
end else begin
assign o_phase = 1'b0;
end endgenerate
assign o_phase = r_phase;
`else
assign o_phase = 1'b0;
`endif
 
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
if (i_ce)
begin
if (OPT_PIPELINED)
o_illegal <= ((i_illegal)
&&((!o_phase)||(!o_valid)))
||((o_illegal)&&(o_phase)&&(o_valid));
else
o_illegal <= (i_illegal)&&(!o_phase);
if ((!OPT_CIS)&&(i_instruction[`CISBIT]))
o_illegal <= 1'b1;
if ((!OPT_MPY)&&(w_mpy))
o_illegal <= 1'b1;
if (i_rst)
o_illegal <= 1'b0;
else if (i_ce)
begin
`ifdef OPT_CIS
o_illegal <= (i_illegal);
`else
o_illegal <= ((i_illegal) || (i_instruction[31]));
`endif
if ((IMPLEMENT_MPY==0)&&((w_cis_op[4:1]==4'h5)||(w_cis_op[4:0]==5'h0c)))
o_illegal <= 1'b1;
 
if ((!OPT_DIVIDE)&&(w_div))
o_illegal <= 1'b1;
else if ((OPT_DIVIDE)&&(w_div)&&(w_dcdR[3:1]==3'h7))
o_illegal <= 1'b1;
if ((IMPLEMENT_DIVIDE==0)&&(w_div))
o_illegal <= 1'b1;
else if ((IMPLEMENT_DIVIDE!=0)&&(w_div)&&(w_dcdR[3:1]==3'h7))
o_illegal <= 1'b1;
 
 
if ((!OPT_FPU)&&(w_fpu))
o_illegal <= 1'b1;
if ((IMPLEMENT_FPU==0)&&(w_fpu))
o_illegal <= 1'b1;
 
if ((!OPT_SIM)&&(w_sim))
// Simulation instructions on real hardware should
// always cause an illegal instruction error
o_illegal <= 1'b1;
if ((w_cis_op[4:3]==2'b11)&&(w_cis_op[2:1]!=2'b00)
&&(w_dcdR[3:1]==3'h7)
&&(
(w_cis_op[2:0] != 3'h4) // BREAK
`ifdef OPT_PIPELINED
&&(w_cis_op[2:0] != 3'h5) // LOCK
`endif
// SIM instructions are always illegal
&&(w_cis_op[2:0] != 3'h7))) // NOOP
o_illegal <= 1'b1;
end
 
// There are two (missing) special instructions
// These should cause an illegal instruction error
if ((w_dcdR[3:1]==3'h7)&&(w_cis_op[4:1]==4'b1101))
o_illegal <= 1'b1;
 
// If the lock function isn't implemented, this should
// also cause an illegal instruction error
if ((!OPT_LOCK)&&(w_lock))
o_illegal <= 1'b1;
end
 
initial o_pc = 0;
always @(posedge i_clk)
if ((i_ce)&&((o_phase)||(i_pf_valid)))
begin
o_pc[0] <= 1'b0;
if (i_ce)
begin
`ifdef OPT_CIS
if (!o_phase)
o_gie<= i_gie;
 
if (OPT_CIS)
begin
if (iword[`CISBIT])
if (iword[31])
begin
if (o_phase)
o_pc[AW+1:1] <= o_pc[AW+1:1] + 1'b1;
else
o_pc <= { i_pc[AW+1:2], 1'b1, 1'b0 };
o_pc <= o_pc + 1'b1;
else if (i_pf_valid)
o_pc <= { i_pc, 1'b1 };
end else begin
// The normal, non-CIS case
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
o_pc <= { i_pc + 1'b1, 1'b0 };
end
end else begin
// The normal, non-CIS case
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
end
end
`else
o_gie<= i_gie;
o_pc <= { i_pc + 1'b1, 1'b0 };
`endif
 
initial o_dcdR = 0;
initial o_dcdA = 0;
initial o_dcdB = 0;
initial o_DV = 0;
initial o_FP = 0;
initial o_lock = 0;
always @(posedge i_clk)
if (i_ce)
begin
// Under what condition will we execute this
// instruction? Only the load immediate instruction
// is completely unconditional.
o_cond <= w_cond;
// Don't change the flags on conditional instructions,
// UNLESS: the conditional instruction was a CMP
// or TST instruction.
o_wF <= w_wF;
// Under what condition will we execute this
// instruction? Only the load immediate instruction
// is completely unconditional.
o_cond <= w_cond;
// Don't change the flags on conditional instructions,
// UNLESS: the conditional instruction was a CMP
// or TST instruction.
o_wF <= w_wF;
 
// Record what operation/op-code (4-bits) we are doing
// Note that LDI magically becomes a MOV
// instruction here. That way it's a pass through
// the ALU. Likewise, the two compare instructions
// CMP and TST becomes SUB and AND here as well.
// We keep only the bottom four bits, since we've
// already done the rest of the decode necessary to
// settle between the other instructions. For example,
// o_FP plus these four bits uniquely defines the FP
// instruction, o_DV plus the bottom of these defines
// the divide, etc.
o_op <= w_cis_op[3:0];
if ((w_ldi)||(w_noop)||(w_lock))
o_op <= 4'hd;
// Record what operation/op-code (4-bits) we are doing
// Note that LDI magically becomes a MOV
// instruction here. That way it's a pass through
// the ALU. Likewise, the two compare instructions
// CMP and TST becomes SUB and AND here as well.
// We keep only the bottom four bits, since we've
// already done the rest of the decode necessary to
// settle between the other instructions. For example,
// o_FP plus these four bits uniquely defines the FP
// instruction, o_DV plus the bottom of these defines
// the divide, etc.
o_op <= ((w_ldi)||(w_noop))? 4'hd : w_cis_op[3:0];
 
// Default values
o_dcdR <= { w_dcdR_cc, w_dcdR_pc, w_dcdR};
o_dcdA <= { w_dcdA_cc, w_dcdA_pc, w_dcdA};
o_dcdB <= { w_dcdB_cc, w_dcdB_pc, w_dcdB};
o_wR <= w_wR;
o_rA <= w_rA;
o_rB <= w_rB;
r_I <= w_I;
o_zI <= w_Iz;
// Default values
o_dcdR <= { w_dcdR_cc, w_dcdR_pc, w_dcdR};
o_dcdA <= { w_dcdA_cc, w_dcdA_pc, w_dcdA};
o_dcdB <= { w_dcdB_cc, w_dcdB_pc, w_dcdB};
o_wR <= w_wR;
o_rA <= w_rA;
o_rB <= w_rB;
r_I <= w_I;
o_zI <= w_Iz;
 
// Turn a NOOP into an ALU operation--subtract in
// particular, although it doesn't really matter as long
// as it doesn't take longer than one clock. Note
// also that this depends upon not setting any registers
// or flags, which should already be true.
o_ALU <= (w_ALU)||(w_ldi)||(w_cmptst)||(w_noop)
||((!OPT_LOCK)&&(w_lock));
o_M <= w_mem;
o_DV <= (OPT_DIVIDE)&&(w_div);
o_FP <= (OPT_FPU)&&(w_fpu);
// Turn a NOOP into an ALU operation--subtract in
// particular, although it doesn't really matter as long
// as it doesn't take longer than one clock. Note
// also that this depends upon not setting any registers
// or flags, which should already be true.
o_ALU <= (w_ALU)||(w_ldi)||(w_cmptst)||(w_noop);
o_M <= w_mem;
o_DV <= w_div;
o_FP <= w_fpu;
 
o_break <= w_break;
o_lock <= (OPT_LOCK)&&(w_lock);
o_break <= (!iword[31])&&(w_op[4:0]==5'h1c)&&(
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7))
||(IMPLEMENT_FPU==0));
`ifdef OPT_PIPELINED
r_lock <= w_lock;
`endif
`ifdef OPT_CIS
r_nxt_half <= { iword[31], iword[14:0] };
`endif
 
if (OPT_CIS)
r_nxt_half <= { iword[14:0] };
else
r_nxt_half <= 0;
 
if (OPT_SIM)
begin
`ifdef VERILATOR
// Support the SIM instruction(s)
o_sim <= (w_sim)||(w_noop);
o_sim <= (!iword[31])&&(w_op[4:1] == 4'hf)
&&(w_dcdR[3:1] == 3'h7);
`else
o_sim <= 1'b0;
`endif
o_sim_immv <= iword[22:0];
end else begin
o_sim <= 1'b0;
o_sim_immv <= 0;
end
end
 
assign o_preA = w_dcdA;
assign o_preB = w_dcdB;
`ifdef OPT_PIPELINED
assign o_lock = r_lock;
`else
assign o_lock = 1'b0;
`endif
 
generate if (OPT_EARLY_BRANCHING)
begin : GEN_EARLY_BRANCH_LOGIC
reg r_early_branch,
r_early_branch_stb,
r_ljmp;
reg [(AW+1):0] r_branch_pc;
generate
if (EARLY_BRANCHING!=0)
begin
reg r_early_branch, r_ljmp;
reg [(AW-1):0] r_branch_pc;
 
initial r_ljmp = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_ljmp <= 1'b0;
else if (i_ce)
begin
if ((r_ljmp)&&(pf_valid))
if (i_rst)
r_ljmp <= 1'b0;
else if (o_early_branch_stb)
r_ljmp <= 1'b0;
else if (pf_valid)
begin
if ((OPT_CIS)&&(iword[`CISBIT]))
r_ljmp <= w_cis_ljmp;
else
r_ljmp <= (w_ljmp);
end else if ((OPT_CIS)&&(o_phase)&&(iword[`CISBIT]))
`ifdef OPT_CIS
else if ((i_ce)&&(o_phase))
r_ljmp <= w_cis_ljmp;
end
`endif
else if ((i_ce)&&(i_pf_valid))
r_ljmp <= (w_ljmp);
assign o_ljmp = r_ljmp;
 
initial r_early_branch = 1'b0;
initial r_early_branch_stb = 1'b0;
always @(posedge i_clk)
if (i_reset)
if (i_rst)
r_early_branch <= 1'b0;
else if ((i_ce)&&(i_pf_valid))
begin
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end else if ((i_ce)&&(pf_valid))
begin
if (r_ljmp)
begin
// LW (PC),PC
r_early_branch <= 1'b1;
r_early_branch_stb <= 1'b1;
end else if ((!iword[`CISBIT])&&(iword[30:27]==`CPU_PC_REG)
// LOD (PC),PC
r_early_branch <= 1'b1;
else if ((!iword[31])&&(iword[30:27]==`CPU_PC_REG)
&&(w_cond[3]))
begin
if ((w_add)&&(!iword[`IMMSEL]))
begin
if ((w_op[4:0]==5'h02)&&(!iword[18]))
// Add x,PC
r_early_branch <= 1'b1;
r_early_branch_stb <= 1'b1;
end else begin
else
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end
// LDI #x,PC is no longer supported
end else begin
end else
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end
end else if (i_ce)
begin
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end else
r_early_branch_stb <= 1'b0;
r_early_branch <= 1'b0;
 
initial r_branch_pc = 0;
always @(posedge i_clk)
if (i_ce)
begin
if (r_ljmp)
r_branch_pc <= { iword[(AW+1):2],
2'b00 };
else begin
// Add x,PC
r_branch_pc[AW+1:2] <= i_pc[AW+1:2]
+ {{(AW-15){iword[17]}},iword[16:2]}
+ {{(AW-1){1'b0}},1'b1};
r_branch_pc[1:0] <= 2'b00;
if (i_ce)
begin
if (r_ljmp)
r_branch_pc <= iword[(AW+1):2];
else // Add x,PC
r_branch_pc <= i_pc
+ {{(AW-15){iword[17]}},iword[16:2]}
+ {{(AW-1){1'b0}},1'b1};
end
end
 
assign w_ljmp_dly = r_ljmp;
assign o_early_branch = r_early_branch;
assign o_early_branch_stb = r_early_branch_stb;
assign o_branch_pc = r_branch_pc;
end else begin
assign w_ljmp_dly = 1'b0;
assign o_early_branch = 1'b0;
assign o_early_branch_stb = 1'b0;
assign o_branch_pc = {(AW+2){1'b0}};
assign o_early_branch = 1'b0;
assign o_branch_pc = {(AW){1'b0}};
assign o_ljmp = 1'b0;
 
// verilator lint_off UNUSED
wire early_branch_unused;
assign early_branch_unused = w_add;
// verilator lint_on UNUSED
end endgenerate
 
 
635,64 → 561,14
// Note that we're not using iword here ... there's a lot of logic
// taking place, and it's only valid if the new word is not compressed.
//
reg r_valid, r_insn_is_pipeable;
generate if (OPT_OPIPE)
begin : GEN_OPIPE
reg r_pipe;
 
wire [13:0] pipe_addr_diff;
assign pipe_addr_diff = w_I[13:0] - r_I[13:0];
 
// Pipeline logic is too extreme for a single clock.
// Let's break it into two clocks, using r_insn_is_pipeable
// If this function is true, then the instruction associated
// with the current output *may* have a pipeable instruction
// following it.
//
initial r_insn_is_pipeable = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_insn_is_pipeable <= 1'b0;
else if ((i_ce)&&((!pf_valid)||(i_illegal))&&(!o_phase))
// Pipeline bubble, can't pipe through it
r_insn_is_pipeable <= 1'b0;
else if (o_ljmp)
r_insn_is_pipeable <= 1'b0;
else if ((i_ce)&&((!OPT_CIS)&&(i_instruction[`CISBIT])))
r_insn_is_pipeable <= 1'b0;
else if (i_ce)
begin // This is a valid instruction
r_insn_is_pipeable <= (w_mem)&&(w_rB)
// PC (and CC) registers can change
// underneath us. Therefore they cannot
// be used as a base register for piped
// memory ops
&&(w_dcdB[3:1] != 3'h7)
// Writes to PC or CC will destroy any
// possibility of pipeing--since they
// could create a jump
&&(w_dcdR[3:1] != 3'h7)
//
// Loads landing in the current address
// pointer register are not allowed,
// as they could then be used to violate
// our rule(s)
&&((w_cis_op[0])||(w_dcdB != w_dcdA));
end // else
// The pipeline is stalled
 
 
initial r_pipe = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_pipe <= 1'b0;
else if (i_ce)
r_pipe <= ((pf_valid)||(o_phase))
// The last operation must be capable of
// being followed by a pipeable memory op
&&(r_insn_is_pipeable)
reg r_valid;
`ifdef OPT_PIPELINED_BUS_ACCESS
initial r_pipe = 1'b0;
always @(posedge i_clk)
if (i_ce)
r_pipe <= (r_valid)&&((i_pf_valid)||(o_phase))
// Both must be memory operations
&&(w_mem)
&&(w_mem)&&(o_M)
// Both must be writes, or both stores
&&(o_op[0] == w_cis_op[0])
// Both must be register ops
699,1161 → 575,34
&&(w_rB)
// Both must use the same register for B
&&(w_dcdB[3:0] == o_dcdB[3:0])
// CC or PC registers are not valid addresses
// Captured above
// But ... the result can never be B
// Captured above
//
// Reads to CC or PC not allowed
// &&((o_op[0])||(w_dcdR[3:1] != 3'h7))
// Prior-reads to CC or PC not allowed
// Captured above
&&((o_op[0])
||(w_dcdB[3:0] != o_dcdA[3:0]))
// Needs to be to the mode, supervisor or user
&&(i_gie == o_gie)
// Same condition, or no condition before
&&((w_cond[2:0]==o_cond[2:0])
&&((i_instruction[21:19]==o_cond[2:0])
||(o_cond[2:0] == 3'h0))
// Same or incrementing immediate
&&(w_I[13]==r_I[13])
&&(pipe_addr_diff <= 14'h4);
assign o_pipe = r_pipe;
end else begin
assign o_pipe = 1'b0;
always @(*)
r_insn_is_pipeable = 1'b0;
// Same immediate
&&((w_I[13:2]==r_I[13:2])
||({1'b0, w_I[13:2]}==(r_I[13:2]+12'h1)));
assign o_pipe = r_pipe;
`else
assign o_pipe = 1'b0;
`endif
 
// verilator lint_off UNUSED
wire unused_pipable;
assign unused_pipable = r_insn_is_pipeable;
// verilator lint_on UNUSED
end endgenerate
 
initial r_valid = 1'b0;
generate if (OPT_PIPELINED)
begin : GEN_DCD_VALID
 
always @(posedge i_clk)
if (i_reset)
r_valid <= 1'b0;
else if (i_ce)
r_valid <= ((pf_valid)||(o_phase))&&(!o_ljmp);
else if (!i_stalled)
r_valid <= 1'b0;
 
end else begin : GEN_DCD_VALID
 
always @(posedge i_clk)
if (i_reset)
always @(posedge i_clk)
if (i_rst)
r_valid <= 1'b0;
else if (i_ce)
r_valid <= ((i_pf_valid)||(o_phase)||(i_illegal))
&&(!o_ljmp)&&(!o_early_branch);
else if (!i_stalled)
r_valid <= ((pf_valid)||(o_phase))&&(!o_ljmp);
else
r_valid <= 1'b0;
 
end endgenerate
 
assign o_valid = r_valid;
 
 
assign o_I = { {(32-22){r_I[22]}}, r_I[21:0] };
 
// Make Verilator happy across all our various options
// verilator lint_off UNUSED
wire [5:0] possibly_unused;
assign possibly_unused = { w_lock, w_ljmp, w_ljmp_dly, w_cis_ljmp, i_pc[1:0] };
// verilator lint_on UNUSED
`ifdef FORMAL
reg f_past_valid;
 
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
 
`define ASSERT assert
`ifdef IDECODE
`define ASSUME assume
`else
`define ASSUME assert
`endif
always @(posedge i_clk)
if ((i_ce)&&(i_pf_valid)&&(!o_phase))
f_insn_word <= i_instruction;
always @(posedge i_clk)
if ((i_ce)&&(i_pf_valid)&&(!o_phase))
f_insn_gie = i_gie;
always @(*)
if (o_phase)
assert(r_nxt_half == f_insn_word[14:0]);
 
////////////////////////////
//
//
// Assumptions about our inputs
//
//
///////////////////////////
always @(*)
if (OPT_PIPELINED)
`ASSUME(i_ce == ((!o_valid)||(!i_stalled)));
else
`ASSUME(i_ce == !i_stalled);
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
`ASSERT(!o_valid);
// `ASSERT(!o_illegal);
`ASSERT(!o_phase);
`ASSERT(!o_ljmp);
`ASSERT(!o_pipe);
 
`ASSUME(!i_pf_valid);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!i_reset))
`ASSUME(i_gie == $past(i_gie));
 
`ifdef IDECODE
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_ce))
&&($past(f_past_valid))&&(!$past(i_reset,2))&&(!$past(i_ce,2)))
assume(i_ce);
`endif
 
reg f_new_insn, f_last_insn;
 
initial f_new_insn = 1'b0;
always @(posedge i_clk)
if (i_reset)
f_new_insn <= 1'b0;
else
f_new_insn <= ((pf_valid)&&(!i_stalled));
 
initial f_last_insn = 1'b0;
always @(posedge i_clk)
if (i_reset)
f_last_insn <= 1'b0;
else
f_last_insn <= (o_valid)&&(i_stalled);
 
always @(posedge i_clk)
if ((f_past_valid)&&(f_last_insn)&&(!i_reset))
begin
if (($past(pf_valid))&&(pf_valid))
begin
`ASSUME(i_instruction == $past(i_instruction));
`ASSUME(i_gie == $past(i_gie));
`ASSUME(i_pc == $past(i_pc));
`ASSUME(i_illegal == $past(i_illegal));
end
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(o_early_branch_stb))
`ASSUME(!pf_valid);
 
always @(*)
`ASSUME(i_pc[1:0] == 2'b00);
always @(*)
if ((o_valid)&&(!o_early_branch))
`ASSERT((o_illegal)||(o_pc[1] == o_phase));
 
wire [4+21+32+1+4+1+4+11+AW+3+23-1:0] f_result;
assign f_result = { o_valid, o_phase, o_illegal,
i_gie, o_dcdR, o_dcdA, o_dcdB, o_I, o_zI, o_cond,
o_wF, o_op, o_ALU, o_M, o_DV, o_FP, o_break, o_lock,
o_wR, o_rA, o_rB, o_early_branch, o_branch_pc, o_ljmp,
o_pipe, o_sim, o_sim_immv, o_pc };
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(f_last_insn))
`ASSERT(f_result == $past(f_result));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(pf_valid))
&&(!$past(o_ljmp)))
`ASSERT((!OPT_PIPELINED)||(o_valid));
 
always @(posedge i_clk)
if ((f_past_valid)&&(f_new_insn)
&&($past(pf_valid))&&($past(i_illegal))&&(!$past(o_phase)))
`ASSERT(o_illegal);
 
`ifdef IDECODE
// Let's walk through some basic instructions
// First 8-instructions, SUB - ASR
always @(*)
if ((!iword[`CISBIT])&&(iword[26:25]==2'b00))
begin
`ASSERT(!w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
`ASSERT((w_rA)&&(w_wR)&&(w_ALU));
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111));
end else if ((iword[`CISBIT])&&(iword[26:24]<3'b011))
begin
`ASSERT(!w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
`ASSERT((w_rA)&&(w_wR)&&(w_ALU));
`ASSERT(w_rB == iword[`CISIMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[22:19]);
 
if (iword[26:24] == 3'b000)
`ASSERT(w_cis_op == 5'h0);
else if (iword[26:24] == 5'h01)
`ASSERT(w_cis_op == 5'h01);
else // if (iword[26:24] == 3'b010)
`ASSERT(w_cis_op == 5'h02);
 
`ASSERT(w_cond == 4'h8);
 
if (iword[`CISIMMSEL])
`ASSERT(w_I == { {(23-3){iword[18]}}, iword[18:16] });
else
`ASSERT(w_I == { {(23-7){iword[22]}}, iword[22:16] });
end else
`ASSERT(!w_add);
 
// BREV and LDILO
always @(*)
if ((!iword[`CISBIT])&&((w_cis_op == 5'h8)
||(w_cis_op == 5'h09)))
begin
`ASSERT(!w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
if (w_cis_op == 5'h8)
begin
`ASSERT(w_brev);
`ASSERT(!w_ldilo);
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU));
end else begin// if (w_cis_op == 5'h9)
`ASSERT(w_ldilo);
`ASSERT(!w_brev);
`ASSERT((w_rA)&&(w_wR)&&(w_ALU));
end
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT(!w_wF);
end else begin
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
end
 
//
// Multiply instructions
always @(*)
if ((!iword[`CISBIT])&&((w_cis_op == 5'ha)
||(w_cis_op == 5'h0b)
||(w_cis_op == 5'h0c)))
begin
`ASSERT(w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT((w_rA)&&(w_wR)&&(w_ALU));
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111));
end else
`ASSERT(!w_mpy);
 
//
// Move instruction
always @(*)
if ((!iword[`CISBIT])&&((w_cis_op == 5'hd)))
begin
`ASSERT(w_mov);
`ASSERT(!w_div);
`ASSERT(!w_mpy);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU));
`ASSERT(w_rB);
`ASSERT(w_dcdA[4] == ((i_gie)||(iword[`IMMSEL])));
`ASSERT(w_dcdB[4] == ((i_gie)||(iword[13])));
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT(!w_wF);
end else if ((iword[`CISBIT])&&(iword[26:24]==3'b111))
begin
`ASSERT(w_mov);
`ASSERT(!w_div);
`ASSERT(!w_mpy);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU));
`ASSERT(w_rB);
`ASSERT(w_dcdA[4] == (i_gie));
`ASSERT(w_dcdB[4] == (i_gie));
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[22:19]);
 
`ASSERT(w_cis_op == 5'h0d);
 
`ASSERT(w_cond == 4'h8);
`ASSERT(!w_wF);
end else
`ASSERT(!w_mov);
 
//
// Divide instruction
always @(*)
if ((!iword[`CISBIT])&&(iword[26:23]==4'b0111))
begin
`ASSERT(w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
`ASSERT((w_rA)&&(w_wR));
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111));
end else
`ASSERT(!w_div);
 
//
// Comparison instructions
always @(*)
if ((!iword[`CISBIT])&&(iword[26:23]==4'b1000))
begin
`ASSERT(w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
`ASSERT((w_rA)&&(!w_wR)&&(!w_ALU));
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT(w_wF);
end else if ((iword[`CISBIT])&&(iword[26:24]==3'b011))
begin
`ASSERT(w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
`ASSERT((w_rA)&&(!w_wR)&&(!w_ALU));
`ASSERT(w_rB == iword[`CISIMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[22:19]);
 
`ASSERT(w_cis_op == 5'h10);
 
`ASSERT(w_cond == 4'h8);
if (iword[`CISIMMSEL])
`ASSERT(w_I == { {(23-3){iword[18]}}, iword[18:16] });
else
`ASSERT(w_I == { {(23-7){iword[22]}}, iword[22:16] });
`ASSERT(w_wF);
end else
`ASSERT(!w_cmptst);
 
always @(posedge i_clk)
if ((f_new_insn)&&($past(w_cmptst)))
`ASSERT(o_ALU);
 
//
// Memory instructions
always @(*)
if ((!iword[`CISBIT])&&(
(iword[26:23]==4'b1001) // Word
||(iword[26:23]==4'b1010) // Half-word, or short
||(iword[26:23]==4'b1011))) // Byte ops
begin
`ASSERT(w_mem);
`ASSERT(w_sto == iword[22]);
`ASSERT(!w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
if (w_sto)
`ASSERT((w_rA)&&(!w_wR));
else
`ASSERT((!w_rA)&&(w_wR));
`ASSERT(!w_ALU);
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT(!w_wF);
end else if ((iword[`CISBIT])&&(iword[26:25]==2'b10))
begin
`ASSERT(w_mem);
`ASSERT(w_sto == iword[24]);
`ASSERT(!w_cmptst);
`ASSERT(!w_div);
`ASSERT(!w_ldi);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(!w_mpy);
if (w_sto)
`ASSERT((w_rA)&&(!w_wR));
else
`ASSERT((!w_rA)&&(w_wR));
`ASSERT(!w_ALU);
`ASSERT(w_rB);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
if (iword[`CISIMMSEL])
`ASSERT(w_dcdB[3:0] == iword[22:19]);
else
`ASSERT(w_dcdB[3:0] == `CPU_SP_REG);
 
if (w_sto)
`ASSERT(w_cis_op == 5'h13);
else
`ASSERT(w_cis_op == 5'h12);
 
`ASSERT(w_cond == 4'h8);
`ASSERT(!w_wF);
end else begin
`ASSERT(!w_sto);
`ASSERT(!w_mem);
end
 
always @(*)
if (w_sto)
`ASSERT(w_mem);
 
//
// LDI -- Load immediate
always @(*)
if ((!iword[`CISBIT])&&(w_op[4:1] == 4'hc))
begin
`ASSERT(w_ldi);
`ASSERT(!w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT((!w_rA)&&(w_wR)&&(!w_ALU));
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(w_rB == 1'b0);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond == 4'h8);
`ASSERT(!w_wF);
 
`ASSERT(w_Iz == (iword[22:0] == 0));
`ASSERT(w_I[22:0] == iword[22:0]);
end else if ((iword[`CISBIT])&&(iword[26:24] == 3'b110))
begin
`ASSERT(w_ldi);
`ASSERT(!w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT((!w_rA)&&(w_wR)&&(!w_ALU));
`ASSERT(!w_special);
`ASSERT(!w_fpu);
`ASSERT(w_rB == 1'b0);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
 
`ASSERT(w_cis_op[4:1] == 4'hc);
 
`ASSERT(w_cond == 4'h8);
`ASSERT(!w_wF);
 
`ASSERT(w_Iz == (iword[23:16] == 0));
`ASSERT(w_I[22:0] == { {(23-8){iword[23]}}, iword[23:16] });
end else
`ASSERT(!w_ldi);
`endif // IDECODE
 
always @(posedge i_clk)
if ((f_new_insn)&&($past(w_ldi)))
`ASSERT(o_ALU);
 
`ifdef IDECODE
always @(*)
if ((w_break)||(w_lock)||(w_sim)||(w_noop))
`ASSERT(w_special);
 
 
//
// FPU -- Floating point instructions
always @(*)
if ((!iword[`CISBIT])&&(OPT_FPU)&&(
(w_cis_op[4:1] == 4'hd)
||(w_cis_op[4:1] == 4'he)
||(w_cis_op[4:1] == 4'hf))
&&(iword[30:28] != 3'h7))
begin
`ASSERT(w_fpu);
`ASSERT(!w_ldi);
`ASSERT(!w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
`ASSERT((w_wR)&&(!w_ALU));
if ((w_cis_op == 5'he)||(w_cis_op == 5'hf))
`ASSERT(!w_rA);
else
`ASSERT(w_rA);
`ASSERT(!w_special);
`ASSERT(w_rB == iword[`IMMSEL]);
`ASSERT(w_dcdA[4] == i_gie);
`ASSERT(w_dcdB[4] == i_gie);
`ASSERT(w_dcdA[3:0] == iword[30:27]);
`ASSERT(w_dcdB[3:0] == iword[17:14]);
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000));
`ASSERT(w_cond[2:0] == iword[21:19]);
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111));
end else
`ASSERT((!w_fpu)||(!OPT_FPU));
 
//
// Special instructions
always @(*)
if ((!iword[`CISBIT])&&(
(w_cis_op == 5'h1c)
||(w_cis_op == 5'h1d)
||(w_cis_op == 5'h1e)
||(w_cis_op == 5'h1f))
&&((iword[30:28] == 3'h7)||(!OPT_FPU)))
begin
`ASSERT(w_special);
if (w_cis_op == 5'h1c)
begin
`ASSERT(w_break);
`ASSERT(!w_lock);
`ASSERT(!w_sim);
`ASSERT(!w_noop);
end else if (w_cis_op == 5'h1d)
begin
`ASSERT(!w_break);
`ASSERT( w_lock);
`ASSERT(!w_sim);
`ASSERT(!w_noop);
end else if (w_cis_op == 5'h1e)
begin
`ASSERT(!w_break);
`ASSERT(!w_lock);
`ASSERT( w_sim);
`ASSERT(!w_noop);
end else begin
`ASSERT(!w_break);
`ASSERT(!w_lock);
`ASSERT(!w_sim);
`ASSERT( w_noop);
end
`ASSERT((!w_fpu)||(!OPT_FPU));
`ASSERT(!w_ldi);
`ASSERT(!w_mpy);
`ASSERT(!w_div);
`ASSERT(!w_cmptst);
`ASSERT(!w_mem);
`ASSERT(!w_sto);
`ASSERT(!w_mov);
`ASSERT(!w_brev);
`ASSERT(!w_ldilo);
 
`ASSERT((!w_rA)&&(!w_rB)&&(!w_wR)&&(!w_ALU));
 
`ASSERT(w_cis_op == w_op);
 
`ASSERT(w_cond == 4'h8);
`ASSERT(!w_wF);
end else begin
`ASSERT(!w_special);
`ASSERT(!w_break);
`ASSERT(!w_lock);
`ASSERT(!w_sim);
`ASSERT(!w_noop);
end
`endif
 
generate if (OPT_EARLY_BRANCHING)
begin
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_ce))&&(!$past(i_reset))&&(!i_reset))
begin
if ($past(pf_valid))
begin
if ($past(o_ljmp))
begin
// 2nd half of LW (PC),PC
`ASSERT(o_early_branch);
`ASSERT(o_early_branch_stb);
end else if ((!$past(iword[`CISBIT]))&&($past(w_add))
&&(!$past(w_rB))
&&($past(w_cond[3]))
&&(o_dcdR[4:0]=={ i_gie, 4'hf }))
begin
// ADD #x,PC
`ASSERT(o_early_branch);
`ASSERT(o_early_branch_stb);
end else if ((!$past(iword[`CISBIT]))
&&($past(w_cis_op == 5'h12))
&&($past(w_rB))
&&($past(w_cond[3]))
&&(o_zI)
&&(o_dcdB[4:0]=={ i_gie, 4'hf })
&&(o_dcdR[4:0]=={ i_gie, 4'hf }))
begin
// LW (PC),PC
`ASSERT(!o_early_branch);
`ASSERT(!o_early_branch_stb);
end else if ((OPT_CIS)&&($past(o_phase))
&&($past(w_cis_op == 5'h12))
&&($past(w_rB))
&&($past(w_cond[3]))
&&($past(w_Iz))
&&($past(w_dcdB_pc))
&&($past(w_dcdR_pc))
&&(o_dcdR[4:0]=={ i_gie, 4'hf }))
begin
// (CIS) LW (PC),PC
`ASSERT(!o_early_branch);
`ASSERT(!o_early_branch_stb);
end else begin
`ASSERT(!o_early_branch);
end
end else if ((OPT_CIS)&&($past(o_phase)))
begin
if (($past(w_cis_op == 5'h12))
&&($past(w_rB))
&&($past(w_cond[3]))
&&($past(w_Iz))
&&($past(w_dcdB_pc))
&&($past(w_dcdR_pc)))
begin
// (CIS) LW (PC),PC
`ASSERT(!o_early_branch);
`ASSERT(!o_early_branch_stb);
end else begin
`ASSERT(!o_early_branch);
`ASSERT(!o_early_branch_stb);
end
end
end else if (!i_reset)
`ASSERT(!o_early_branch_stb);
 
// // CIS instruction 16'hfcf8 decodes into:
// // 1.1111.100.1.1111.0000
// // = LW (PC),PC
// always @(*)
// assume(i_instruction[31:16] != 16'hfcf8);
 
end else begin
always @(*)
`ASSERT(!o_early_branch_stb);
always @(*)
`ASSERT(!o_early_branch);
end endgenerate
 
always @(*)
if (o_early_branch_stb)
`ASSERT(o_early_branch);
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_early_branch_stb))&&(!$past(pf_valid)))
`ASSERT(!o_early_branch_stb);
 
always @(*)
if (!OPT_LOCK)
`ASSERT(!o_lock);
 
generate if (OPT_CIS)
begin : F_OPT_CIS
always @(*)
if ((OPT_PIPELINED)&&(!o_valid))
`ASSERT(!o_phase);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
begin
if ((o_phase)&&($past(i_ce)))
`ASSERT((iword[30:16] == $past(i_instruction[14:0]))
&&(iword[`CISBIT]));
else if (!o_phase)
`ASSERT(iword == i_instruction);
 
if ((!$past(o_phase))&&($past(i_ce))
&&($past(pf_valid))
&&(!$past(i_illegal))
&&(!$past(w_ljmp_dly))
&&($past(i_instruction[`CISBIT]))
&&((!$past(w_dcdR_pc))
||(!$past(w_wR))))
`ASSERT(o_phase);
else if (($past(o_phase))&&($past(i_ce)))
`ASSERT(!o_phase);
if (($past(i_ce))&&(!$past(o_phase))
&&($past(i_illegal))&&($past(i_pf_valid)))
`ASSERT((o_illegal)&&(!o_phase));
 
`ASSERT((!o_phase)||(!o_ljmp));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_stalled))&&($past(pf_valid))
&&($past(i_ce)))
begin
`ASSERT(o_pc[0] == 1'b0);
if (!$past(iword[`CISBIT]))
begin
`ASSERT(o_pc[1:0]==2'b00);
`ASSERT(o_pc[AW+1:2] == $past(i_pc[AW+1:2])+1'b1);
end else if ($past(iword[`CISBIT])&&($past(o_phase)))
`ASSERT(o_pc[(AW+1):1] == $past(o_pc[(AW+1):1]) + 1'b1);
else if ($past(iword[`CISBIT]))
begin
`ASSERT(o_pc[(AW+1):1] == { $past(i_pc[(AW+1):2]), 1'b1});
if (o_valid)
begin
`ASSERT(o_pc[1]);
`ASSERT((o_illegal)||(o_phase));
end
end
end
 
 
always @(*)
if (iword[`CISBIT])
begin
`ASSERT((!w_ldi)||(w_I == { {(23-8){iword[23]}}, iword[23:16] }));
`ASSERT((w_ldi)||(iword[`CISIMMSEL])
||(w_I == { {(23-7){iword[22]}}, iword[22:16] }));
`ASSERT((w_ldi)||(!iword[`CISIMMSEL])
||(w_I == { {(23-3){iword[18]}}, iword[18:16] }));
end else begin
`ASSERT((!w_ldi)||(w_I == iword[22:0]));
`ASSERT((!w_mov)||(w_I == { {(23-13){iword[12]}}, iword[12:0] }));
`ASSERT((w_ldi)||(w_mov)||(iword[`IMMSEL])
||(w_I == { {(23-18){iword[17]}}, iword[17:0] }));
`ASSERT((w_ldi)||(w_mov)||(!iword[`IMMSEL])
||(w_I == { {(23-14){iword[13]}}, iword[13:0] }));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(o_phase)&&($past(i_ce)))
`ASSERT(($past(i_instruction[`CISBIT]))
&&(r_nxt_half[14:0]==$past(i_instruction[14:0])));
end else begin
 
always @(*)
begin
`ASSERT((o_phase)||(iword[30:0] == i_instruction[30:0]));
`ASSERT(o_phase == 1'b0);
`ASSERT(o_pc[0] == 1'b0);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_ce))&&($past(i_pf_valid)))
`ASSERT(o_pc[AW+1:2] == $past(i_pc[AW+1:2]) + 1'b1);
else if (f_past_valid)
`ASSERT(o_pc == $past(o_pc));
 
always @(*)
`ASSERT(o_pc[1:0] == 2'b00);
 
always @(*)
`ASSERT((!w_ldi)||(w_I == iword[22:0]));
always @(*)
`ASSERT((!w_mov)||(w_I == { {(23-13){iword[12]}}, iword[12:0] }));
always @(*)
`ASSERT((w_ldi)||(w_mov)||(iword[`IMMSEL])
||(w_I == { {(23-18){iword[17]}}, iword[17:0] }));
always @(*)
`ASSERT((w_ldi)||(w_mov)||(!iword[`IMMSEL])
||(w_I == { {(23-14){iword[13]}}, iword[13:0] }));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_ce))&&(!$past(i_reset)))
`ASSERT((!$past(i_instruction[`CISBIT]))
||(!$past(pf_valid))||(o_illegal));
end endgenerate
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))&&($past(w_fpu)))
begin
if (OPT_FPU)
`ASSERT(o_FP);
else if (!$past(w_special))
`ASSERT(o_illegal);
end
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))&&($past(w_lock)))
begin
if (OPT_LOCK)
`ASSERT(o_lock);
else
`ASSERT(o_illegal);
end
 
wire [20:0] f_next_pipe_I, f_this_pipe_I;
assign f_this_pipe_I = r_I[22:2];
assign f_next_pipe_I = r_I[22:2]+1'b1;
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
begin
if (OPT_OPIPE)
begin
if (($past(i_ce))
&&(($past(pf_valid))||($past(o_phase))))
begin
if ((!$past(o_M))||(!o_M))
`ASSERT(!o_pipe);
else if ($past(o_op[0])!=o_op[0])
`ASSERT(!o_pipe);
else if ($past(o_rB)!=o_rB)
`ASSERT(!o_pipe);
else if ((o_rB)&&($past(o_dcdB) != o_dcdB))
`ASSERT(!o_pipe);
else if (($past(o_wR))
&&($past(o_dcdR[3:1]) == 3'h7))
`ASSERT(!o_pipe);
// else if ((o_wR)&&(o_dcdR[3:1] == 3'h7))
// `ASSERT(!o_pipe);
else if (o_wR != $past(o_wR))
`ASSERT(!o_pipe);
else if ((o_wR)&&($past(o_dcdR) == o_dcdB))
`ASSERT(!o_pipe);
else if ((o_wR)&&(o_dcdB[3:1] == 3'h7))
`ASSERT(!o_pipe);
else if (($past(o_cond) != 4'h8)
&&($past(o_cond) != o_cond))
`ASSERT(!o_pipe);
else if ($past(r_I[22])!=r_I[22])
`ASSERT(!o_pipe);
else if (r_I[22:0] - $past(r_I[22:0])>23'h4)
`ASSERT(!o_pipe);
else if (!$past(o_valid))
`ASSERT(!o_pipe);
// else
// assert(o_pipe);
end else if ($past(i_stalled))
`ASSERT(o_pipe == $past(o_pipe));
end
end
 
always @(*)
`ASSERT((OPT_OPIPE)||(!o_pipe));
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))
&&($past(i_pf_valid))&&($past(w_mpy)))
`ASSERT((OPT_MPY)||(o_illegal));
 
always @(*)
if (o_valid)
`ASSERT((!o_phase)||(!o_early_branch));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&($past(o_ljmp))&&($past(!i_stalled)))
`ASSERT(!o_valid);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_early_branch_stb)))
begin
`ASSERT(!o_phase);
if (!$past(i_stalled))
`ASSERT(!o_valid);
`ASSERT(!o_ljmp);
end
 
// Unless another valid instruction comes along, once o_ljmp is asserted
// it should stay asserted until either a reset or an early branch
// strobe.
always @(posedge i_clk)
if ((OPT_EARLY_BRANCHING)&&(f_past_valid)
&&($past(o_ljmp))&&(!$past(pf_valid))
&&(!$past(i_reset))&&(!$past(o_early_branch_stb)))
`ASSERT(o_ljmp);
 
// o_ljmp should only ever be asserted following a valid prefetch
// input. Hence, if the prefetch input isn't valid, then o_ljmp
// should be left low
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_ljmp))
&&( (!$past(pf_valid)) || (!$past(i_ce)) )
&&( !$past(o_phase) )
&&(!$past(i_reset))&&(!$past(o_early_branch_stb)))
`ASSERT(!o_ljmp);
 
always @(posedge i_clk)
if ((OPT_EARLY_BRANCHING)&&(f_past_valid)&&($past(o_ljmp))&&(!o_ljmp)
&&(!$past(i_reset)))
`ASSERT((o_early_branch_stb)&&(!o_valid));
 
always @(posedge i_clk)
`ASSERT((!o_early_branch_stb)||(!o_ljmp));
 
always @(posedge i_clk)
`ASSERT((!o_valid)||(!o_ljmp)||(o_phase == o_pc[1]));
 
always @(posedge i_clk)
if (!OPT_CIS)
`ASSERT(!o_phase);
else if (!f_insn_word[31])
`ASSERT(!o_phase);
else if (o_phase)
`ASSERT(o_pc[1]);
 
always @(*)
if ((o_early_branch)&&(!o_early_branch_stb))
`ASSERT(!o_pipe);
 
always @(*)
if (o_ljmp)
`ASSERT(!o_pipe);
 
always @(*)
`ASSERT(o_dcdR == o_dcdA);
 
always @(*)
if ((o_valid)&&(o_phase))
begin
`ASSERT(!o_illegal);
`ASSERT(o_pc[1]);
`ASSERT(f_insn_word[31]);
end
 
always @(*)
`ASSERT(o_branch_pc[1:0] == 2'b00);
always @(*)
`ASSERT(o_pc[0] == 1'b0);
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_pf_valid))&&(i_pf_valid))
`ASSUME((i_reset)||($stable(i_gie)));
 
wire fc_illegal, fc_wF, fc_ALU, fc_M, fc_DV, fc_FP, fc_break,
fc_lock, fc_wR, fc_rA, fc_rB, fc_prepipe, fc_sim;
wire [6:0] fc_dcdR, fc_dcdA, fc_dcdB;
wire [31:0] fc_I;
wire [3:0] fc_cond;
wire [3:0] fc_op;
wire [22:0] fc_sim_immv;
f_idecode #(
.ADDRESS_WIDTH(AW),
.OPT_MPY(OPT_MPY),
.OPT_EARLY_BRANCHING(OPT_EARLY_BRANCHING),
.OPT_DIVIDE(OPT_DIVIDE),
.OPT_FPU(OPT_FPU),
.OPT_CIS(OPT_CIS),
.OPT_LOCK(OPT_LOCK),
.OPT_OPIPE(OPT_OPIPE),
.OPT_SIM(OPT_SIM)
) formal_decoder(
f_insn_word, o_phase, f_insn_gie,
fc_illegal,
fc_dcdR, fc_dcdA,fc_dcdB, fc_I, fc_cond, fc_wF, fc_op,
fc_ALU, fc_M, fc_DV, fc_FP, fc_break, fc_lock,
fc_wR, fc_rA, fc_rB, fc_prepipe, fc_sim, fc_sim_immv);
 
always @(posedge i_clk)
if ((o_valid)&&(fc_illegal))
assert(o_illegal);
 
always @(posedge i_clk)
if ((o_valid)&&(!o_illegal))
begin
`ASSERT(fc_dcdR== o_dcdR); //
`ASSERT(fc_dcdA== o_dcdA); //
`ASSERT(fc_dcdB== o_dcdB); //
`ASSERT(fc_I == o_I);
`ASSERT(o_zI == (fc_I == 0));
`ASSERT(fc_cond== o_cond);
`ASSERT(fc_wF == o_wF);
`ASSERT(fc_op == o_op);
`ASSERT(fc_ALU == o_ALU);
`ASSERT(fc_M == o_M);
`ASSERT(fc_DV == o_DV);
`ASSERT(fc_FP == o_FP);
`ASSERT(fc_break== o_break);
`ASSERT(fc_lock == o_lock);
`ASSERT(fc_wR == o_wR);
`ASSERT(fc_rA == o_rA);
`ASSERT(fc_rB == o_rB);
`ASSERT(fc_sim == o_sim);
`ASSERT(fc_sim_immv == o_sim_immv);
`ASSERT(fc_prepipe == r_insn_is_pipeable);
end else
`ASSERT(!r_insn_is_pipeable);
 
always @(*)
if (o_phase)
`ASSERT(r_nxt_half[14:0] == f_insn_word[14:0]);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_ce))&&(o_valid)&&(!$past(i_reset)))
begin
`ASSERT(((fc_illegal)
||$past((i_illegal)&&(!o_phase))
||$past((o_illegal)&&( o_phase)))== o_illegal);
end
 
always @(posedge i_clk)
if ((!o_valid)||(o_illegal))
`ASSERT(!r_insn_is_pipeable);
 
generate if ((OPT_CIS)&&(OPT_EARLY_BRANCHING))
begin
 
always @(*)
if ((o_valid)
// LW
&&(o_M)&&(o_op[2:0]==3'b010)
// Zero immediate
&&(o_zI)
// Unconditional
&&(o_cond[3])
// From PC to PC
&&(o_dcdR[5])&&(o_dcdB[5]))
`ASSERT((o_ljmp)
||((f_insn_word[31])&&(o_phase || o_illegal)));
else if (o_valid)
`ASSERT(!o_ljmp);
 
end endgenerate
 
`endif // FORMAL
endmodule
/memops.v
6,7 → 6,7
//
// Purpose: A memory unit to support a CPU.
//
// In the interests of code simplicity, this memory operator is
// In the interests of code simplicity, this memory operator is
// susceptible to unknown results should a new command be sent to it
// before it completes the last one. Unpredictable results might then
// occurr.
19,7 → 19,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC
// Copyright (C) 2015,2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
43,32 → 43,22
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module memops(i_clk, i_reset, i_stb, i_lock,
module memops(i_clk, i_rst, i_stb, i_lock,
i_op, i_addr, i_data, i_oreg,
o_busy, o_valid, o_err, o_wreg, o_result,
o_wb_cyc_gbl, o_wb_cyc_lcl,
o_wb_stb_gbl, o_wb_stb_lcl,
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data
`ifdef FORMAL
, f_nreqs, f_nacks, f_outstanding
`endif
);
parameter ADDRESS_WIDTH=30;
parameter [0:0] IMPLEMENT_LOCK=1'b1,
WITH_LOCAL_BUS=1'b1,
OPT_ALIGNMENT_ERR=1'b1,
OPT_ZERO_ON_IDLE=1'b0;
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data);
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0, WITH_LOCAL_BUS=0;
localparam AW=ADDRESS_WIDTH;
input wire i_clk, i_reset;
input wire i_stb, i_lock;
input i_clk, i_rst;
input i_stb, i_lock;
// CPU interface
input wire [2:0] i_op;
input wire [31:0] i_addr;
input wire [31:0] i_data;
input wire [4:0] i_oreg;
input [2:0] i_op;
input [31:0] i_addr;
input [31:0] i_data;
input [4:0] i_oreg;
// CPU outputs
output wire o_busy;
output reg o_valid;
85,91 → 75,51
output reg [31:0] o_wb_data;
output reg [3:0] o_wb_sel;
// Wishbone inputs
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [31:0] i_wb_data;
// Formal
parameter F_LGDEPTH = 2;
`ifdef FORMAL
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
`endif
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
 
reg misaligned;
 
generate if (OPT_ALIGNMENT_ERR)
begin : GENERATE_ALIGNMENT_ERR
always @(*)
casez({ i_op[2:1], i_addr[1:0] })
4'b01?1: misaligned = i_stb; // Words must be halfword aligned
4'b0110: misaligned = i_stb; // Words must be word aligned
4'b10?1: misaligned = i_stb; // Halfwords must be aligned
// 4'b11??: misaligned <= 1'b0; Byte access are never misaligned
default: misaligned = 1'b0;
endcase
end else
always @(*) misaligned = 1'b0;
endgenerate
 
reg r_wb_cyc_gbl, r_wb_cyc_lcl;
wire gbl_stb, lcl_stb;
assign lcl_stb = (i_stb)&&(WITH_LOCAL_BUS!=0)&&(i_addr[31:24]==8'hff)
&&(!misaligned);
assign gbl_stb = (i_stb)&&((WITH_LOCAL_BUS==0)||(i_addr[31:24]!=8'hff))
&&(!misaligned);
assign lcl_stb = (i_stb)&&(WITH_LOCAL_BUS!=0)&&(i_addr[31:24]==8'hff);
assign gbl_stb = (i_stb)&&((WITH_LOCAL_BUS==0)||(i_addr[31:24]!=8'hff));
 
initial r_wb_cyc_gbl = 1'b0;
initial r_wb_cyc_lcl = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
end else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl))
begin
if ((i_wb_ack)||(i_wb_err))
if (i_rst)
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
end else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl))
begin
if ((i_wb_ack)||(i_wb_err))
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
end
end else if (i_stb) // New memory operation
begin // Grab the wishbone
r_wb_cyc_lcl <= lcl_stb;
r_wb_cyc_gbl <= gbl_stb;
end
end else begin // New memory operation
// Grab the wishbone
r_wb_cyc_lcl <= (lcl_stb);
r_wb_cyc_gbl <= (gbl_stb);
end
initial o_wb_stb_gbl = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_wb_stb_gbl <= 1'b0;
else if ((i_wb_err)&&(r_wb_cyc_gbl))
o_wb_stb_gbl <= 1'b0;
else if (o_wb_cyc_gbl)
o_wb_stb_gbl <= (o_wb_stb_gbl)&&(i_wb_stall);
else
// Grab wishbone on any new transaction to the gbl bus
o_wb_stb_gbl <= (gbl_stb);
 
initial o_wb_stb_lcl = 1'b0;
if (o_wb_cyc_gbl)
o_wb_stb_gbl <= (o_wb_stb_gbl)&&(i_wb_stall);
else
o_wb_stb_gbl <= gbl_stb; // Grab wishbone on new operation
always @(posedge i_clk)
if (i_reset)
o_wb_stb_lcl <= 1'b0;
else if ((i_wb_err)&&(r_wb_cyc_lcl))
o_wb_stb_lcl <= 1'b0;
else if (o_wb_cyc_lcl)
o_wb_stb_lcl <= (o_wb_stb_lcl)&&(i_wb_stall);
else
// Grab wishbone on any new transaction to the lcl bus
o_wb_stb_lcl <= (lcl_stb);
if (o_wb_cyc_lcl)
o_wb_stb_lcl <= (o_wb_stb_lcl)&&(i_wb_stall);
else
o_wb_stb_lcl <= lcl_stb; // Grab wishbone on new operation
 
reg [3:0] r_op;
initial o_wb_we = 1'b0;
initial o_wb_data = 0;
initial o_wb_sel = 0;
always @(posedge i_clk)
if (i_stb)
begin
o_wb_we <= i_op[0];
if (OPT_ZERO_ON_IDLE)
if (i_stb)
begin
o_wb_we <= i_op[0];
casez({ i_op[2:1], i_addr[1:0] })
`ifdef ZERO_ON_IDLE
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 };
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] };
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 };
176,62 → 126,58
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 };
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 };
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] };
default: o_wb_data <= i_data;
endcase
end else
casez({ i_op[2:1], i_addr[1:0] })
`else
4'b10??: o_wb_data <= { (2){ i_data[15:0] } };
4'b11??: o_wb_data <= { (4){ i_data[7:0] } };
`endif
default: o_wb_data <= i_data;
endcase
 
o_wb_addr <= i_addr[(AW+1):2];
casez({ i_op[2:1], i_addr[1:0] })
4'b01??: o_wb_sel <= 4'b1111;
4'b100?: o_wb_sel <= 4'b1100;
4'b101?: o_wb_sel <= 4'b0011;
4'b1100: o_wb_sel <= 4'b1000;
4'b1101: o_wb_sel <= 4'b0100;
4'b1110: o_wb_sel <= 4'b0010;
4'b1111: o_wb_sel <= 4'b0001;
default: o_wb_sel <= 4'b1111;
endcase
r_op <= { i_op[2:1] , i_addr[1:0] };
end else if ((OPT_ZERO_ON_IDLE)&&(!o_wb_cyc_gbl)&&(!o_wb_cyc_lcl))
begin
o_wb_we <= 1'b0;
o_wb_addr <= 0;
o_wb_data <= 32'h0;
o_wb_sel <= 4'h0;
end
o_wb_addr <= i_addr[(AW+1):2];
`ifdef SET_SEL_ON_READ
if (i_op[0] == 1'b0)
o_wb_sel <= 4'hf;
else
`endif
casez({ i_op[2:1], i_addr[1:0] })
4'b01??: o_wb_sel <= 4'b1111;
4'b100?: o_wb_sel <= 4'b1100;
4'b101?: o_wb_sel <= 4'b0011;
4'b1100: o_wb_sel <= 4'b1000;
4'b1101: o_wb_sel <= 4'b0100;
4'b1110: o_wb_sel <= 4'b0010;
4'b1111: o_wb_sel <= 4'b0001;
default: o_wb_sel <= 4'b1111;
endcase
r_op <= { i_op[2:1] , i_addr[1:0] };
end
`ifdef ZERO_ON_IDLE
else if ((!o_wb_cyc_gbl)&&(!o_wb_cyc_lcl))
begin
o_wb_we <= 1'b0;
o_wb_addr <= 0;
o_wb_data <= 32'h0;
o_wb_sel <= 4'h0;
end
`endif
 
initial o_valid = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_valid <= 1'b0;
else
o_valid <= (((o_wb_cyc_gbl)||(o_wb_cyc_lcl))
&&(i_wb_ack)&&(!o_wb_we));
o_valid <= (!i_rst)&&((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_ack)&&(~o_wb_we);
initial o_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_err <= 1'b0;
else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl))
o_err <= i_wb_err;
else if ((i_stb)&&(!o_busy))
o_err <= misaligned;
else
o_err <= 1'b0;
o_err <= (!i_rst)&&((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_err);
assign o_busy = (o_wb_cyc_gbl)||(o_wb_cyc_lcl);
 
assign o_busy = (r_wb_cyc_gbl)||(r_wb_cyc_lcl);
 
always @(posedge i_clk)
if (i_stb)
o_wreg <= i_oreg;
if (i_stb)
o_wreg <= i_oreg;
always @(posedge i_clk)
if ((OPT_ZERO_ON_IDLE)&&(!i_wb_ack))
o_result <= 32'h0;
else begin
`ifdef ZERO_ON_IDLE
if (!i_wb_ack)
o_result <= 32'h0;
else
`endif
casez(r_op)
4'b01??: o_result <= i_wb_data;
4'b100?: o_result <= { 16'h00, i_wb_data[31:16] };
242,438 → 188,25
4'b1111: o_result <= { 24'h00, i_wb_data[ 7: 0] };
default: o_result <= i_wb_data;
endcase
end
 
reg lock_gbl, lock_lcl;
 
generate
if (IMPLEMENT_LOCK != 0)
begin
reg lock_gbl, lock_lcl;
 
initial lock_gbl = 1'b0;
initial lock_lcl = 1'b0;
 
always @(posedge i_clk)
if (i_reset)
begin
lock_gbl <= 1'b0;
lock_lcl <= 1'b0;
end else if (((i_wb_err)&&((r_wb_cyc_gbl)||(r_wb_cyc_lcl)))
||(misaligned))
begin
// Kill the lock if
// there's a bus error, or
// User requests a misaligned memory op
lock_gbl <= 1'b0;
lock_lcl <= 1'b0;
end else begin
// Kill the lock if
// i_lock goes down
// User starts on the global bus, then switches
// to local or vice versa
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl))
&&(!lcl_stb);
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl))
&&(!gbl_stb);
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
end
 
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
end else begin
 
assign o_wb_cyc_gbl = (r_wb_cyc_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl);
 
always @(*)
{ lock_gbl, lock_lcl } = 2'b00;
 
// Make verilator happy
// verilator lint_off UNUSED
wire [2:0] lock_unused;
assign lock_unused = { i_lock, lock_gbl, lock_lcl };
// verilator lint_on UNUSED
 
end endgenerate
 
`ifdef VERILATOR
always @(posedge i_clk)
if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl))
assert(!i_stb);
`endif
 
 
// Make verilator happy
// verilator lint_off UNUSED
generate if (AW < 22)
begin : TOO_MANY_ADDRESS_BITS
 
wire [(21-AW):0] unused_addr;
assign unused_addr = i_addr[23:(AW+2)];
 
end endgenerate
// verilator lint_on UNUSED
 
`ifdef FORMAL
`define ASSERT assert
`ifdef MEMOPS
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
reg f_past_valid;
initial f_past_valid = 0;
always @(posedge i_clk)
f_past_valid = 1'b1;
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
initial `ASSUME(!i_stb);
 
wire f_cyc, f_stb;
assign f_cyc = (o_wb_cyc_gbl)||(o_wb_cyc_lcl);
assign f_stb = (o_wb_stb_gbl)||(o_wb_stb_lcl);
 
`ifdef MEMOPS
`define MASTER fwb_master
`else
`define MASTER fwb_counter
`endif
 
fwb_master #(.AW(AW), .F_LGDEPTH(F_LGDEPTH),
.F_OPT_RMW_BUS_OPTION(IMPLEMENT_LOCK),
.F_OPT_DISCONTINUOUS(IMPLEMENT_LOCK))
f_wb(i_clk, i_reset,
f_cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
 
// Rule: Only one of the two CYC's may be valid, never both
always @(posedge i_clk)
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl));
 
// Rule: Only one of the two STB's may be valid, never both
always @(posedge i_clk)
`ASSERT((!o_wb_stb_gbl)||(!o_wb_stb_lcl));
 
// Rule: if WITH_LOCAL_BUS is ever false, neither the local STB nor CYC
// may be valid
always @(*)
if (!WITH_LOCAL_BUS)
begin
`ASSERT(!o_wb_cyc_lcl);
`ASSERT(!o_wb_stb_lcl);
end
 
// Rule: If the global CYC is ever true, the LCL one cannot be true
// on the next clock without an intervening idle of both
always @(posedge i_clk)
if ((f_past_valid)&&($past(r_wb_cyc_gbl)))
`ASSERT(!r_wb_cyc_lcl);
 
// Same for if the LCL CYC is true
always @(posedge i_clk)
if ((f_past_valid)&&($past(r_wb_cyc_lcl)))
`ASSERT(!r_wb_cyc_gbl);
 
// STB can never be true unless CYC is also true
always @(posedge i_clk)
if (o_wb_stb_gbl)
`ASSERT(r_wb_cyc_gbl);
always @(posedge i_clk)
if (o_wb_stb_lcl)
`ASSERT(r_wb_cyc_lcl);
 
// This core only ever has zero or one outstanding transaction(s)
always @(posedge i_clk)
if ((o_wb_stb_gbl)||(o_wb_stb_lcl))
`ASSERT(f_outstanding == 0);
else
`ASSERT((f_outstanding == 0)||(f_outstanding == 1));
 
// The LOCK function only allows up to two transactions (at most)
// before CYC must be dropped.
always @(posedge i_clk)
if ((o_wb_stb_gbl)||(o_wb_stb_lcl))
begin
if (IMPLEMENT_LOCK)
`ASSERT((f_outstanding == 0)||(f_outstanding == 1));
else
`ASSERT(f_nreqs <= 1);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(o_busy))
begin
 
// If i_stb doesn't change, then neither do any of the other
// inputs
if (($past(i_stb))&&(i_stb))
begin
`ASSUME($stable(i_op));
`ASSUME($stable(i_addr));
`ASSUME($stable(i_data));
`ASSUME($stable(i_oreg));
`ASSUME($stable(i_lock));
end
 
 
// No strobe's are allowed if a request is outstanding, either
// having been accepted by the bus or waiting to be accepted
// by the bus.
if ((f_outstanding != 0)||(f_stb))
`ASSUME(!i_stb);
/*
if (o_busy)
assert( (!i_stb)
||((!o_wb_stb_gbl)&&(!o_wb_stb_lcl)&&(i_lock)));
 
if ((f_cyc)&&($past(f_cyc)))
assert($stable(r_op));
*/
end
 
always @(*)
if (!IMPLEMENT_LOCK)
`ASSUME(!i_lock);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_cyc))&&($past(!i_lock)))
`ASSUME(!i_lock);
 
// Following any i_stb request, assuming we are idle, immediately
// begin a bus transaction
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_stb))
&&(!$past(f_cyc))&&(!$past(i_reset)))
begin
if ($past(misaligned))
begin
`ASSERT(!f_cyc);
`ASSERT(!o_busy);
`ASSERT(o_err);
`ASSERT(!o_valid);
end else begin
`ASSERT(f_cyc);
`ASSERT(o_busy);
end
end
 
always @(posedge i_clk)
if (o_busy)
`ASSUME(!i_stb);
 
always @(posedge i_clk)
if (o_wb_cyc_gbl)
`ASSERT((o_busy)||(lock_gbl));
 
always @(posedge i_clk)
if (o_wb_cyc_lcl)
`ASSERT((o_busy)||(lock_lcl));
 
always @(posedge i_clk)
if (f_outstanding > 0)
`ASSERT(o_busy);
 
// If a transaction ends in an error, send o_err on the output port.
always @(posedge i_clk)
if (f_past_valid)
begin
if ($past(i_reset))
`ASSERT(!o_err);
else if (($past(f_cyc))&&($past(i_wb_err)))
`ASSERT(o_err);
else if ($past(misaligned))
`ASSERT(o_err);
end
 
// Always following a successful ACK, return an O_VALID value.
always @(posedge i_clk)
if (f_past_valid)
begin
if ($past(i_reset))
`ASSERT(!o_valid);
else if(($past(f_cyc))&&($past(i_wb_ack))
&&(!$past(o_wb_we)))
`ASSERT(o_valid);
else if ($past(misaligned))
`ASSERT((!o_valid)&&(o_err));
else
`ASSERT(!o_valid);
end
 
//always @(posedge i_clk)
// if ((f_past_valid)&&($past(f_cyc))&&(!$past(o_wb_we))&&($past(i_wb_ack)))
 
/*
input wire [2:0] i_op;
input wire [31:0] i_addr;
input wire [31:0] i_data;
input wire [4:0] i_oreg;
// CPU outputs
output wire o_busy;
output reg o_valid;
output reg o_err;
output reg [4:0] o_wreg;
output reg [31:0] o_result;
*/
 
initial o_wb_we = 1'b0;
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_stb)))
begin
// On a write, assert o_wb_we should be true
assert( $past(i_op[0]) == o_wb_we);
 
// Word write
if ($past(i_op[2:1]) == 2'b01)
begin
`ASSERT(o_wb_sel == 4'hf);
`ASSERT(o_wb_data == $past(i_data));
end
 
// Halfword (short) write
if ($past(i_op[2:1]) == 2'b10)
begin
if (!$past(i_addr[1]))
begin
`ASSERT(o_wb_sel == 4'hc);
`ASSERT(o_wb_data[31:16] == $past(i_data[15:0]));
end else begin
`ASSERT(o_wb_sel == 4'h3);
`ASSERT(o_wb_data[15:0] == $past(i_data[15:0]));
end
end
 
if ($past(i_op[2:1]) == 2'b11)
begin
if ($past(i_addr[1:0])==2'b00)
begin
`ASSERT(o_wb_sel == 4'h8);
`ASSERT(o_wb_data[31:24] == $past(i_data[7:0]));
end
 
if ($past(i_addr[1:0])==2'b01)
begin
`ASSERT(o_wb_sel == 4'h4);
`ASSERT(o_wb_data[23:16] == $past(i_data[7:0]));
end
if ($past(i_addr[1:0])==2'b10)
begin
`ASSERT(o_wb_sel == 4'h2);
`ASSERT(o_wb_data[15:8] == $past(i_data[7:0]));
end
if ($past(i_addr[1:0])==2'b11)
begin
`ASSERT(o_wb_sel == 4'h1);
`ASSERT(o_wb_data[7:0] == $past(i_data[7:0]));
end
end
 
`ASSUME($past(i_op[2:1] != 2'b00));
end
 
// This logic is fixed in the definitions of the lock(s) above
// i.e., the user cna be stupid and this will still work
/*
always @(posedge i_clk)
if ((i_lock)&&(i_stb)&&(WITH_LOCAL_BUS))
begin
restrict((lock_gbl)||(i_addr[31:24] ==8'hff));
restrict((lock_lcl)||(i_addr[31:24]!==8'hff));
end
*/
 
always @(posedge i_clk)
if (o_wb_stb_lcl)
`ASSERT(o_wb_addr[29:22] == 8'hff);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(misaligned)))
begin
`ASSERT(!o_wb_cyc_gbl);
`ASSERT(!o_wb_cyc_lcl);
`ASSERT(!o_wb_stb_gbl);
`ASSERT(!o_wb_stb_lcl);
`ASSERT(o_err);
//OPT_ALIGNMENT_ERR=1'b0,
//OPT_ZERO_ON_IDLE=1'b0;
end
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
`ASSUME(!i_stb);
always @(*)
if (o_busy)
`ASSUME(!i_stb);
 
always @(posedge i_clk)
if ((f_past_valid)&&(IMPLEMENT_LOCK)
&&(!$past(i_reset))&&(!$past(i_wb_err))
&&(!$past(misaligned))
&&(!$past(lcl_stb))
&&($past(i_lock))&&($past(lock_gbl)))
assert(lock_gbl);
 
always @(posedge i_clk)
if ((f_past_valid)&&(IMPLEMENT_LOCK)
&&(!$past(i_reset))&&(!$past(i_wb_err))
&&(!$past(misaligned))
&&(!$past(lcl_stb))
&&($past(o_wb_cyc_gbl))&&($past(i_lock))
&&($past(lock_gbl)))
assert(o_wb_cyc_gbl);
 
always @(posedge i_clk)
if ((f_past_valid)&&(IMPLEMENT_LOCK)
&&(!$past(i_reset))&&(!$past(i_wb_err))
&&(!$past(misaligned))
&&(!$past(gbl_stb))
&&($past(o_wb_cyc_lcl))&&($past(i_lock))
&&($past(lock_lcl)))
assert(o_wb_cyc_lcl);
 
//
// Cover properties
//
always @(posedge i_clk)
cover(i_wb_ack);
 
// Cover a response on the same clock it is made
always @(posedge i_clk)
cover((o_wb_stb_gbl)&&(i_wb_ack));
 
// Cover a response a clock later
always @(posedge i_clk)
cover((o_wb_stb_gbl)&&(i_wb_ack));
 
 
generate if (WITH_LOCAL_BUS)
begin
 
// Same things on the local bus
always @(posedge i_clk)
cover((o_wb_cyc_lcl)&&(!o_wb_stb_lcl)&&(i_wb_ack));
always @(posedge i_clk)
cover((o_wb_stb_lcl)&&(i_wb_ack));
 
end endgenerate
 
`endif
endmodule
//
//
// Usage (from yosys):
// (BFOR) (!ZOI,ALIGN) (ZOI,ALIGN) (!ZOI,!ALIGN)
// Cells 230 226 281 225
// FDRE 114 116 116 116
// LUT2 17 23 76 19
// LUT3 9 23 17 20
// LUT4 15 4 11 14
// LUT5 18 18 7 15
// LUT6 33 18 54 38
// MUX7 16 12 2
// MUX8 8 1 1
//
//
/prefetch.v
6,31 → 6,25
//
// Purpose: This is a very simple instruction fetch approach. It gets
// one instruction at a time. Future versions should pipeline
// fetches and perhaps even cache results--this doesn't do that. It
// should, however, be simple enough to get things running.
// fetches and perhaps even cache results--this doesn't do that.
// It should, however, be simple enough to get things running.
//
// The interface is fascinating. The 'i_pc' input wire is just a
// suggestion of what to load. Other wires may be loaded instead. i_pc
// is what must be output, not necessarily input.
// The interface is fascinating. The 'i_pc' input wire is just
// a suggestion of what to load. Other wires may be loaded
// instead. i_pc is what must be output, not necessarily input.
//
// 20150919 -- Added support for the WB error signal. When reading an
// instruction results in this signal being raised, the pipefetch module
// will set an illegal instruction flag to be returned to the CPU together
// with the instruction. Hence, the ZipCPU can trap on it if necessary.
// 20150919 -- Added support for the WB error signal. When reading an
// instruction results in this signal being raised, the pipefetch
// module will set an illegal instruction flag to be returned to
// the CPU together with the instruction. Hence, the ZipCPU
// can trap on it if necessary.
//
// 20171020 -- Added a formal proof to prove that the module works. This
// also involved adding a req_addr register, and the logic associated
// with it.
//
// 20171113 -- Removed the req_addr register, replacing it with a bus abort
// capability.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC
// Copyright (C) 2015,2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
54,40 → 48,33
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
module prefetch(i_clk, i_reset, i_new_pc, i_clear_cache, i_stalled_n, i_pc,
o_insn, o_pc, o_valid, o_illegal,
// Flash requires a minimum of 4 clocks per byte to read, so that would be
// 4*(4bytes/32bit word) = 16 clocks per word read---and that's in pipeline
// mode which this prefetch does not support. In non--pipelined mode, the
// flash will require (16+6+6)*2 = 56 clocks plus 16 clocks per word read,
// or 72 clocks to fetch one instruction.
module prefetch(i_clk, i_rst, i_new_pc, i_clear_cache, i_stalled_n, i_pc,
o_i, o_pc, o_valid, o_illegal,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data);
parameter ADDRESS_WIDTH=30, DATA_WIDTH=32;
localparam AW=ADDRESS_WIDTH,
DW=DATA_WIDTH;
input wire i_clk, i_reset;
// CPU interaction wires
input wire i_new_pc, i_clear_cache, i_stalled_n;
// We ignore i_pc unless i_new_pc is true as well
input wire [(AW+1):0] i_pc;
output reg [(DW-1):0] o_insn; // Instruction read from WB
output wire [(AW+1):0] o_pc; // Address of that instruction
output reg o_valid; // If the output is valid
output reg o_illegal; // Result is from a bus err
parameter ADDRESS_WIDTH=32;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst, i_new_pc, i_clear_cache,
i_stalled_n;
input [(AW-1):0] i_pc;
output reg [31:0] o_i;
output wire [(AW-1):0] o_pc;
output reg o_valid;
// Wishbone outputs
output reg o_wb_cyc, o_wb_stb;
output wire o_wb_we;
output reg [(AW-1):0] o_wb_addr;
output wire [(DW-1):0] o_wb_data;
output wire [31:0] o_wb_data;
// And return inputs
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(DW-1):0] i_wb_data;
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
output reg o_illegal;
 
// Declare local variables
reg invalid;
 
// These are kind of obligatory outputs when dealing with a bus, that
// we'll set them here. Nothing's going to pay attention to these,
// though, this is primarily for form.
assign o_wb_we = 1'b0;
assign o_wb_data = 32'h0000;
 
96,497 → 83,56
// pipeline this, but for now let's just do one at a time.
initial o_wb_cyc = 1'b0;
initial o_wb_stb = 1'b0;
initial o_wb_addr= 0;
always @(posedge i_clk)
if ((i_reset)||((o_wb_cyc)&&((i_wb_ack)||(i_wb_err))))
begin
// End any bus cycle on a reset, or a return ACK
// or error.
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end else if ((!o_wb_cyc)&&(
// Start if the last instruction output was
// accepted, *and* it wasn't a bus error
// response
((i_stalled_n)&&(!o_illegal))
// Start if the last bus result ended up
// invalid
||(invalid)
// Start on any request for a new address
||(i_new_pc)))
begin
// Initiate a bus transaction
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end else if (o_wb_cyc)
begin
// If our request has been accepted, then drop the
// strobe line
if (!i_wb_stall)
o_wb_stb <= 1'b0;
 
// Abort on new-pc
// ... clear_cache is identical, save that it will
// immediately be followed by a new PC, so we don't
// need to worry about that other than to drop
// CYC and STB here.
if (i_new_pc)
if ((i_rst)||(i_wb_ack)||(i_wb_err))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end else if ((!o_wb_cyc)&&((i_stalled_n)||(!o_valid)))
begin // Initiate a bus cycle
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end else if (o_wb_cyc) // Independent of ce
begin
if (~i_wb_stall)
o_wb_stb <= 1'b0;
end
end
 
//
// If during the current bus request, a command came in from the CPU
// that will invalidate the results of that request, then we need to
// keep track of an "invalid" flag to remember that and so squash
// the result.
//
reg invalid;
initial invalid = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(!o_wb_cyc))
invalid <= 1'b0;
else if (i_new_pc)
invalid <= 1'b1;
if (!o_wb_cyc)
invalid <= 1'b0;
else if ((i_new_pc)||(i_clear_cache))
invalid <= (!o_wb_stb);
 
// The wishbone request address, o_wb_addr
//
// The rule regarding this address is that it can *only* be changed
// when no bus request is active. Further, since the CPU is depending
// upon this value to know what "PC" is associated with the instruction
// it is processing, we can't change until either the CPU has accepted
// our result, or it is requesting a new PC (and hence not using the
// output).
//
initial o_wb_addr= 0;
always @(posedge i_clk)
if (i_new_pc)
o_wb_addr <= i_pc[AW+1:2];
else if ((o_valid)&&(i_stalled_n)&&(!o_illegal))
o_wb_addr <= o_wb_addr + 1'b1;
if (i_new_pc)
o_wb_addr <= i_pc;
else if ((!o_wb_cyc)&&(i_stalled_n)&&(!invalid))
o_wb_addr <= o_wb_addr + 1'b1;
 
// The instruction returned is given by the data returned from the bus.
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
o_insn <= i_wb_data;
if ((o_wb_cyc)&&(i_wb_ack))
o_i <= i_wb_data;
 
//
// Finally, the flags associated with the prefetch. The rule is that
// if the output represents a return from the bus, then o_valid needs
// to be true. o_illegal will be true any time the last bus request
// resulted in an error. o_illegal is only relevant to the CPU when
// o_valid is also true, hence o_valid will be true even in the case
// of a bus error in our request.
//
initial o_valid = 1'b0;
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_new_pc)||(i_clear_cache))
begin
// On any reset, request for a new PC (i.e. a branch),
// or a request to clear our cache (i.e. the data
// in memory may have changed), we invalidate any
// output.
o_valid <= 1'b0;
o_illegal <= 1'b0;
end else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))
begin
// Otherwise, at the end of our bus cycle, the
// answer will be valid. Well, not quite. If the
// user requested something mid-cycle (i_new_pc)
// or (i_clear_cache), then we'll have to redo the
// bus request, so we aren't valid.
//
o_valid <= 1'b1;
o_illegal <= ( i_wb_err);
end else if (i_stalled_n)
begin
// Once the CPU accepts any result we produce, clear
// the valid flag, lest we send two identical
// instructions to the CPU.
//
o_valid <= 1'b0;
//
// o_illegal doesn't change ... that way we don't
// access the bus again until a new address request
// is given to us, via i_new_pc, or we are asked
// to check again via i_clear_cache
//
// o_illegal <= (!i_stalled_n);
end
if (i_rst)
begin
o_valid <= 1'b0;
o_illegal <= 1'b0;
end else if ((o_wb_cyc)&&(i_wb_ack))
begin
o_valid <= (!i_wb_err)&&(!invalid);
o_illegal <= ( i_wb_err)&&(!invalid);
end else if ((i_stalled_n)||(i_clear_cache))
begin
o_valid <= 1'b0;
o_illegal <= 1'b0;
end
 
// The o_pc output shares its value with the (last) wishbone address
assign o_pc = { o_wb_addr, 2'b00 };
 
// Make verilator happy
// verilator lint_off UNUSED
wire [1:0] unused;
assign unused = i_pc[1:0];
// verilator lint_on UNUSED
`ifdef FORMAL
localparam F_LGDEPTH=2;
reg f_past_valid;
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks,
f_outstanding;
reg [(AW-1):0] f_last_pc;
reg f_last_pc_valid;
reg [(AW-1):0] f_req_addr;
 
//
//
// Generic setup
//
//
`ifdef PREFETCH
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
// Assume a clock
 
// Keep track of a flag telling us whether or not $past()
// will return valid results
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid = 1'b1;
 
/////////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
/////////////////////////////////////////////////
 
// Assume we start from a reset condition
initial `ASSUME(i_reset);
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
// Some things to know from the CPU ... there will always be a
// i_new_pc request following any reset
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
`ASSUME(i_new_pc);
 
// There will also be a i_new_pc request following any request to clear
// the cache.
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_clear_cache)))
`ASSUME(i_new_pc);
 
//
//
// Let's make some assumptions about how long it takes our
// phantom bus and phantom CPU to respond.
//
// These delays need to be long enough to flush out any potential
// errors, yet still short enough that the formal method doesn't
// take forever to solve.
//
localparam F_CPU_DELAY = 4;
reg [4:0] f_cpu_delay;
// First, let's assume that any response from the bus comes back
// within F_WB_DELAY clocks
 
// Here's our delay assumption: We'll assume that the
// wishbone will always respond within F_WB_DELAY clock ticks
// of the beginning of any cycle.
//
// This includes both dropping the stall line, as well as
// acknowledging any request. While this may not be
// a reasonable assumption for a piped master, it should
// work here for us.
 
// Count the number of clocks it takes the CPU to respond to our
// instruction.
always @(posedge i_clk)
// If no instruction is ready, then keep our counter at zero
if ((i_reset)||(!o_valid)||(i_stalled_n))
f_cpu_delay <= 0;
else
// Otherwise, count the clocks the CPU takes to respond
f_cpu_delay <= f_cpu_delay + 1'b1;
 
`ifdef PREFETCH
// Only *assume* that we are less than F_CPU_DELAY if we are not
// integrated into the CPU
always @(posedge i_clk)
assume(f_cpu_delay < F_CPU_DELAY);
`endif
 
fwb_master #(.AW(AW), .DW(DW),.F_LGDEPTH(F_LGDEPTH),
.F_MAX_REQUESTS(1), .F_OPT_SOURCE(1),
.F_OPT_RMW_BUS_OPTION(0),
.F_OPT_DISCONTINUOUS(0))
f_wbm(i_clk, i_reset,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
/////////////////////////////////////////////////
//
//
// Assertions about our outputs
//
//
/////////////////////////////////////////////////
//
// Assertions about our wishbone control outputs first
// Prefetches don't write
always @(posedge i_clk)
if (o_wb_stb)
assert(!o_wb_we);
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_past_valid))
&&($past(i_clear_cache,2))
&&($past(o_wb_cyc,2)))
// Make sure any clear-cache transaction is aborted,
// *and* that no valid instructions get sent to the
// CPU
assert((!$past(o_wb_cyc))||(!o_wb_cyc));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&(o_valid))
assert(o_wb_addr == $past(o_wb_addr));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(!i_reset))&&($past(invalid)))
assert(o_wb_cyc);
 
// Any time the CPU accepts an instruction, assert that on the
// valid line will be low on the next clock
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&($past(i_stalled_n)))
assert(!o_valid);
 
// Since we only change our output on a response from the bus, we
// need to insist that the item has been read by the CPU before
// we go looking/asking for a next value.
//
// This routine should never be requesting a new instruction when
// one is valid--lest the CPU never accept the old instruction and we
// have nothing to do with the data when the bus request returns.
always @(*)
if (o_wb_cyc)
assert(!o_valid);
 
// If we just got a valid instruction from the wishbone, assert that
// the instruction is listed as valid on the next instruction cycle
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(o_wb_cyc))
&&($past(!i_clear_cache))
&&($past(i_wb_ack))&&(!$past(i_wb_err)))
begin
if (!invalid)
assert(o_valid);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_clear_cache)))
assert(!o_valid);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_past_valid))
&&($past(i_clear_cache,2))
&&($past(o_wb_cyc,2)))
// Make sure any clear-cache transaction is aborted,
// *and* that no valid instructions get sent to the
// CPU
assert(!o_valid);
 
//
// Assertions about our return responses
//
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(i_new_pc))&&(!$past(i_clear_cache))
&&($past(o_valid))&&(!$past(i_stalled_n)))
assert(o_valid == $past(o_valid));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&(o_valid))
begin
assert($stable(o_pc));
assert($stable(o_insn));
assert($stable(o_illegal));
end
 
//
// The o_illegal line is the one we use to remind us not to go
// back and retry the last value if it returned a bus error. Hence,
// let's assert that this line stays constant any time o_wb_cyc
// is low, and we haven't received any new requests.
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(i_new_pc))&&(!$past(i_clear_cache))
&&($past(!o_wb_cyc)))
assert(o_illegal == $past(o_illegal));
 
 
//
//
// Let's examine whether or not we "walk" though PC addresses one
// at a time like we expect.
//
initial f_last_pc_valid = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache)||(i_new_pc)||(invalid))
f_last_pc_valid <= 1'b0;
else if (o_valid)
f_last_pc_valid <= (!o_illegal);
 
// initial f_last_pc = 0;
always @(posedge i_clk)
if (o_valid)
f_last_pc <= o_pc[AW+1:2];
else if (f_last_pc_valid)
assert(o_pc[AW+1:2] == f_last_pc + 1'b1);
 
always @(*)
assert(o_pc[1:0] == 2'b00);
 
// If we are producing a new result, and no new-pc or clear cache
// has come through (i.e. f_last_pc_valid is true), then the resulting
// PC should be one more than the last time.
//
// The o_valid && !$past(o_valid) is necessary because f_last_pc_valid
// will be different by the second o_valid.
always @(posedge i_clk)
if ((f_past_valid)&&(o_valid)
&&(!$past(o_valid))&&(f_last_pc_valid))
assert(o_pc[AW+1:2] == (f_last_pc + 1'b1));
 
// Let's also keep track of the address the CPU wants us to return.
// Any time the CPU branches to a new_pc, we'll record that request.
// Likewise, any time an instruction is returned from the bus,
// we'll increment this address so as to automatically walk through
// memory.
//
always @(*)
assume(i_pc[1:0] == 2'b00);
initial f_req_addr = 0;
always @(posedge i_clk)
if (i_new_pc)
f_req_addr <= i_pc[AW+1:2];
else if ((!invalid)&&(o_wb_cyc)&&(i_wb_ack)&&(!i_wb_err))
f_req_addr <= f_req_addr + 1'b1;
 
// Let's also keep the formal methods on track. Any time we are
// requesting a value, it should either be from the req_addr, or if
// not a new value should've come in rendering this one invalid.
always @(posedge i_clk)
if (o_wb_cyc)
assert((invalid)||(f_req_addr == o_wb_addr));
// This isn't good enough for induction, so we'll need to
// constrain this further
else if ((!o_valid)&&(!i_new_pc)&&(!i_reset))
assert(f_req_addr == o_wb_addr);
 
// In this version, invalid should only ever be high for one cycle.
// CYC should be high on the cycle following--if ever.
always @(posedge i_clk)
if ((f_past_valid)&&($past(invalid)))
assert(!invalid);
 
(* anyconst *) reg [AW:0] const_addr;
(* anyconst *) reg [DW-1:0] const_insn;
 
wire f_this_addr, f_this_pc, f_this_req, f_this_data;
assign f_this_addr = (o_wb_addr == const_addr[AW-1:0]);
assign f_this_pc = (o_pc == { const_addr[AW-1:0], 2'b00 });
assign f_this_req = (i_pc == { const_addr[AW-1:0], 2'b00 });
assign f_this_data = (i_wb_data == const_insn);
 
reg f_addr_pending;
initial f_addr_pending = 1'b0;
always @(posedge i_clk)
if (i_reset)
f_addr_pending <= 1'b0;
else if (!o_wb_cyc)
f_addr_pending <= 1'b0;
else if ((o_wb_stb)&&(f_this_addr))
begin
if ((!i_wb_ack)&&(!i_wb_err))
f_addr_pending <= 1'b1;
end
 
always @(*)
if ((o_wb_stb)&&(f_this_addr)&&(!i_wb_stall))
begin
if (!const_addr[AW])
assume(!i_wb_err);
else
assume(!i_wb_ack);
if (i_wb_ack)
assume(f_this_data);
end else if ((o_wb_cyc)&&(f_addr_pending))
begin
if (!const_addr[AW])
assume(!i_wb_err);
else
assume(!i_wb_ack);
if (i_wb_ack)
assume(f_this_data);
end
 
always @(*)
if ((o_valid)&&(f_this_pc)&&(!o_illegal))
assert(o_insn == const_insn);
always @(*)
if ((o_valid)&&(f_this_pc))
assert(o_illegal == const_addr[AW]);
 
reg f_insn_pending;
 
initial f_insn_pending = 1'b0;
always @(posedge i_clk)
if (i_reset)
f_insn_pending <= 1'b0;
else if (i_clear_cache)
f_insn_pending <= 1'b0;
else if ((i_new_pc)&&(f_this_req))
f_insn_pending <= 1'b1;
else if ((o_valid)||(i_new_pc))
f_insn_pending <= 1'b0;
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc)&&(f_insn_pending))
assert(f_this_pc);
 
always @(posedge i_clk)
if (((f_past_valid)&&($past(o_wb_cyc))&&($past(f_insn_pending)))
&&(!$past(i_reset))&&(!$past(i_clear_cache))
&&(!$past(i_new_pc)))
begin
if(!o_wb_cyc)
assert((o_valid)&&(f_this_pc));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_wb_cyc))&&(!o_wb_cyc))
assert(!f_insn_pending);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc)&&(f_this_addr))
assert(f_addr_pending);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc))&&(f_insn_pending))
assert(f_this_addr);
`endif
assign o_pc = o_wb_addr;
endmodule
//
// Usage: (this) (mid) (past)
// Cells 167 230 175
// FDRE 67 97 69
// LUT1 1 1 1
// LUT2 1 3 3
// LUT3 31 63 33
// LUT4 5 3 3
// LUT5 1 3 3
// LUT6 2 1 3
// MUXCY 29 29 31
// XORCY 30 30 32
/zipcpu.v
3,7 → 3,7
// Filename: zipcpu.v
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//{{{
//
// Purpose: This is the top level module holding the core of the Zip CPU
// together. The Zip CPU is designed to be as simple as possible.
// (actual implementation aside ...) The instruction set is about as
49,7 → 49,7
//
//
// always @(posedge i_clk)
// if ((i_reset)||(clear_pipeline))
// if ((i_rst)||(clear_pipeline))
// (n)_valid = 0
// else if (n)_ce
// (n)_valid = 1
68,14 → 68,14
// Note that a stage can stall even if no instruction is loaded into
// it.
//
//}}}
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
//{{{
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or (at
90,7 → 90,7
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//}}}
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
98,13 → 98,7
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define CPU_SUB_OP 4'h0 // also a compare instruction
`define CPU_AND_OP 4'h1 // also a test instruction
`define CPU_BREV_OP 4'h8
`define CPU_MOV_OP 4'hd
//
`define CPU_CC_REG 4'he
`define CPU_PC_REG 4'hf
`define CPU_CLRCACHE_BIT 14 // Set to clear the I-cache, automatically clears
123,7 → 117,7
`include "cpudefs.v"
//
//
module zipcpu(i_clk, i_reset, i_interrupt,
module zipcpu(i_clk, i_rst, i_interrupt,
// Debug interface
i_halt, i_clear_pf_cache, i_dbg_reg, i_dbg_we, i_dbg_data,
o_dbg_stall, o_dbg_reg, o_dbg_cc,
137,14 → 131,12
// Accounting/CPU usage interface
o_op_stall, o_pf_stall, o_i_count
`ifdef DEBUG_SCOPE
, o_debug // , o_dcache_debug
, o_debug
`endif
);
// Parameters
//{{{
parameter [31:0] RESET_ADDRESS=32'h0100000;
parameter ADDRESS_WIDTH=30,
LGICACHE=12;
LGICACHE=8;
`ifdef OPT_MULTIPLY
parameter IMPLEMENT_MPY = `OPT_MULTIPLY;
`else
151,64 → 143,30
parameter IMPLEMENT_MPY = 0;
`endif
`ifdef OPT_DIVIDE
parameter [0:0] IMPLEMENT_DIVIDE = 1;
parameter IMPLEMENT_DIVIDE = 1;
`else
parameter [0:0] IMPLEMENT_DIVIDE = 0;
parameter IMPLEMENT_DIVIDE = 0;
`endif
`ifdef OPT_IMPLEMENT_FPU
parameter [0:0] IMPLEMENT_FPU = 1;
parameter IMPLEMENT_FPU = 1,
`else
parameter [0:0] IMPLEMENT_FPU = 0;
parameter IMPLEMENT_FPU = 0,
`endif
IMPLEMENT_LOCK=1;
`ifdef OPT_EARLY_BRANCHING
parameter [0:0] EARLY_BRANCHING = 1;
parameter EARLY_BRANCHING = 1;
`else
parameter [0:0] EARLY_BRANCHING = 0;
parameter EARLY_BRANCHING = 0;
`endif
`ifdef OPT_CIS
parameter [0:0] OPT_CIS = 1'b1;
`else
parameter [0:0] OPT_CIS = 1'b0;
`endif
`ifdef OPT_NO_USERMODE
localparam [0:0] OPT_NO_USERMODE = 1'b1;
`else
localparam [0:0] OPT_NO_USERMODE = 1'b0;
`endif
`ifdef OPT_PIPELINED
parameter [0:0] OPT_PIPELINED = 1'b1;
`else
parameter [0:0] OPT_PIPELINED = 1'b0;
`endif
`ifdef OPT_PIPELINED_BUS_ACCESS
localparam [0:0] OPT_PIPELINED_BUS_ACCESS = (OPT_PIPELINED);
`else
localparam [0:0] OPT_PIPELINED_BUS_ACCESS = 1'b0;
`endif
localparam [0:0] OPT_MEMPIPE = OPT_PIPELINED_BUS_ACCESS;
parameter [0:0] IMPLEMENT_LOCK=1;
localparam [0:0] OPT_LOCK=(IMPLEMENT_LOCK)&&(OPT_PIPELINED);
`ifdef OPT_DCACHE
parameter OPT_LGDCACHE = 10;
`else
parameter OPT_LGDCACHE = 0;
`endif
localparam [0:0] OPT_DCACHE = (OPT_LGDCACHE > 0);
 
parameter [0:0] WITH_LOCAL_BUS = 1'b1;
parameter WITH_LOCAL_BUS = 1;
localparam AW=ADDRESS_WIDTH;
localparam [(AW-1):0] RESET_BUS_ADDRESS = RESET_ADDRESS[(AW+1):2];
parameter F_LGDEPTH=8;
 
//}}}
// I/O declarations
//{{{
input wire i_clk, i_reset, i_interrupt;
input i_clk, i_rst, i_interrupt;
// Debug interface -- inputs
input wire i_halt, i_clear_pf_cache;
input wire [4:0] i_dbg_reg;
input wire i_dbg_we;
input wire [31:0] i_dbg_data;
input i_halt, i_clear_pf_cache;
input [4:0] i_dbg_reg;
input i_dbg_we;
input [31:0] i_dbg_data;
// Debug interface -- outputs
output wire o_dbg_stall;
output reg [31:0] o_dbg_reg;
221,9 → 179,9
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
// Wishbone interface -- inputs
input wire i_wb_ack, i_wb_stall;
input wire [31:0] i_wb_data;
input wire i_wb_err;
input i_wb_ack, i_wb_stall;
input [31:0] i_wb_data;
input i_wb_err;
// Accounting outputs ... to help us count stalls and usage
output wire o_op_stall;
output wire o_pf_stall;
231,9 → 189,7
//
`ifdef DEBUG_SCOPE
output reg [31:0] o_debug;
// output wire [31:0] o_dcache_debug;
`endif
//}}}
 
 
// Registers
246,7 → 202,11
// that logic.
//
(* ram_style = "distributed" *)
reg [31:0] regset [0:(OPT_NO_USERMODE)? 15:31];
`ifdef OPT_NO_USERMODE
reg [31:0] regset [0:15];
`else
reg [31:0] regset [0:31];
`endif
 
// Condition codes
// (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z
253,7 → 213,7
reg [3:0] flags, iflags;
wire [14:0] w_uflags, w_iflags;
reg break_en, step, sleep, r_halted;
wire break_pending, trap, gie, ubreak, pending_interrupt;
wire break_pending, trap, gie, ubreak;
wire w_clear_icache, ill_err_u;
reg ill_err_i;
reg ibus_err_flag;
263,7 → 223,7
wire ihalt_phase, uhalt_phase;
 
// The master chip enable
wire master_ce, master_stall;
wire master_ce;
 
//
//
270,30 → 230,19
// PIPELINE STAGE #1 :: Prefetch
// Variable declarations
//
//{{{
reg [(AW+1):0] pf_pc;
wire [(AW+1):0] pf_request_address, pf_instruction_pc;
reg new_pc;
wire clear_pipeline;
assign clear_pipeline = new_pc;
 
reg dcd_stalled;
wire pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err;
wire dcd_stalled;
wire pf_cyc, pf_stb, pf_we, pf_busy, pf_ack, pf_stall, pf_err;
wire [(AW-1):0] pf_addr;
wire [31:0] pf_data;
wire [31:0] pf_instruction;
wire pf_valid, pf_gie, pf_illegal;
wire pf_stalled;
wire pf_new_pc;
`ifdef FORMAL
wire [31:0] f_dcd_insn_word;
wire f_dcd_insn_gie;
reg [31:0] f_op_insn_word;
reg [31:0] f_alu_insn_word;
`endif
wire [(AW-1):0] pf_instruction_pc;
wire pf_valid, pf_gie, pf_illegal;
 
assign clear_pipeline = new_pc;
//}}}
 
//
//
// PIPELINE STAGE #2 :: Instruction Decode
300,13 → 249,12
// Variable declarations
//
//
//{{{
reg op_valid /* verilator public_flat */,
op_valid_mem, op_valid_alu;
reg op_valid_div, op_valid_fpu;
wire op_stall, dcd_ce, dcd_phase;
wire [3:0] dcd_opn;
wire [4:0] dcd_A, dcd_B, dcd_R, dcd_preA, dcd_preB;
wire [4:0] dcd_A, dcd_B, dcd_R;
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,
314,21 → 262,17
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 [AW: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_early_branch;
wire [(AW-1):0] dcd_branch_pc;
 
wire dcd_sim;
wire [22:0] dcd_sim_immv;
wire prelock_stall;
wire cc_invalid_for_dcd;
wire pending_sreg_write;
//}}}
 
 
//
338,29 → 282,20
//
//
//
//{{{
// Now, let's read our operands
reg [4:0] alu_reg;
wire [3:0] op_opn;
reg [4:0] op_R;
reg op_Rcc;
reg [4:0] op_Aid, op_Bid;
reg op_rA, op_rB;
wire [4:0] op_R;
reg [31:0] r_op_Av, r_op_Bv;
reg [(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;
reg [(AW-1):0] op_pc;
wire [31:0] w_op_Av, w_op_Bv;
wire [31:0] op_A_nowait, op_B_nowait, op_Av, op_Bv;
reg op_wR, op_wF;
wire op_gie;
wire [3:0] op_Fl;
wire op_gie, op_Rcc;
wire [14: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;
reg [3:0] r_op_opn;
wire w_op_valid;
wire [8:0] w_cpu_info;
wire op_ce, op_phase, op_pipe, op_change_data_ce;
// Some pipeline control wires
reg op_illegal;
wire op_break;
369,10 → 304,7
`ifdef VERILATOR
reg op_sim /* verilator public_flat */;
reg [22:0] op_sim_immv /* verilator public_flat */;
`else
wire op_sim = 1'b0;
`endif
//}}}
 
 
//
381,8 → 313,7
// Variable declarations
//
//
//{{{
wire [(AW+1):0] alu_pc;
wire [(AW-1):0] alu_pc;
reg r_alu_pc_valid, mem_pc_valid;
wire alu_pc_valid;
wire alu_phase;
395,10 → 326,11
wire alu_gie, alu_illegal;
 
 
wire mem_ce, mem_stalled;
wire mem_pipe_stalled;
wire mem_valid, mem_ack, mem_stall, mem_err, bus_err,
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, mem_we;
 
wire mem_ce, mem_stalled;
wire mem_pipe_stalled;
wire mem_valid, mem_ack, mem_stall, mem_err, bus_err,
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, mem_we;
wire [4:0] mem_wreg;
 
wire mem_busy, mem_rdbusy;
406,62 → 338,43
wire [31:0] mem_data, mem_result;
wire [3:0] mem_sel;
 
wire div_ce, div_error, div_busy, div_valid;
wire div_ce, div_error, div_busy, div_valid;
wire [31:0] div_result;
wire [3:0] div_flags;
 
wire fpu_ce, fpu_error, fpu_busy, fpu_valid;
assign div_ce = (master_ce)&&(!clear_pipeline)&&(op_valid_div)
&&(!mem_rdbusy)&&(!div_busy)&&(!fpu_busy)
&&(set_cond);
 
wire fpu_ce, fpu_error, fpu_busy, fpu_valid;
wire [31:0] fpu_result;
wire [3:0] fpu_flags;
reg adf_ce_unconditional;
 
wire bus_lock;
assign fpu_ce = (master_ce)&&(!clear_pipeline)&&(op_valid_fpu)
&&(!mem_rdbusy)&&(!div_busy)&&(!fpu_busy)
&&(set_cond);
 
reg dbgv, dbg_clear_pipe;
reg [31:0] dbg_val;
wire adf_ce_unconditional;
 
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_reg_ce, wr_flags_ce, wr_write_pc, wr_write_cc,
wr_write_scc, wr_write_ucc;
wire [4:0] wr_reg_id;
wire [31:0] wr_gpreg_vl, wr_spreg_vl;
wire w_switch_to_interrupt, w_release_from_interrupt;
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;
//}}}
 
`ifdef FORMAL
wire [F_LGDEPTH-1:0]
f_gbl_arb_nreqs, f_gbl_arb_nacks, f_gbl_arb_outstanding,
f_lcl_arb_nreqs, f_lcl_arb_nacks, f_lcl_arb_outstanding,
f_gbl_mem_nreqs, f_gbl_mem_nacks, f_gbl_mem_outstanding,
f_lcl_mem_nreqs, f_lcl_mem_nacks, f_lcl_mem_outstanding,
f_gbl_pf_nreqs, f_gbl_pf_nacks, f_gbl_pf_outstanding,
f_lcl_pf_nreqs, f_lcl_pf_nacks, f_lcl_pf_outstanding,
f_mem_nreqs, f_mem_nacks, f_mem_outstanding;
reg f_pf_nreqs, f_pf_nacks, f_pf_outstanding;
wire f_mem_pc;
`endif
 
 
//
// MASTER: clock enable.
//
assign master_ce = ((!i_halt)||(alu_phase))
&&(!cc_write_hold)&&(!o_break)&&(!sleep);
assign master_ce = ((!i_halt)||(alu_phase))&&(!o_break)&&(!sleep);
 
 
//
475,50 → 388,45
// PIPELINE STAGE #2 :: Instruction Decode
// Calculate stall conditions
 
always @(*)
if (OPT_PIPELINED)
dcd_stalled = (dcd_valid)&&(op_stall);
else
dcd_stalled = (!master_ce)||(ill_err_i)||(dcd_valid)||(op_valid)
||(ibus_err_flag)||(idiv_err_flag)
||(alu_busy)||(div_busy)||(fpu_busy)||(mem_busy);
`ifdef OPT_PIPELINED
assign dcd_stalled = (dcd_valid)&&(op_stall);
`else // Not pipelined -- either double or single fetch
assign dcd_stalled = (dcd_valid)&&(op_stall);
`endif
//
// PIPELINE STAGE #3 :: Read Operands
// Calculate stall conditions
//{{{
generate if (OPT_PIPELINED)
begin : GEN_OP_STALL
reg r_cc_invalid_for_dcd;
always @(posedge i_clk)
r_cc_invalid_for_dcd <=
(set_cond)&&(op_valid)
&&((op_wF)||((op_wR)&&(op_R[4:0] == { op_gie, `CPU_CC_REG })))
||((r_cc_invalid_for_dcd)
&&((alu_busy)||(mem_rdbusy)||(div_busy)||(fpu_busy)));
wire prelock_stall;
`ifdef OPT_PIPELINED
reg cc_invalid_for_dcd;
always @(posedge i_clk)
cc_invalid_for_dcd <= (wr_flags_ce)
||(wr_reg_ce)&&(wr_reg_id[3:0] == `CPU_CC_REG)
||(op_valid)&&((op_wF)||((op_wR)&&(op_R[3:0] == `CPU_CC_REG)))
||((alu_wF)||((alu_wR)&&(alu_reg[3:0] == `CPU_CC_REG)))
||(mem_busy)||(div_busy)||(fpu_busy);
 
assign cc_invalid_for_dcd = r_cc_invalid_for_dcd;
 
reg r_pending_sreg_write;
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 ((!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/validins and the
// next stage is accepting our instruction
(!adf_ce_unconditional)&&(!mem_ce)
assign op_stall = (op_valid)&&( // Only stall if we're loaded w/validins
// Stall if we're stopped, and not allowed to execute
// an instruction
// (!master_ce) // Already captured in alu_stall
//
// Stall if going into the ALU and the ALU is stalled
// i.e. if the memory is busy, or we are single
// stepping. This also includes our stalls for
// op_break and op_lock, so we don't need to
// include those as well here.
// This also includes whether or not the divide or
// floating point units are busy.
(alu_stall)
||(((op_valid_div)||(op_valid_fpu))
&&(!adf_ce_unconditional))
//
// Stall if we are going into memory with an operation
// that cannot be pipelined, and the memory is
// already busy
||(mem_stalled) // &&(op_valid_mem) part of mem_stalled
||(op_Rcc)
)
||(dcd_valid)&&(
// Stall if we need to wait for an operand A
533,36 → 441,25
// CC register
||(dcd_F_stall)
);
//}}}
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall);
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall);
 
end else begin // !OPT_PIPELINED
`else
assign op_stall = (alu_busy)||(div_busy)||(fpu_busy)||(wr_reg_ce)
||(mem_busy)||(op_valid)||(!master_ce)||(wr_flags_ce);
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall);
`endif
 
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 lint_off UNUSED
wire [1:0] pipe_unused;
assign pipe_unused = { cc_invalid_for_dcd,
pending_sreg_write };
// Verilator lint_on UNUSED
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);
//}}}
assign op_change_data_ce = (!op_stall);
 
//
// PIPELINE STAGE #4 :: ALU / Memory
// Calculate stall conditions
//
//{{{
// 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
572,31 → 469,18
// 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
assign alu_stall = (((master_stall)||(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 lint_off unused
wire unused_alu_stall = alu_stall;
// Verilator lint_on unused
end else begin
 
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 lint_off unused
wire unused_alu_stall = alu_stall;
// Verilator lint_on unused
end endgenerate
`ifdef OPT_PIPELINED
assign alu_stall = (((!master_ce)||(mem_rdbusy)||(alu_busy))&&(op_valid_alu)) //Case 1&2
||(prelock_stall)
||((op_valid)&&(op_break))
||(wr_reg_ce)&&(wr_write_cc)
||(div_busy)||(fpu_busy);
assign alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall)
&&(!clear_pipeline);
`else
assign alu_stall = (op_valid_alu)&&((!master_ce)||(op_break));
assign alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall)&&(!clear_pipeline);
`endif
//
 
//
606,55 → 490,38
assign mem_ce = (master_ce)&&(op_valid_mem)&&(!mem_stalled)
&&(!clear_pipeline);
 
generate if (OPT_PIPELINED_BUS_ACCESS)
begin
 
assign mem_stalled = (master_stall)||((op_valid_mem)&&(
`ifdef OPT_PIPELINED_BUS_ACCESS
assign mem_stalled = (!master_ce)||(alu_busy)||((op_valid_mem)&&(
(mem_pipe_stalled)
||(bus_err)||(div_error)
||(prelock_stall)
||((!op_pipe)&&(mem_busy))
||(div_busy)
||(fpu_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_reg_ce)&&(wr_reg_id[4] == op_gie)
&&((wr_write_pc)||(wr_write_cc)))));
end else if (OPT_PIPELINED)
begin
assign mem_stalled = (master_stall)||((op_valid_mem)&&(
(bus_err)||(div_error)||(mem_busy)
`else
`ifdef OPT_PIPELINED
assign mem_stalled = (mem_busy)||((op_valid_mem)&&(
(!master_ce)
// 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
||((wr_reg_ce)&&(wr_reg_id[4] == op_gie)&&((wr_write_pc)||(wr_write_cc)))));
`else
assign mem_stalled = (op_valid_mem)&&(!master_ce);
`endif
`endif
 
assign mem_stalled = (master_stall);
 
end endgenerate
//}}}
 
assign master_stall = (!master_ce)||(!op_valid)||(ill_err_i)
||(ibus_err_flag)||(idiv_err_flag)
||(pending_interrupt)&&(!alu_phase)
||(alu_busy)||(div_busy)||(fpu_busy)||(op_break)
||((OPT_PIPELINED)&&(
((OPT_LOCK)&&(prelock_stall))
||((mem_busy)&&(op_illegal))
||((mem_busy)&&(op_valid_div))
||(alu_illegal)||(o_break)));
 
 
// 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)&&(!mem_rdbusy)
&&((!mem_busy)||(!op_wR)||(op_R[4:1] != { gie, 3'h7}));
else
adf_ce_unconditional = (!master_stall)&&(op_valid)&&(!op_valid_mem);
assign adf_ce_unconditional = (master_ce)&&(!clear_pipeline)&&(op_valid)
&&(!op_valid_mem)&&(!mem_rdbusy)
&&((!op_valid_alu)||(!alu_stall))&&(!op_break)
&&(!div_busy)&&(!fpu_busy)&&(!clear_pipeline);
 
//
//
661,37 → 528,19
// PIPELINE STAGE #1 :: Prefetch
//
//
//{{{
wire pf_stalled;
assign pf_stalled = (dcd_stalled)||(dcd_phase);
 
assign pf_new_pc = (new_pc)||((dcd_early_branch_stb)&&(!clear_pipeline));
wire pf_new_pc;
assign pf_new_pc = (new_pc)||((dcd_early_branch)&&(!clear_pipeline));
 
assign pf_request_address = ((dcd_early_branch_stb)&&(!clear_pipeline))
? dcd_branch_pc:pf_pc;
wire [(AW-1):0] pf_request_address;
assign pf_request_address = ((dcd_early_branch)&&(!clear_pipeline))
? dcd_branch_pc:pf_pc[(AW+1):2];
assign pf_gie = gie;
`ifdef FORMAL
abs_prefetch #(ADDRESS_WIDTH)
//{{{
pf(i_clk, (i_reset), pf_new_pc, w_clear_icache,
(!pf_stalled),
pf_request_address,
pf_instruction, pf_instruction_pc,
pf_valid,
pf_cyc, pf_stb, pf_we, pf_addr, pf_data,
pf_ack, pf_stall, pf_err, i_wb_data,
pf_illegal);
always @(*)
begin
f_pf_nreqs = 0;
f_pf_nacks = 0;
f_pf_outstanding = 0;
end
//}}}
`else
`ifdef OPT_SINGLE_FETCH
prefetch #(ADDRESS_WIDTH)
//{{{
pf(i_clk, (i_reset), pf_new_pc, w_clear_icache,
pf(i_clk, (i_rst), pf_new_pc, w_clear_icache,
(!pf_stalled),
pf_request_address,
pf_instruction, pf_instruction_pc,
698,13 → 547,14
pf_valid, pf_illegal,
pf_cyc, pf_stb, pf_we, pf_addr, pf_data,
pf_ack, pf_stall, pf_err, i_wb_data);
//}}}
 
`else
`ifdef OPT_DOUBLE_FETCH
 
wire [1:0] pf_dbg;
dblfetch #(ADDRESS_WIDTH)
//{{{
pf(i_clk, i_reset, pf_new_pc, w_clear_icache,
pf(i_clk, i_rst, pf_new_pc,
w_clear_icache,
(!pf_stalled),
pf_request_address,
pf_instruction, pf_instruction_pc,
712,14 → 562,12
pf_cyc, pf_stb, pf_we, pf_addr, pf_data,
pf_ack, pf_stall, pf_err, i_wb_data,
pf_illegal);
//}}}
 
`else // Not single fetch and not double fetch
 
`ifdef OPT_TRADITIONAL_PFCACHE
pfcache #(LGICACHE, ADDRESS_WIDTH)
//{{{
pf(i_clk, i_reset, pf_new_pc, w_clear_icache,
pf(i_clk, i_rst, pf_new_pc, w_clear_icache,
// dcd_pc,
(!pf_stalled),
pf_request_address,
727,71 → 575,65
pf_cyc, pf_stb, pf_we, pf_addr, pf_data,
pf_ack, pf_stall, pf_err, i_wb_data,
pf_illegal);
//}}}
`else
pipefetch #({RESET_BUS_ADDRESS, 2'b00}, LGICACHE, ADDRESS_WIDTH)
//{{{
pf(i_clk, i_reset, pf_new_pc,
pipefetch #(RESET_BUS_ADDRESS, LGICACHE, ADDRESS_WIDTH)
pf(i_clk, i_rst, pf_new_pc,
w_clear_icache, (!pf_stalled),
(new_pc)?pf_pc:dcd_branch_pc,
(new_pc)?pf_pc[(AW+1):2]:dcd_branch_pc,
pf_instruction, pf_instruction_pc, pf_valid,
pf_cyc, pf_stb, pf_we, pf_addr, pf_data,
pf_ack, pf_stall, pf_err, i_wb_data,
(mem_cyc_lcl)||(mem_cyc_gbl),
pf_illegal);
//}}}
`endif // OPT_TRADITIONAL_CACHE
`endif // OPT_DOUBLE_FETCH
`endif // OPT_SINGLE_FETCH
`endif // FORMAL
//}}}
 
//
//
// PIPELINE STAGE #2 :: Instruction Decode
//
//
//{{{
assign dcd_ce =((OPT_PIPELINED)&&(!dcd_valid))||(!dcd_stalled);
idecode #(.ADDRESS_WIDTH(AW),
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0),
.OPT_PIPELINED(OPT_PIPELINED),
.OPT_EARLY_BRANCHING(EARLY_BRANCHING),
.OPT_DIVIDE(IMPLEMENT_DIVIDE),
.OPT_FPU(IMPLEMENT_FPU),
.OPT_LOCK(OPT_LOCK),
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS),
.OPT_NO_USERMODE(OPT_NO_USERMODE),
`ifdef VERILATOR
.OPT_SIM(1'b1),
`else
.OPT_SIM(1'b0),
`endif
.OPT_CIS(OPT_CIS))
instruction_decoder(i_clk,
(i_reset)||(clear_pipeline)||(w_clear_icache),
assign dcd_ce = (!dcd_valid)||(!dcd_stalled);
idecode #(AW, IMPLEMENT_MPY, EARLY_BRANCHING, IMPLEMENT_DIVIDE,
IMPLEMENT_FPU)
instruction_decoder(i_clk,
(clear_pipeline)||(w_clear_icache),
dcd_ce,
dcd_stalled, pf_instruction, pf_gie,
pf_instruction_pc, pf_valid, pf_illegal,
pf_instruction_pc, pf_valid, pf_illegal,
dcd_valid, dcd_phase,
dcd_illegal, dcd_pc,
dcd_illegal, dcd_pc, dcd_gie,
{ dcd_Rcc, dcd_Rpc, dcd_R },
{ dcd_Acc, dcd_Apc, dcd_A },
{ dcd_Bcc, dcd_Bpc, dcd_B },
dcd_preA, dcd_preB,
dcd_I, dcd_zI, dcd_F, dcd_wF, dcd_opn,
dcd_ALU, dcd_M, dcd_DIV, dcd_FP, dcd_break, dcd_lock,
dcd_wR,dcd_rA, dcd_rB,
dcd_early_branch, dcd_early_branch_stb,
dcd_early_branch,
dcd_branch_pc, dcd_ljmp,
dcd_pipe,
dcd_sim, dcd_sim_immv
`ifdef FORMAL
, f_dcd_insn_word, f_dcd_insn_gie
dcd_sim, dcd_sim_immv);
 
`ifdef OPT_PIPELINED_BUS_ACCESS
reg r_op_pipe;
 
initial r_op_pipe = 1'b0;
// 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.
always @(posedge i_clk)
if (clear_pipeline)
r_op_pipe <= 1'b0;
else if (op_ce)
r_op_pipe <= dcd_pipe;
else if (mem_ce) // Clear us any time an op_ is clocked in
r_op_pipe <= 1'b0;
assign op_pipe = r_op_pipe;
`else
assign op_pipe = 1'b0;
`endif
);
assign dcd_gie = pf_gie;
//}}}
 
//
//
798,204 → 640,122
// PIPELINE STAGE #3 :: Read Operands (Registers)
//
//
//{{{
generate if (OPT_PIPELINED_BUS_ACCESS)
begin : GEN_OP_PIPE
reg r_op_pipe;
 
initial r_op_pipe = 1'b0;
// 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.
always @(posedge i_clk)
if ((clear_pipeline)||(i_halt))
r_op_pipe <= 1'b0;
else if (op_ce)
r_op_pipe <= (dcd_pipe)&&(op_valid_mem);
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;
end else begin
 
assign op_pipe = 1'b0;
 
end endgenerate
 
// `define NO_DISTRIBUTED_RAM
`ifdef NO_DISTRIBUTED_RAM
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
 
generate if (OPT_NO_USERMODE)
begin
always @(posedge i_clk)
if (dcd_ce)
begin
pre_op_Av <= regset[dcd_preA[3:0]];
pre_op_Bv <= regset[dcd_preB[3:0]];
end
end else begin
 
always @(posedge i_clk)
if (dcd_ce)
begin
pre_op_Av <= regset[dcd_preA];
pre_op_Bv <= regset[dcd_preB];
end
 
end endgenerate
 
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;
`ifdef OPT_NO_USERMODE
assign w_op_Av = regset[dcd_A[3:0]];
assign w_op_Bv = regset[dcd_B[3:0]];
`else
generate if (OPT_NO_USERMODE)
begin
assign w_op_Av = regset[dcd_A[3:0]];
assign w_op_Bv = regset[dcd_B[3:0]];
end else begin
 
assign w_op_Av = regset[dcd_A];
assign w_op_Bv = regset[dcd_B];
 
end endgenerate
 
// verilator lint_off UNUSED
wire [9:0] unused_prereg_addrs;
assign unused_prereg_addrs = { dcd_preA, dcd_preB };
// verilator lint_on UNUSED
assign w_op_Av = regset[dcd_A];
assign w_op_Bv = regset[dcd_B];
`endif
 
wire [8:0] w_cpu_info;
assign w_cpu_info = {
//{{{
1'b1,
(IMPLEMENT_MPY >0)? 1'b1:1'b0,
(IMPLEMENT_DIVIDE >0)? 1'b1:1'b0,
(IMPLEMENT_FPU >0)? 1'b1:1'b0,
OPT_PIPELINED,
`ifdef OPT_PIPELINED
1'b1,
`else
1'b0,
`endif
`ifdef OPT_TRADITIONAL_CACHE
1'b1,
`else
1'b0,
`endif
(EARLY_BRANCHING > 0)? 1'b1:1'b0,
OPT_PIPELINED_BUS_ACCESS,
OPT_CIS
`ifdef OPT_EARLY_BRANCHING
1'b1,
`else
1'b0,
`endif
`ifdef OPT_PIPELINED_BUS_ACCESS
1'b1,
`else
1'b0,
`endif
`ifdef OPT_CIS
1'b1
`else
1'b0
`endif
};
//}}}
 
always @(*)
if ((OPT_NO_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 };
 
wire [31:0] w_pcA_v;
assign w_pcA_v[(AW+1):0] = { (dcd_A[4] == dcd_gie)
? { dcd_pc[AW:1], 2'b00 }
: { upc[(AW+1):2], uhalt_phase, 1'b0 } };
generate
if (AW < 30)
always @(*)
w_pcA_v[31:(AW+2)] = 0;
assign w_pcA_v[31:(AW+2)] = 0;
endgenerate
 
generate if (OPT_PIPELINED)
begin : OPV
initial op_R = 0;
initial op_Aid = 0;
initial op_Bid = 0;
initial op_rA = 0;
initial op_rB = 0;
initial op_Rcc = 0;
always @(posedge i_clk)
`ifdef OPT_PIPELINED
reg [4:0] op_Aid, op_Bid;
reg op_rA, op_rB;
always @(posedge i_clk)
if (op_ce)
begin
op_R <= dcd_R;
op_Aid <= dcd_A;
op_Bid <= dcd_B;
op_rA <= (dcd_rA)&&(!dcd_early_branch)&&(!dcd_illegal);
op_rB <= (dcd_rB)&&(!dcd_early_branch)&&(!dcd_illegal);
op_Rcc <= (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie);
op_rA <= dcd_rA;
op_rB <= dcd_rB;
end
`endif
 
end else begin
 
always @(*)
always @(posedge i_clk)
if (op_ce)
begin
op_R = dcd_R;
op_Aid = dcd_A;
op_Bid = dcd_B;
op_rA = dcd_rA;
op_rB = dcd_rB;
op_Rcc = (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie);
`ifdef OPT_PIPELINED
if ((wr_reg_ce)&&(wr_reg_id == dcd_A))
r_op_Av <= wr_gpreg_vl;
else
`endif
if (dcd_Apc)
r_op_Av <= w_pcA_v;
else if (dcd_Acc)
r_op_Av <= { w_cpu_info, w_op_Av[22:16], 1'b0, (dcd_A[4])?w_uflags:w_iflags };
else
r_op_Av <= w_op_Av;
`ifdef OPT_PIPELINED
end else
begin
if ((wr_reg_ce)&&(wr_reg_id == op_Aid)&&(op_rA))
r_op_Av <= wr_gpreg_vl;
`endif
end
 
end endgenerate
 
 
always @(posedge i_clk)
if ((!OPT_PIPELINED)||(op_ce))
begin
if ((OPT_PIPELINED)&&(wr_reg_ce)&&(wr_reg_id == dcd_A))
r_op_Av <= wr_gpreg_vl;
else if (dcd_Apc)
r_op_Av <= w_pcA_v;
else if (dcd_Acc)
r_op_Av <= { w_cpu_info, w_op_Av[22:16], 1'b0, (dcd_A[4])?w_uflags:w_iflags };
else
r_op_Av <= w_op_Av;
end else if (OPT_PIPELINED)
begin
if ((wr_reg_ce)&&(wr_reg_id == op_Aid)&&(op_rA))
r_op_Av <= wr_gpreg_vl;
end
 
always @(*)
if ((OPT_NO_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 };
wire [31:0] w_op_BnI, w_pcB_v;
assign w_pcB_v[(AW+1):0] = { (dcd_B[4] == dcd_gie)
? { dcd_pc[AW:1], 2'b00 }
: { upc[(AW+1):2], uhalt_phase, 1'b0 } };
generate
if (AW < 30)
always @(*)
w_pcB_v[31:(AW+2)] = 0;
assign w_pcB_v[31:(AW+2)] = 0;
endgenerate
 
always @(*)
if (!dcd_rB)
w_op_BnI = 0;
else if ((OPT_PIPELINED)&&(wr_reg_ce)&&(wr_reg_id == dcd_B))
w_op_BnI = wr_gpreg_vl;
else if (dcd_Bcc)
w_op_BnI = { w_cpu_info, w_op_Bv[22:16], 1'b0,
(dcd_B[4]) ? w_uflags : w_iflags };
else
w_op_BnI = w_op_Bv;
assign w_op_BnI = (!dcd_rB) ? 32'h00
`ifdef OPT_PIPELINED
: ((wr_reg_ce)&&(wr_reg_id == dcd_B)) ? wr_gpreg_vl
`endif
: ((dcd_Bcc) ? { w_cpu_info, w_op_Bv[22:16], // w_op_B[31:14],
1'b0, (dcd_B[4])?w_uflags:w_iflags}
: w_op_Bv);
 
always @(posedge i_clk)
if ((!OPT_PIPELINED)||(op_ce))
begin
`ifdef OPT_PIPELINED
if ((op_ce)&&(dcd_Bpc)&&(dcd_rB))
r_op_Bv <= w_pcB_v + { dcd_I[29:0], 2'b00 };
else if (op_ce)
r_op_Bv <= w_op_BnI + dcd_I;
else if ((wr_reg_ce)&&(op_Bid == wr_reg_id)&&(op_rB))
r_op_Bv <= wr_gpreg_vl;
`else
if ((dcd_Bpc)&&(dcd_rB))
r_op_Bv <= w_pcB_v + { dcd_I[29:0], 2'b00 };
else
r_op_Bv <= w_op_BnI + dcd_I;
end else if ((OPT_PIPELINED)&&(op_rB)
&&(wr_reg_ce)&&(op_Bid == wr_reg_id))
r_op_Bv <= wr_gpreg_vl;
`endif
 
// 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
1007,27 → 767,25
// below, arriving at what we finally want in the (now wire net)
// op_F.
always @(posedge i_clk)
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 // Bit order is { (flags_not_used), VNCZ mask, VNCZ value }
if (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 // Bit order is { (flags_not_used), VNCZ mask, VNCZ value }
assign op_F = { r_op_F[3], r_op_F[6:0] };
 
assign w_op_valid = (!clear_pipeline)&&(dcd_valid)
&&(!dcd_ljmp)&&(!dcd_early_branch);
 
wire w_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;
1034,31 → 792,29
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)
if (clear_pipeline)
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 <= (IMPLEMENT_DIVIDE)&&(dcd_DIV)&&(!dcd_illegal)&&(w_op_valid);
op_valid_fpu <= (IMPLEMENT_FPU)&&(dcd_FP)&&(!dcd_illegal)&&(w_op_valid);
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.
op_valid<= (w_op_valid)||(dcd_illegal)&&(dcd_valid)||(dcd_early_branch);
op_valid_alu <= (w_op_valid)&&((dcd_ALU)||(dcd_illegal)
||(dcd_early_branch));
op_valid_mem <= (dcd_M)&&(!dcd_illegal)&&(w_op_valid);
op_valid_div <= (dcd_DIV)&&(!dcd_illegal)&&(w_op_valid);
op_valid_fpu <= (dcd_FP)&&(!dcd_illegal)&&(w_op_valid);
end else if ((adf_ce_unconditional)||(mem_ce))
begin
op_valid <= 1'b0;
1067,14 → 823,6
op_valid_div <= 1'b0;
op_valid_fpu <= 1'b0;
end
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
 
// Here's part of our debug interface. When we recognize a break
// instruction, we set the op_break flag. That'll prevent this
1085,76 → 833,74
// 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);
reg r_op_break;
 
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);
if ((i_rst)||(clear_pipeline)) r_op_break <= 1'b0;
else if (op_ce)
r_op_break <= (dcd_break);
else if (!op_valid)
r_op_break <= 1'b0;
assign op_break = r_op_break;
 
generate if ((!OPT_PIPELINED)||(!OPT_LOCK))
`ifdef OPT_PIPELINED
generate
if (IMPLEMENT_LOCK != 0)
begin
 
assign op_lock = 1'b0;
 
// Verilator lint_off UNUSED
wire dcd_lock_unused;
assign dcd_lock_unused = dcd_lock;
// Verilator lint_on UNUSED
 
end else // if (IMPLEMENT_LOCK != 0)
begin : 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);
if (clear_pipeline)
r_op_lock <= 1'b0;
else if (op_ce)
r_op_lock <= (dcd_valid)&&(dcd_lock)&&(!clear_pipeline);
assign op_lock = r_op_lock;
 
end else begin
assign op_lock = 1'b0;
end endgenerate
 
`else
assign op_lock = 1'b0;
`endif
 
`ifdef OPT_ILLEGAL_INSTRUCTION
initial op_illegal = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(clear_pipeline))
op_illegal <= 1'b0;
else if (OPT_PIPELINED)
begin
if (clear_pipeline)
op_illegal <= 1'b0;
else if(op_ce)
`ifdef OPT_PIPELINED
op_illegal <= (dcd_valid)&&((dcd_illegal)||((dcd_lock)&&(IMPLEMENT_LOCK == 0)));
`else
op_illegal <= (dcd_valid)&&((dcd_illegal)||(dcd_lock));
`endif
else if(alu_ce)
op_illegal <= 1'b0;
`endif
 
// No generate on EARLY_BRANCHING here, since if EARLY_BRANCHING is not
// set, dcd_early_branch will simply be a wire connected to zero and
// this logic should just optimize.
`ifdef OPT_PIPELINED
always @(posedge i_clk)
if (op_ce)
op_illegal <= (dcd_valid)&&(!dcd_ljmp)
&&(!dcd_early_branch)&&(dcd_illegal);
end else if (!OPT_PIPELINED)
begin
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR))
&&(!dcd_early_branch)&&(!dcd_illegal);
op_wR <= (dcd_wR)&&(!dcd_early_branch)&&(!dcd_illegal);
end
`else
always @(posedge i_clk)
begin
if (dcd_valid)
op_illegal <= (!dcd_ljmp)&&(!dcd_early_branch)&&(dcd_illegal);
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR))
&&(!dcd_early_branch)&&(!dcd_illegal);
op_wR <= (dcd_wR)&&(!dcd_early_branch)&&(!dcd_illegal);
end
`endif
 
always @(posedge i_clk)
if ((!OPT_PIPELINED)||(op_ce))
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR))
&&(!dcd_early_branch);
 
generate if ((OPT_PIPELINED)||(EARLY_BRANCHING))
begin
 
always @(posedge i_clk)
if (op_ce)
op_wR <= (dcd_wR)&&(!dcd_early_branch);
 
end else begin
 
always @(*)
op_wR = (dcd_wR);
 
end endgenerate
 
`ifdef VERILATOR
`ifdef SINGLE_FETCH
always @(*)
1164,7 → 910,7
end
`else
always @(posedge i_clk)
if (op_ce)
if (op_change_data_ce)
begin
op_sim <= dcd_sim;
op_sim_immv <= dcd_sim_immv;
1172,68 → 918,48
`endif
`endif
 
reg [3:0] r_op_opn;
reg [4:0] r_op_R;
reg r_op_Rcc;
reg r_op_gie;
 
generate if ((OPT_PIPELINED)||(EARLY_BRANCHING))
begin : SET_OP_PC
 
initial op_pc[0] = 1'b0;
always @(posedge i_clk)
if (op_ce)
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc;
 
end else begin : SET_OP_PC
 
always @(*)
op_pc = dcd_pc;
 
end endgenerate
 
generate if (!OPT_PIPELINED)
begin
always @(*)
r_op_opn = dcd_opn;
 
end else begin
 
always @(posedge i_clk)
if (op_ce)
initial r_op_gie = 1'b0;
always @(posedge i_clk)
if (op_change_data_ce)
begin
// Which ALU operation? Early branches are
// unimplemented moves
r_op_opn <= ((dcd_early_branch)||(dcd_illegal))
? `CPU_MOV_OP : dcd_opn;
r_op_opn <= (dcd_early_branch) ? 4'hf : dcd_opn;
// opM <= dcd_M; // Is this a memory operation?
// What register will these results be written into?
r_op_R <= dcd_R;
r_op_Rcc <= (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie);
// User level (1), vs supervisor (0)/interrupts disabled
r_op_gie <= dcd_gie;
 
//
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc[AW:1];
end
 
end endgenerate
 
assign op_opn = r_op_opn;
assign op_gie = gie;
assign op_R = r_op_R;
assign op_gie = r_op_gie;
assign op_Rcc = r_op_Rcc;
 
assign op_Fl = (op_gie)?(w_uflags[3:0]):(w_iflags[3:0]);
assign op_Fl = (op_gie)?(w_uflags):(w_iflags);
 
generate if (OPT_CIS)
begin : OPT_CIS_OP_PHASE
`ifdef OPT_CIS
reg r_op_phase;
initial r_op_phase = 1'b0;
always @(posedge i_clk)
if (clear_pipeline)
r_op_phase <= 1'b0;
else if (op_change_data_ce)
r_op_phase <= (dcd_phase)&&((!dcd_wR)||(!dcd_Rpc));
assign op_phase = r_op_phase;
`else
assign op_phase = 1'b0;
`endif
 
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
 
// 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
1246,18 → 972,14
// 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
 
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid))
`ifdef OPT_PIPELINED
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid)) // &&(op_rA))
? wr_gpreg_vl : r_op_Av;
`else
assign op_Av = r_op_Av;
`endif
 
end else begin
 
assign op_Av = r_op_Av;
 
end endgenerate
 
`ifdef OPT_PIPELINED
// 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 ...)
1264,27 → 986,24
// The operation might set flags, and we wish to read the
// CC register
// OR ... (No other conditions)
generate if (OPT_PIPELINED)
begin
 
assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere
assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere
&&((op_valid)||(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
`else
// There are no pipeline hazards, if we aren't pipelined
assign dcd_A_stall = 1'b0;
`endif
 
// There are no pipeline hazards, if we aren't pipelined
assign dcd_A_stall = 1'b0;
 
end endgenerate
 
assign op_Bv = ((OPT_PIPELINED)&&(wr_reg_ce)
&&(wr_reg_id == op_Bid)&&(op_rB))
`ifdef OPT_PIPELINED
assign op_Bv = ((wr_reg_ce)&&(wr_reg_id == op_Bid)&&(op_rB))
? wr_gpreg_vl: r_op_Bv;
`else
assign op_Bv = r_op_Bv;
`endif
 
generate if (OPT_PIPELINED)
begin
`ifdef OPT_PIPELINED
// 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 ...)
1293,7 → 1012,6
// 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
1321,8 → 1039,6
((!dcd_zI)&&(
((op_R == dcd_B)&&(op_wR))
||((mem_rdbusy)&&(!dcd_pipe))
||(((alu_busy)||(div_busy))&&(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
1333,75 → 1049,45
// ||((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));
assign dcd_F_stall = ((!dcd_F[3])
||((dcd_rA)&&(dcd_Acc))
||((dcd_rB)&&(dcd_Bcc)))
&&(op_valid)&&(op_Rcc);
// &&(dcd_valid) is checked for elsewhere
//}}}
end else begin
// 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
 
//}}}
`else
// 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;
`endif
//
//
// PIPELINE STAGE #4 :: Apply Instruction
//
//
// ALU
cpuops #(IMPLEMENT_MPY) doalu(i_clk, ((i_reset)||(clear_pipeline)),
//{{{
cpuops #(IMPLEMENT_MPY) doalu(i_clk, (clear_pipeline),
alu_ce, op_opn, op_Av, op_Bv,
alu_result, alu_flags, alu_valid, alu_busy);
//}}}
 
// Divide
//{{{
generate if (IMPLEMENT_DIVIDE != 0)
begin : DIVIDE
`ifdef FORMAL
`define DIVIDE_MODULE abs_div
`else
`define DIVIDE_MODULE div
`endif
`DIVIDE_MODULE thedivide(i_clk, ((i_reset)||(clear_pipeline)),
div_ce, op_opn[0],
generate
if (IMPLEMENT_DIVIDE != 0)
begin
div thedivide(i_clk, (clear_pipeline), div_ce, op_opn[0],
op_Av, op_Bv, div_busy, div_valid, div_error, div_result,
div_flags);
 
end else begin
 
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 lint_off UNUSED
wire unused_divide;
assign unused_divide = div_ce;
// verilator lint_on UNUSED
end endgenerate
//}}}
 
// (Non-existent) FPU
//{{{
generate if (IMPLEMENT_FPU != 0)
begin : FPU
generate
if (IMPLEMENT_FPU != 0)
begin
//
// sfpu thefpu(i_clk, i_reset, fpu_ce, op_opn[2:0],
// sfpu thefpu(i_clk, i_rst, fpu_ce,
// op_Av, op_Bv, fpu_busy, fpu_valid, fpu_err, fpu_result,
// fpu_flags);
//
1417,16 → 1103,13
assign fpu_result= 32'h00;
assign fpu_flags = 4'h0;
end endgenerate
//}}}
 
 
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
always @(posedge i_clk)
if (i_reset)
always @(posedge i_clk)
if (i_rst)
begin
alu_wR <= 1'b0;
alu_wF <= 1'b0;
1433,130 → 1116,90
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);
alu_wR <= (op_wR)&&(set_cond);
alu_wF <= (op_wF)&&(set_cond);
end else if (!alu_busy) begin
// These are strobe signals, so clear them if not
// set for any particular clock
alu_wR <= (r_halted)&&(i_dbg_we);
alu_wR <= (i_halt)&&(i_dbg_we);
alu_wF <= 1'b0;
end
end else begin
 
always @(posedge i_clk)
alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal);
always @(posedge i_clk)
alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal);
`ifdef OPT_CIS
reg r_alu_phase;
initial r_alu_phase = 1'b0;
always @(posedge i_clk)
if (i_rst)
r_alu_phase <= 1'b0;
else if ((adf_ce_unconditional)||(mem_ce))
r_alu_phase <= op_phase;
assign alu_phase = r_alu_phase;
`else
assign alu_phase = 1'b0;
`endif
 
end endgenerate
 
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
 
assign alu_phase = 1'b0;
end endgenerate
 
generate if (OPT_PIPELINED)
begin
 
always @(posedge i_clk)
`ifdef OPT_PIPELINED
always @(posedge i_clk)
if (adf_ce_unconditional)
alu_reg <= op_R;
else if ((r_halted)&&(i_dbg_we))
else if ((i_halt)&&(i_dbg_we))
alu_reg <= i_dbg_reg;
`else
always @(posedge i_clk)
if ((i_halt)&&(i_dbg_we))
alu_reg <= i_dbg_reg;
else
alu_reg <= op_R;
`endif
 
end else begin
 
always @(posedge i_clk)
if ((r_halted)&&(i_dbg_we))
alu_reg <= i_dbg_reg;
else
alu_reg <= op_R;
end endgenerate
 
//
// DEBUG Register write access starts here
//
//{{{
reg dbgv;
initial dbgv = 1'b0;
always @(posedge i_clk)
if (i_reset)
dbgv <= 0;
else
dbgv <= (i_dbg_we)&&(r_halted);
 
dbgv <= (!i_rst)&&(i_halt)&&(i_dbg_we)&&(r_halted);
reg [31:0] dbg_val;
always @(posedge i_clk)
dbg_val <= i_dbg_data;
`ifdef OPT_NO_USERMODE
assign alu_gie = 1'b0;
`else
`ifdef OPT_PIPELINED
reg r_alu_gie;
 
always @(posedge i_clk)
if ((i_reset)||(clear_pipeline))
dbg_clear_pipe <= 0;
else if ((i_dbg_we)&&(r_halted))
begin
if (!OPT_PIPELINED)
dbg_clear_pipe <= 1'b1;
else if ((i_dbg_reg == op_Bid)&&(op_rB))
dbg_clear_pipe <= 1'b1;
else if (i_dbg_reg[3:1] == 3'h7)
dbg_clear_pipe <= 1'b1;
else
dbg_clear_pipe <= 1'b0;
end else if ((!OPT_PIPELINED)&&(i_clear_pf_cache))
dbg_clear_pipe <= 1'b1;
else
dbg_clear_pipe <= 1'b0;
if ((adf_ce_unconditional)||(mem_ce))
r_alu_gie <= op_gie;
assign alu_gie = r_alu_gie;
`else
assign alu_gie = op_gie;
`endif
`endif
 
assign alu_gie = gie;
//}}}
 
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 (i_reset)
r_alu_pc <= 0;
else if ((adf_ce_unconditional)
||((master_ce)&&(op_valid_mem)
&&(!clear_pipeline)&&(!mem_stalled)))
`ifdef OPT_PIPELINED
reg [(AW-1):0] r_alu_pc;
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;
assign alu_pc = r_alu_pc;
`else
assign alu_pc = op_pc;
`endif
 
end else begin
reg r_alu_illegal;
initial r_alu_illegal = 0;
always @(posedge i_clk)
if (clear_pipeline)
r_alu_illegal <= 1'b0;
else if (alu_ce)
r_alu_illegal <= op_illegal;
else
r_alu_illegal <= 1'b0;
assign alu_illegal = (r_alu_illegal);
 
assign alu_pc = op_pc;
 
end endgenerate
 
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
 
initial r_alu_pc_valid = 1'b0;
initial mem_pc_valid = 1'b0;
always @(posedge i_clk)
1568,16 → 1211,16
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)
if (i_rst)
mem_pc_valid <= 1'b0;
else
mem_pc_valid <= (mem_ce);
 
// Bus lock logic
//{{{
wire bus_lock;
`ifdef OPT_PIPELINED
generate
if ((OPT_PIPELINED)&&(!OPT_LOCK))
begin : BUSLOCK
if (IMPLEMENT_LOCK != 0)
begin
reg r_prelock_stall;
 
initial r_prelock_stall = 1'b0;
1592,7 → 1235,6
assign prelock_stall = r_prelock_stall;
 
reg r_prelock_primed;
initial r_prelock_primed = 1'b0;
always @(posedge i_clk)
if (clear_pipeline)
r_prelock_primed <= 1'b0;
1618,51 → 1260,12
assign prelock_stall = 1'b0;
assign bus_lock = 1'b0;
end endgenerate
//}}}
 
// Memory interface
//{{{
generate if (OPT_DCACHE)
begin : MEM_DCACHE
 
dcache #(.LGCACHELEN(OPT_LGDCACHE), .ADDRESS_WIDTH(AW),
.LGNLINES(OPT_LGDCACHE-3), .OPT_LOCAL_BUS(WITH_LOCAL_BUS),
.OPT_PIPE(OPT_MEMPIPE),
.OPT_LOCK(OPT_LOCK)
`ifdef FORMAL
, .OPT_FIFO_DEPTH(2)
, .F_LGDEPTH(F_LGDEPTH)
`else
assign bus_lock = 1'b0;
`endif
) docache(i_clk, i_reset,
///{{{
(mem_ce)&&(set_cond), bus_lock,
(op_opn[2:0]), op_Bv, op_Av, op_R,
mem_busy, mem_pipe_stalled,
mem_valid, bus_err, mem_wreg, mem_result,
mem_cyc_gbl, mem_cyc_lcl,
mem_stb_gbl, mem_stb_lcl,
mem_we, mem_addr, mem_data, mem_sel,
mem_ack, mem_stall, mem_err, i_wb_data
`ifdef FORMAL
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding, f_mem_pc
`endif
// , o_dcache_debug
);
///}}}
end else begin : NO_CACHE
if (OPT_PIPELINED_BUS_ACCESS)
begin : MEM
 
pipemem #(.ADDRESS_WIDTH(AW),
.IMPLEMENT_LOCK(OPT_LOCK),
.WITH_LOCAL_BUS(WITH_LOCAL_BUS)
`ifdef FORMAL
, .OPT_MAXDEPTH(4'h3),
.F_LGDEPTH(F_LGDEPTH)
`endif
) domem(i_clk,i_reset,
///{{{
(mem_ce)&&(set_cond), bus_lock,
`ifdef OPT_PIPELINED_BUS_ACCESS
pipemem #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock,
(op_opn[2:0]), op_Bv, op_Av, op_R,
mem_busy, mem_pipe_stalled,
mem_valid, bus_err, mem_wreg, mem_result,
1669,22 → 1272,10
mem_cyc_gbl, mem_cyc_lcl,
mem_stb_gbl, mem_stb_lcl,
mem_we, mem_addr, mem_data, mem_sel,
mem_ack, mem_stall, mem_err, i_wb_data
`ifdef FORMAL
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding, f_mem_pc
`endif
);
//}}}
end else begin : MEM
mem_ack, mem_stall, mem_err, i_wb_data);
 
memops #(.ADDRESS_WIDTH(AW),
.IMPLEMENT_LOCK(OPT_LOCK),
.WITH_LOCAL_BUS(WITH_LOCAL_BUS)
`ifdef FORMAL
, .F_LGDEPTH(F_LGDEPTH)
`endif // F_LGDEPTH
) domem(i_clk,i_reset,
//{{{
`else // PIPELINED_BUS_ACCESS
memops #(AW,IMPLEMENT_LOCK,WITH_LOCAL_BUS) domem(i_clk, i_rst,
(mem_ce)&&(set_cond), bus_lock,
(op_opn[2:0]), op_Bv, op_Av, op_R,
mem_busy,
1692,28 → 1283,14
mem_cyc_gbl, mem_cyc_lcl,
mem_stb_gbl, mem_stb_lcl,
mem_we, mem_addr, mem_data, mem_sel,
mem_ack, mem_stall, mem_err, i_wb_data
`ifdef FORMAL
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding
`endif
);
`ifdef FORMAL
assign f_mem_pc = 1'b0;
`endif
//}}}
assign mem_pipe_stalled = 1'b0;
end end endgenerate
mem_ack, mem_stall, mem_err, i_wb_data);
assign mem_pipe_stalled = 1'b0;
`endif // PIPELINED_BUS_ACCESS
assign mem_rdbusy = ((mem_busy)&&(!mem_we));
 
assign mem_rdbusy = (mem_busy)&&((!OPT_PIPELINED)||(!mem_we));
 
// Either the prefetch or the instruction gets the memory bus, but
// never both.
wbdblpriarb #(.DW(32),.AW(AW)
`ifdef FORMAL
,.F_LGDEPTH(F_LGDEPTH), .F_MAX_STALL(2), .F_MAX_ACK_DELAY(2)
`endif // FORMAL
) pformem(i_clk, i_reset,
//{{{
wbdblpriarb #(32,AW) pformem(i_clk, i_rst,
// Memory access to the arbiter, priority position
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl,
mem_we, mem_addr, mem_data, mem_sel,
1733,20 → 1310,10
// Common wires, in and out, of the arbiter
o_wb_gbl_cyc, o_wb_lcl_cyc, o_wb_gbl_stb, o_wb_lcl_stb,
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_err
`ifdef FORMAL
,f_gbl_arb_nreqs, f_gbl_arb_nacks, f_gbl_arb_outstanding,
f_lcl_arb_nreqs, f_lcl_arb_nacks, f_lcl_arb_outstanding,
f_gbl_mem_nreqs, f_gbl_mem_nacks, f_gbl_mem_outstanding,
f_lcl_mem_nreqs, f_lcl_mem_nacks, f_lcl_mem_outstanding,
f_gbl_pf_nreqs, f_gbl_pf_nacks, f_gbl_pf_outstanding,
f_lcl_pf_nreqs, f_lcl_pf_nacks, f_lcl_pf_outstanding
`endif
);
//}}}
//}}}
i_wb_ack, i_wb_stall, i_wb_err);
 
 
 
//
//
//
1757,7 → 1324,6
//
// 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
1775,23 → 1341,20
assign wr_reg_ce = (dbgv)||(mem_valid)
||((!clear_pipeline)&&(!alu_illegal)
&&(((alu_wR)&&(alu_valid))
||((div_valid)&&(!div_error))
||((fpu_valid)&&(!fpu_error))));
||(div_valid)||(fpu_valid)));
// 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_NO_USERMODE)
begin
assign wr_reg_id[3:0] = (mem_valid)
? mem_wreg[3:0] : alu_reg[3:0];
`ifdef OPT_NO_USERMODE
assign wr_reg_id[3:0] = (alu_wR|div_valid|fpu_valid)
? alu_reg[3:0]:mem_wreg[3:0];
assign wr_reg_id[4] = 1'b0;
`else
assign wr_reg_id = (alu_wR|div_valid|fpu_valid)?alu_reg:mem_wreg;
`endif
 
assign wr_reg_id[4] = 1'b0;
end else begin
assign wr_reg_id = (mem_valid) ? mem_wreg : alu_reg;
end endgenerate
 
// Are we writing to the CC register?
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});
1806,30 → 1369,19
:((dbgv) ? dbg_val : alu_result));
assign wr_spreg_vl = ((mem_valid) ? mem_result
:((dbgv) ? dbg_val : alu_result));
always @(posedge i_clk)
if (wr_reg_ce)
`ifdef OPT_NO_USERMODE
regset[wr_reg_id[3:0]] <= wr_gpreg_vl;
`else
regset[wr_reg_id] <= wr_gpreg_vl;
`endif
 
generate if (OPT_NO_USERMODE)
begin : SET_REGISTERS
 
always @(posedge i_clk)
if (wr_reg_ce)
regset[{1'b0,wr_reg_id[3:0]}] <= wr_gpreg_vl;
 
end else begin : SET_REGISTERS
 
always @(posedge i_clk)
if (wr_reg_ce)
regset[wr_reg_id] <= wr_gpreg_vl;
 
end endgenerate
 
 
//
// Write back to the condition codes/flags register ...
// 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);
assign wr_flags_ce = ((alu_wF)||(div_valid)||(fpu_valid))&&(!clear_pipeline)&&(!alu_illegal);
assign w_uflags = { 1'b0, uhalt_phase, ufpu_err_flag,
udiv_err_flag, ubus_err_flag, trap, ill_err_u,
ubreak, step, 1'b1, sleep,
1874,35 → 1426,30
// condition has taken place.
initial break_en = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_halt))
if ((i_rst)||(i_halt))
break_en <= 1'b0;
else if ((wr_reg_ce)&&(wr_write_scc))
break_en <= wr_spreg_vl[`CPU_BREAK_BIT];
 
generate if (OPT_PIPELINED)
begin : GEN_PENDING_BREAK
reg r_break_pending;
`ifdef OPT_PIPELINED
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)&&(!mem_busy)
&&(!wr_reg_ce);
// else
// r_break_pending <= 1'b0;
assign break_pending = r_break_pending;
end else begin
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 <= (!alu_busy)&&(!div_busy)&&(!fpu_busy)&&(!mem_busy)&&(!wr_reg_ce);
else
r_break_pending <= 1'b0;
assign break_pending = r_break_pending;
`else
assign break_pending = op_break;
`endif
 
assign break_pending = op_break;
end endgenerate
 
 
assign o_break = ((break_en)||(!op_gie))&&(break_pending)
&&(!clear_pipeline)
||(ill_err_i)
||((!alu_gie)&&(bus_err))
||((!alu_gie)&&(div_error))
||((!alu_gie)&&(fpu_error))
1915,108 → 1462,80
// 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_NO_USERMODE)
begin : GEN_NO_USERMODE_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]))
r_sleep_is_halt <= 1'b1;
`ifdef OPT_NO_USERMODE
reg r_sleep_is_halt;
initial r_sleep_is_halt = 1'b0;
always @(posedge i_clk)
if (i_rst)
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]))
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 else begin : GEN_SLEEP
// 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_rst)||((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;
`else
always @(posedge i_clk)
if ((i_rst)||(w_switch_to_interrupt))
sleep <= 1'b0;
else if ((wr_reg_ce)&&(wr_write_cc)&&(!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 shouold only be possible if the interrupt
// flag isn't set.
// 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_reg_ce)&&(wr_write_cc)&&(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];
`endif
 
always @(posedge i_clk)
if ((i_reset)||(w_switch_to_interrupt))
sleep <= 1'b0;
else if ((wr_reg_ce)&&(wr_write_cc)&&(!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 shouold only be
// possible if the interrupt flag isn't set.
// 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_reg_ce)&&(wr_write_cc)
&&(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 endgenerate
 
always @(posedge i_clk)
if (i_reset)
if (i_rst)
step <= 1'b0;
else if ((wr_reg_ce)&&(!alu_gie)&&(wr_write_ucc))
step <= wr_spreg_vl[`CPU_STEP_BIT];
 
// The GIE register. Only interrupts can disable the interrupt register
generate if (OPT_NO_USERMODE)
begin
 
assign w_switch_to_interrupt = 1'b0;
assign w_release_from_interrupt = 1'b0;
 
end else begin : GEN_PENDING_INTERRUPT
reg r_pending_interrupt;
 
always @(posedge i_clk)
if (i_reset)
r_pending_interrupt <= 1'b0;
else if ((clear_pipeline)||(w_switch_to_interrupt)||(!gie))
r_pending_interrupt <= 1'b0;
else if (i_interrupt)
r_pending_interrupt <= 1'b1;
else if (adf_ce_unconditional)
begin
if ((op_illegal)||(step)||(break_pending))
r_pending_interrupt <= 1'b1;
end else if (break_pending)
r_pending_interrupt <= 1'b1;
else if ((mem_ce)&&(step))
r_pending_interrupt <= 1'b1;
 
assign pending_interrupt = r_pending_interrupt;
 
 
assign w_switch_to_interrupt = (gie)&&(
`ifdef OPT_NO_USERMODE
assign w_switch_to_interrupt = 1'b0;
assign w_release_from_interrupt = 1'b0;
`else
assign w_switch_to_interrupt = (gie)&&(
// On interrupt (obviously)
((pending_interrupt)
&&(!alu_phase)&&(!bus_lock)&&(!mem_busy))
//
((i_interrupt)&&(!alu_phase)&&(!bus_lock))
// If we are stepping the CPU
||(((alu_pc_valid)||(mem_pc_valid))&&(step)&&(!alu_phase)&&(!bus_lock))
// If we encounter a break instruction, if the break
// enable isn't set.
||((master_ce)&&(break_pending)&&(!break_en))
// On an illegal instruction
||((alu_illegal)&&(!clear_pipeline))
// 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)
//
//
||(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))
2026,66 → 1545,60
&&(((wr_reg_ce)&&(wr_spreg_vl[`CPU_GIE_BIT])
&&(wr_write_scc))
);
end endgenerate
`endif
 
generate if (OPT_NO_USERMODE)
begin
assign gie = 1'b0;
end else begin : SET_GIE
`ifdef OPT_NO_USERMODE
assign gie = 1'b0;
`else
reg r_gie;
 
reg r_gie;
initial r_gie = 1'b0;
always @(posedge i_clk)
if (i_rst)
r_gie <= 1'b0;
else if (w_switch_to_interrupt)
r_gie <= 1'b0;
else if (w_release_from_interrupt)
r_gie <= 1'b1;
assign gie = r_gie;
`endif
 
initial r_gie = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_gie <= 1'b0;
else if (w_switch_to_interrupt)
r_gie <= 1'b0;
else if (w_release_from_interrupt)
r_gie <= 1'b1;
assign gie = r_gie;
end endgenerate
`ifdef OPT_NO_USERMODE
assign trap = 1'b0;
assign ubreak = 1'b0;
`else
reg r_trap;
 
generate if (OPT_NO_USERMODE)
begin
initial r_trap = 1'b0;
always @(posedge i_clk)
if ((i_rst)||(w_release_from_interrupt))
r_trap <= 1'b0;
else if ((alu_gie)&&(wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT])
&&(wr_write_ucc)) // &&(wr_reg_id[4]) implied
r_trap <= 1'b1;
else if ((wr_reg_ce)&&(wr_write_ucc)&&(!alu_gie))
r_trap <= (r_trap)&&(wr_spreg_vl[`CPU_TRAP_BIT]);
 
assign trap = 1'b0;
assign ubreak = 1'b0;
reg r_ubreak;
 
end else begin : SET_TRAP_N_UBREAK
initial r_ubreak = 1'b0;
always @(posedge i_clk)
if ((i_rst)||(w_release_from_interrupt))
r_ubreak <= 1'b0;
else if ((op_gie)&&(break_pending)&&(w_switch_to_interrupt))
r_ubreak <= 1'b1;
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc))
r_ubreak <= (ubreak)&&(wr_spreg_vl[`CPU_BREAK_BIT]);
 
reg r_trap;
assign trap = r_trap;
assign ubreak = r_ubreak;
`endif
 
initial r_trap = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(w_release_from_interrupt))
r_trap <= 1'b0;
else if ((alu_gie)&&(wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT])
&&(wr_write_ucc)) // &&(wr_reg_id[4]) implied
r_trap <= 1'b1;
else if ((wr_reg_ce)&&(wr_write_ucc)&&(!alu_gie))
r_trap <= (r_trap)&&(wr_spreg_vl[`CPU_TRAP_BIT]);
 
reg r_ubreak;
 
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))
r_ubreak <= 1'b1;
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc))
r_ubreak <= (ubreak)&&(wr_spreg_vl[`CPU_BREAK_BIT]);
 
assign trap = r_trap;
assign ubreak = r_ubreak;
 
end endgenerate
 
 
`ifdef OPT_ILLEGAL_INSTRUCTION
initial ill_err_i = 1'b0;
always @(posedge i_clk)
if (i_reset)
if (i_rst)
ill_err_i <= 1'b0;
// Only the debug interface can clear this bit
else if ((dbgv)&&(wr_write_scc))
2093,37 → 1606,35
else if ((alu_illegal)&&(!alu_gie)&&(!clear_pipeline))
ill_err_i <= 1'b1;
 
generate if (OPT_NO_USERMODE)
begin
`ifdef OPT_NO_USERMODE
assign ill_err_u = 1'b0;
`else
reg r_ill_err_u;
 
assign ill_err_u = 1'b0;
initial r_ill_err_u = 1'b0;
always @(posedge i_clk)
// The bit is automatically cleared on release from interrupt
// or reset
if ((i_rst)||(w_release_from_interrupt))
r_ill_err_u <= 1'b0;
// If the supervisor (or debugger) writes to this register,
// clearing the bit, then clear it
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc))
r_ill_err_u <=((ill_err_u)&&(wr_spreg_vl[`CPU_ILL_BIT]));
else if ((alu_illegal)&&(alu_gie)&&(!clear_pipeline))
r_ill_err_u <= 1'b1;
 
end else begin : SET_USER_ILLEGAL_INSN
 
reg r_ill_err_u;
 
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;
// If the supervisor (or debugger) writes to this
// register, clearing the bit, then clear it
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc))
r_ill_err_u <=((ill_err_u)&&(wr_spreg_vl[`CPU_ILL_BIT]));
else if ((alu_illegal)&&(alu_gie)&&(!clear_pipeline))
r_ill_err_u <= 1'b1;
 
assign ill_err_u = r_ill_err_u;
 
end endgenerate
 
assign ill_err_u = r_ill_err_u;
`endif
`else
assign ill_err_u = 1'b0;
assign ill_err_i = 1'b0;
`endif
// 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)
if (i_rst)
ibus_err_flag <= 1'b0;
else if ((dbgv)&&(wr_write_scc))
ibus_err_flag <= (ibus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]);
2131,29 → 1642,26
ibus_err_flag <= 1'b1;
// User bus error flag -- if ever set, it will cause an interrupt to
// supervisor mode.
generate if (OPT_NO_USERMODE)
begin
`ifdef OPT_NO_USERMODE
assign ubus_err_flag = 1'b0;
`else
reg r_ubus_err_flag;
 
assign ubus_err_flag = 1'b0;
initial r_ubus_err_flag = 1'b0;
always @(posedge i_clk)
if ((i_rst)||(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 ((bus_err)&&(alu_gie))
r_ubus_err_flag <= 1'b1;
 
end else begin : SET_USER_BUSERR
assign ubus_err_flag = r_ubus_err_flag;
`endif
 
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 ((bus_err)&&(alu_gie))
r_ubus_err_flag <= 1'b1;
 
assign ubus_err_flag = r_ubus_err_flag;
end endgenerate
 
generate if (IMPLEMENT_DIVIDE != 0)
begin : DIVERR
generate
if (IMPLEMENT_DIVIDE != 0)
begin
reg r_idiv_err_flag, r_udiv_err_flag;
 
// Supervisor/interrupt divide (by zero) error flag -- this will
2161,7 → 1669,7
// to be able to tell if/why the CPU crashed.
initial r_idiv_err_flag = 1'b0;
always @(posedge i_clk)
if (i_reset)
if (i_rst)
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]);
2169,39 → 1677,37
r_idiv_err_flag <= 1'b1;
 
assign idiv_err_flag = r_idiv_err_flag;
`ifdef OPT_NO_USERMODE
assign udiv_err_flag = 1'b0;
`else
// 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_rst)||(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;
 
if (OPT_NO_USERMODE)
begin
assign udiv_err_flag = 1'b0;
end else begin
 
// 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
assign udiv_err_flag = r_udiv_err_flag;
`endif
end else begin
assign idiv_err_flag = 1'b0;
assign udiv_err_flag = 1'b0;
end endgenerate
 
generate if (IMPLEMENT_FPU !=0)
begin : FPUERR
generate
if (IMPLEMENT_FPU !=0)
begin
// 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)
if (i_rst)
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]);
2211,7 → 1717,7
// 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))
if ((i_rst)&&(w_release_from_interrupt))
r_ufpu_err_flag <= 1'b0;
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)
&&(wr_write_ucc))
2226,47 → 1732,39
assign ufpu_err_flag = 1'b0;
end endgenerate
 
generate if (OPT_CIS)
begin : GEN_IHALT_PHASE
reg r_ihalt_phase;
`ifdef OPT_CIS
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;
initial r_ihalt_phase = 0;
always @(posedge i_clk)
if (i_rst)
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 = r_ihalt_phase;
 
assign ihalt_phase = 1'b0;
`ifdef OPT_NO_USERMODE
assign uhalt_phase = 1'b0;
`else
reg r_uhalt_phase;
 
end endgenerate
 
generate if ((!OPT_CIS) || (OPT_NO_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))
initial r_uhalt_phase = 0;
always @(posedge i_clk)
if ((i_rst)||(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];
else if ((!alu_gie)&&(wr_reg_ce)&&(wr_write_ucc))
r_uhalt_phase <= wr_spreg_vl[`CPU_PHASE_BIT];
 
assign uhalt_phase = r_uhalt_phase;
assign uhalt_phase = r_uhalt_phase;
`endif
`else
assign ihalt_phase = 1'b0;
assign uhalt_phase = 1'b0;
`endif
 
end endgenerate
 
//
// Write backs to the PC register, and general increments of it
// We support two: upc and ipc. If the instruction is normal,
2277,217 → 1775,134
// 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?
generate if (OPT_NO_USERMODE)
begin
`ifdef OPT_NO_USERMODE
assign upc = {(AW+2){1'b0}};
`else
reg [(AW+1):0] r_upc;
 
assign upc = {(AW+2){1'b0}};
 
end else 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 endgenerate
 
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;
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, 2'b00 };
assign upc = r_upc;
`endif
 
initial pf_pc = { RESET_BUS_ADDRESS, 2'b00 };
always @(posedge i_clk)
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)&&((w_clear_icache)||(dbg_clear_pipe))))
pf_pc <= { ipc[(AW+1):2], 2'b00 };
else if ((w_release_from_interrupt)||((gie)&&((w_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)&&(pf_valid)))
pf_pc <= { pf_pc[(AW+1):2] + 1'b1, 2'b00 };
if (i_rst)
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, 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);
if (i_rst)
pf_pc <= { RESET_BUS_ADDRESS, 2'b00 };
else if ((w_switch_to_interrupt)||((!gie)&&(w_clear_icache)))
pf_pc <= { ipc[(AW+1):2], 2'b00 };
else if ((w_release_from_interrupt)||((gie)&&(w_clear_icache)))
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)&&(!clear_pipeline))
pf_pc <= { dcd_branch_pc + 1'b1, 2'b00 };
else if ((new_pc)||((!pf_stalled)&&(pf_valid)))
pf_pc <= { pf_pc[(AW+1):2] + {{(AW-1){1'b0}},1'b1}, 2'b00 };
 
// 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.
reg r_clear_icache;
initial r_clear_icache = 1'b1;
always @(posedge i_clk)
if (i_reset)
r_clear_icache <= 1'b0;
else if ((r_halted)&&(i_clear_pf_cache))
r_clear_icache <= 1'b1;
else if ((wr_reg_ce)&&(wr_write_scc))
r_clear_icache <= wr_spreg_vl[`CPU_CLRCACHE_BIT];
else
r_clear_icache <= 1'b0;
if ((i_rst)||(i_clear_pf_cache))
r_clear_icache <= 1'b1;
else if ((wr_reg_ce)&&(wr_write_scc))
r_clear_icache <= wr_spreg_vl[`CPU_CLRCACHE_BIT];
else
r_clear_icache <= 1'b0;
assign w_clear_icache = r_clear_icache;
 
initial new_pc = 1'b1;
always @(posedge i_clk)
if ((i_reset)||(w_clear_icache)||(dbg_clear_pipe))
if ((i_rst)||(w_clear_icache))
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))
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc))
new_pc <= 1'b1;
else
new_pc <= 1'b0;
 
//
// The debug write-back interface
//{{{
// The debug interface
wire [31:0] w_debug_pc;
generate if (OPT_NO_USERMODE)
begin
 
assign w_debug_pc[(AW+1):0] = { ipc, 2'b00 };
end else begin
 
assign w_debug_pc[(AW+1):0] = { (i_dbg_reg[4])
`ifdef OPT_NO_USERMODE
assign w_debug_pc[(AW+1):0] = { ipc, 2'b00 };
`else
assign w_debug_pc[(AW+1):0] = { (i_dbg_reg[4])
? { upc[(AW+1):2], uhalt_phase, 1'b0 }
: { ipc[(AW+1):2], ihalt_phase, 1'b0 } };
end endgenerate
 
`endif
generate
if (AW<30)
assign w_debug_pc[31:(AW+2)] = 0;
endgenerate
 
generate if (OPT_NO_USERMODE)
begin : NO_USER_SETDBG
 
always @(posedge i_clk)
always @(posedge i_clk)
begin
`ifdef OPT_NO_USERMODE
o_dbg_reg <= regset[i_dbg_reg[3:0]];
if (i_dbg_reg[3:0] == `CPU_PC_REG)
o_dbg_reg <= w_debug_pc;
else if (i_dbg_reg[3:0] == `CPU_CC_REG)
begin
o_dbg_reg <= regset[i_dbg_reg[3:0]];
if (i_dbg_reg[3:0] == `CPU_PC_REG)
o_dbg_reg <= w_debug_pc;
else if (i_dbg_reg[3:0] == `CPU_CC_REG)
begin
o_dbg_reg[14:0] <= w_iflags;
o_dbg_reg[15] <= 1'b0;
o_dbg_reg[31:23] <= w_cpu_info;
o_dbg_reg[`CPU_GIE_BIT] <= gie;
end
o_dbg_reg[14:0] <= w_iflags;
o_dbg_reg[15] <= 1'b0;
o_dbg_reg[31:23] <= w_cpu_info;
o_dbg_reg[`CPU_GIE_BIT] <= gie;
end
end else begin : SETDBG
 
`ifdef NO_DISTRIBUTED_RAM
reg [31:0] pre_dbg_reg;
always @(posedge i_clk)
pre_dbg_reg <= regset[i_dbg_reg];
 
always @(posedge i_clk)
begin
o_dbg_reg <= pre_dbg_reg;
if (i_dbg_reg[3:0] == `CPU_PC_REG)
o_dbg_reg <= w_debug_pc;
else if (i_dbg_reg[3:0] == `CPU_CC_REG)
begin
o_dbg_reg[14:0] <= (i_dbg_reg[4])
? w_uflags : w_iflags;
o_dbg_reg[15] <= 1'b0;
o_dbg_reg[31:23] <= w_cpu_info;
o_dbg_reg[`CPU_GIE_BIT] <= gie;
end
end
 
`else
always @(posedge i_clk)
o_dbg_reg <= regset[i_dbg_reg];
if (i_dbg_reg[3:0] == `CPU_PC_REG)
o_dbg_reg <= w_debug_pc;
else if (i_dbg_reg[3:0] == `CPU_CC_REG)
begin
o_dbg_reg <= regset[i_dbg_reg];
if (i_dbg_reg[3:0] == `CPU_PC_REG)
o_dbg_reg <= w_debug_pc;
else if (i_dbg_reg[3:0] == `CPU_CC_REG)
begin
o_dbg_reg[14:0] <= (i_dbg_reg[4])
? w_uflags : w_iflags;
o_dbg_reg[15] <= 1'b0;
o_dbg_reg[31:23] <= w_cpu_info;
o_dbg_reg[`CPU_GIE_BIT] <= gie;
end
o_dbg_reg[14:0] <= (i_dbg_reg[4])?w_uflags:w_iflags;
o_dbg_reg[15] <= 1'b0;
o_dbg_reg[31:23] <= w_cpu_info;
o_dbg_reg[`CPU_GIE_BIT] <= gie;
end
`endif
end
 
end endgenerate
 
always @(posedge i_clk)
o_dbg_cc <= { o_break, bus_err, gie, sleep };
 
generate if (OPT_PIPELINED)
begin
always @(posedge i_clk)
r_halted <= (i_halt)&&(!alu_phase)&&(!bus_lock)&&(
// To be halted, any long lasting instruction
// must be completed.
(!pf_cyc)&&(!mem_busy)&&(!alu_busy)
&&(!div_busy)&&(!fpu_busy)
// Operations must either be valid, or illegal
&&((op_valid)||(i_reset)||(dcd_illegal))
// Decode stage must be either valid, in reset,
// or producing an illelgal instruction
&&((dcd_valid)||(i_reset)||(pf_illegal)));
end else begin
 
always @(posedge i_clk)
r_halted <= (i_halt)&&(!alu_phase)
// To be halted, any long lasting instruction
// must be completed.
&&(!pf_cyc)&&(!mem_busy)&&(!alu_busy)
&&(!div_busy)&&(!fpu_busy);
end endgenerate
`ifdef NO_DISTRIBUTED_RAM
reg r_dbg_stall;
initial r_dbg_stall = 1'b1;
 
`ifdef OPT_PIPELINED
always @(posedge i_clk)
if (i_reset)
r_dbg_stall <= 1'b1;
else if (!r_halted)
r_dbg_stall <= 1'b1;
else
r_dbg_stall <= (!i_dbg_we)||(!r_dbg_stall);
 
assign o_dbg_stall = !r_halted;
r_halted <= (i_halt)&&(
// To be halted, any long lasting instruction must
// be completed.
(!pf_cyc)&&(!mem_busy)&&(!alu_busy)
&&(!div_busy)&&(!fpu_busy)
// Operations must either be valid, or illegal
&&((op_valid)||(i_rst)||(dcd_illegal))
// Decode stage must be either valid, in reset, or ill
&&((dcd_valid)||(i_rst)||(pf_illegal)));
`else
always @(posedge i_clk)
r_halted <= (i_halt)&&((op_valid)||(i_rst));
`endif
assign o_dbg_stall = !r_halted;
`endif
//}}}
 
//}}}
 
//
//
// Produce accounting outputs: Account for any CPU stalls, so we can
2499,1884 → 1914,52
assign o_i_count = (alu_pc_valid)&&(!clear_pipeline);
 
`ifdef DEBUG_SCOPE
//{{{
 
reg debug_trigger;
initial debug_trigger = 1'b0;
always @(posedge i_clk)
debug_trigger <= (!i_halt)&&(o_break);
o_debug <= {
/*
o_break, i_wb_err, pf_pc[1:0],
flags,
pf_valid, dcd_valid, op_valid, alu_valid, mem_valid,
op_ce, alu_ce, mem_ce,
//
master_ce, op_valid_alu, op_valid_mem,
//
alu_stall, mem_busy, op_pipe, mem_pipe_stalled,
mem_we,
// ((op_valid_alu)&&(alu_stall))
// ||((op_valid_mem)&&(~op_pipe)&&(mem_busy))
// ||((op_valid_mem)&&( op_pipe)&&(mem_pipe_stalled)));
// op_Av[23:20], op_Av[3:0],
gie, sleep, wr_reg_ce, wr_gpreg_vl[4:0]
*/
/*
i_rst, master_ce, (new_pc),
((dcd_early_branch)&&(dcd_valid)),
pf_valid, pf_illegal,
op_ce, dcd_ce, dcd_valid, dcd_stalled,
pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err,
pf_pc[7:0], pf_addr[7:0]
*/
 
wire [31:0] debug_flags;
assign debug_flags = { debug_trigger, 3'b101,
master_ce, i_halt, o_break, sleep,
gie, ibus_err_flag, trap, ill_err_i,
w_clear_icache, pf_valid, 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, mem_we,
mem_busy, mem_pipe_stalled, (new_pc), (dcd_early_branch) };
i_wb_err, gie, alu_illegal,
(new_pc)||((dcd_early_branch)&&(~clear_pipeline)),
mem_busy,
(mem_busy)?{ (o_wb_gbl_stb|o_wb_lcl_stb), o_wb_we,
o_wb_addr[8:0] }
: { pf_instruction[31:21] },
pf_valid, (pf_valid) ? alu_pc[14:0]
:{ pf_cyc, pf_stb, pf_pc[14:2] }
 
/*
wire [25:0] bus_debug;
assign bus_debug = { debug_trigger,
mem_ce, mem_we, mem_busy, mem_pipe_stalled,
o_wb_gbl_cyc, o_wb_gbl_stb, o_wb_lcl_cyc, o_wb_lcl_stb,
o_wb_we, i_wb_ack, i_wb_stall, i_wb_err,
pf_cyc, pf_stb, pf_ack, pf_stall,
pf_err,
mem_cyc_gbl, mem_stb_gbl, mem_cyc_lcl, mem_stb_lcl,
mem_we, mem_ack, mem_stall, mem_err
/*
i_wb_err, gie, new_pc, dcd_early_branch, // 4
pf_valid, pf_cyc, pf_stb, pf_instruction_pc[0], // 4
pf_instruction[30:27], // 4
dcd_gie, mem_busy, o_wb_gbl_cyc, o_wb_gbl_stb, // 4
dcd_valid,
((dcd_early_branch)&&(~clear_pipeline)) // 15
? dcd_branch_pc[14:0]:pf_pc[14:0]
*/
};
*/
 
// Verilator lint_off UNUSED
wire [27:0] dbg_pc, dbg_wb_addr;
// Verilator lint_on UNUSED
generate if (AW-1 < 27)
begin
assign dbg_pc[(AW-1):0] = pf_pc[(AW+1):2];
assign dbg_pc[27:AW] = 0;
 
assign dbg_wb_addr[(AW-1):0] = o_wb_addr;
assign dbg_wb_addr[27:AW] = 0;
end else // if (AW-1 >= 27)
begin
assign dbg_pc[27:0] = pf_pc[29:2];
assign dbg_wb_addr = o_wb_addr;
end endgenerate
 
always @(posedge i_clk)
begin
if ((i_halt)||(!master_ce)||(debug_trigger)||(o_break))
o_debug <= debug_flags;
else if ((mem_valid)||((!clear_pipeline)&&(!alu_illegal)
&&(((alu_wR)&&(alu_valid))
||(div_valid)||(fpu_valid))))
o_debug <= { debug_trigger, 1'b0, wr_reg_id[3:0], wr_gpreg_vl[25:0]};
else if (clear_pipeline)
o_debug <= { debug_trigger, 3'b100, dbg_pc };
else if ((o_wb_gbl_stb)|(o_wb_lcl_stb))
o_debug <= {debug_trigger, 2'b11, o_wb_gbl_stb, o_wb_we,
(o_wb_we)?o_wb_data[26:0] : dbg_wb_addr[26:0] };
else
o_debug <= debug_flags;
// o_debug[25:0] <= bus_debug;
end
//}}}
`endif
 
// Make verilator happy
//{{{
// verilator lint_off UNUSED
wire [56:0] unused;
assign unused = { pf_new_pc,
fpu_ce, pf_data, 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, mem_pipe_stalled, prelock_stall,
dcd_F };
generate if (AW+2 < 32)
begin
wire [31:(AW+2)] generic_ignore;
assign generic_ignore = wr_spreg_vl[31:(AW+2)];
end endgenerate
// verilator lint_on UNUSED
//}}}
 
// Formal methods
//{{{
`ifdef FORMAL
// PHASE_ONE is defined by default if nothing else is defined
//
`ifdef ZIPCPU
`define ASSUME assume
`else
`define ASSUME assert
`endif
`define ASSERT assert
//
//
 
wire [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;
////////////////////////////////////////////////////////////////
//
//
// Formal methods section
//
//
////////////////////////////////////////////////////////////////
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
 
initial assume(i_reset);
initial assume(!i_wb_ack);
initial assume(!i_wb_err);
always @(posedge i_clk)
if (!f_past_valid)
begin
assume(i_reset);
assume(!i_wb_ack);
assume(!i_wb_err);
end
 
//////////////////////////////////////////////
//
//
// The debugging interface
//
//
//////////////////////////////////////////////
//
//
 
// Reading from the debugging interface
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_halt))&&(!$past(i_dbg_we)))
begin
`ifdef NO_DISTRIBUTED_RAM
if ($past(i_dbg_reg[3:1],2) != 3'h7)
assert(o_dbg_reg
== regset[i_dbg_reg[$past(i_dbg_reg,2)]]);
`else
if ($past(i_dbg_reg[3:1]) != 3'h7)
assert(o_dbg_reg == regset[i_dbg_reg[$past(i_dbg_reg)]]);
`endif
if ($past(i_dbg_reg[4:0]) == 5'h0f)
assert(o_dbg_reg[AW+1:0] == { ipc[(AW+1):2], ihalt_phase, 1'b0});
if ($past(i_dbg_reg[4:0]) == 5'h1f)
assert(o_dbg_reg[AW+1:0] == { upc[(AW+1):2], uhalt_phase, 1'b0});
if ($past(i_dbg_reg[4:0]) == 5'h0e)
begin
assert(o_dbg_reg[14:6] == w_iflags[14:6]);
assert(o_dbg_reg[ 4:0] == w_iflags[ 4:0]);
end
 
if ($past(i_dbg_reg[4:0]) == 5'h1e)
begin
assert(o_dbg_reg[14:6] == w_uflags[14:6]);
assert(o_dbg_reg[ 4:0] == w_uflags[ 4:0]);
end
 
if ($past(i_dbg_reg[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] == gie);
end
end
 
reg [2:0] f_dbg_pc_seq, f_dbg_cc_seq, f_dbg_reg_seq;
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] <= r_halted && i_dbg_we
&& (i_dbg_reg == { 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])
assert(dbgv && alu_reg == { gie, `CPU_PC_REG });
if (f_dbg_pc_seq[1])
begin
assert(clear_pipeline);
assert(pf_request_address == $past(i_dbg_data,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] <= r_halted && i_dbg_we
&& (i_dbg_reg == { gie, `CPU_CC_REG });
f_dbg_cc_seq[2:1] <= f_dbg_cc_seq[1:0];
end
 
always @(posedge i_clk)
begin
if (f_dbg_cc_seq[1])
begin
assert(wr_reg_ce);
assert(wr_reg_id == $past(i_dbg_reg,2));
assert(wr_spreg_vl == $past(i_dbg_data));
end
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] <= r_halted && i_dbg_we
&& (i_dbg_reg[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])
begin
assert(dbgv && alu_reg == $past(i_dbg_reg));
assert($past(i_dbg_reg[3:1]) != 3'h7);
assert(dbg_val == $past(i_dbg_data));
end
 
 
if (f_dbg_reg_seq[1])
begin
assert(wr_reg_ce);
assert(wr_gpreg_vl == $past(i_dbg_data,2));
assert(wr_reg_id == $past(i_dbg_reg,2));
end
end
 
/*
`ifdef NO_DISTRIBUTED_RAM
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_past_valid)))
assert(o_dbg_ack == $past(i_dbg_stb,2));
`else // NO_DISTRIBUTED_RAM
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_past_valid)))
assert(o_dbg_ack == $past(i_dbg_stb));
`endif
*/
 
//////////////////////////////////////////////
//
//
// Problem limiting assumptions
//
//
//////////////////////////////////////////////
//
// Take care that the assumptions below are actually representative
// of how the CPU is used. One "careless" assumption could render
// the proof meaningless.
//
// Because of the consequences of a careless assumption, we'll work
// to place all of our assumptions at the beginning of the formal
// properties for any phase.
//
 
// If the CPU is not halted, the debug interface will not try writing
// to the CPU, nor will it try to issue a clear i-cache command
always @(*)
if (!r_halted)
begin
assume(!i_dbg_we);
assume(!i_clear_pf_cache);
end
 
// A debug write will only take place during a CPUI halt
always @(posedge i_clk)
if (i_dbg_we)
assume(i_halt);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_dbg_we))&&(!$past(o_dbg_stall)))
assume(i_halt);
 
always @(*)
if ((!r_halted)||(!i_halt))
assume(!i_clear_pf_cache);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_clear_pf_cache)))
assume(i_halt);
 
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_dbg_we))&&($past(o_dbg_stall)))
assume(($stable(i_dbg_we))&&($stable(i_dbg_data)));
 
 
// Any attempt to set the PC will leave the bottom two bits clear
always @(*)
if ((wr_reg_ce)&&(wr_reg_id[3:0] == `CPU_PC_REG))
assume(wr_gpreg_vl[1:0] == 2'b00);
 
// Once a halt is requested, the halt request will remain active until
// the CPU comes to a complete halt.
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_halt))&&(!$past(r_halted)))
assume(i_halt);
 
// 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)))
begin
if ($past(gie))
assume(i_interrupt);
end
 
always @(*)
assume(!i_dbg_we);
 
////////////////////////////////////////////////////////////////
//
//
// Reset checks
//
//
////////////////////////////////////////////////////////////////
//
//
 
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
// Initial assertions
`ASSERT(!pf_valid);
`ASSERT(!dcd_phase);
`ASSERT(!op_phase);
`ASSERT(!alu_phase);
//
`ASSERT(!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(!mem_valid);
`ASSERT(!mem_busy);
`ASSERT(!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 (pf_valid) `ASSERT(f_past_valid);
if (dcd_valid) `ASSERT(f_past_valid);
if (alu_pc_valid) `ASSERT(f_past_valid);
if (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 (mem_busy) `ASSERT(f_past_valid);
if (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(!mem_busy);
`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)
&&(!mem_busy)&&(!mem_valid)&&(!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);
if (!dcd_valid)
assert(dcd_ce);
end
 
//
// Make sure the ops stage is never permanently stalled
always @(*)
if ((op_valid)&&(master_ce)&&(!clear_pipeline)&&(!i_reset)
&&(!div_busy)&&(!div_valid)
&&(!mem_busy)&&(!mem_valid)&&(!bus_err)
&&(!alu_busy)&&(!alu_pc_valid)
&&(!fpu_busy)&&(!fpu_valid)&&(!fpu_error)
&&(!op_break)&&(!o_break)
&&(!w_switch_to_interrupt)
&&(!alu_illegal)
&&(!ibus_err_flag)&&(!ill_err_i)&&(!idiv_err_flag))
`ASSERT(adf_ce_unconditional | mem_ce);
 
//
// 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))
`ASSERT(!dcd_early_branch);
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))
`ASSERT(!dcd_early_branch);
else
`ASSERT(!op_valid);
end
 
////////////////////////////////////////////////
//
// Assertions about the Program counter
//
////////////////////////////////////////////////
always @(*)
`ASSERT(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)&&(pf_valid))
`ASSERT(pf_gie == gie);
 
always @(*)
if ((pf_valid)&&(!clear_pipeline))
`ASSERT(pf_gie == gie);
 
////////////////////////////////////////////////
//
// Assertions about the decode stage
//
////////////////////////////////////////////////
//
//
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(clear_pipeline))
&&(!$past(w_clear_icache))
&&($past(dcd_valid))&&($past(dcd_stalled))
&&(!clear_pipeline))
begin
`ASSERT((!OPT_PIPELINED)||(dcd_valid));
`ASSERT($stable(f_dcd_data));
`ASSERT($stable(f_dcd_insn_word));
end
 
always @(*)
if ((dcd_valid)&&(!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))
`ASSERT(((!dcd_rA)&&(dcd_wR))
||((!dcd_rA)&&(!dcd_rB)&&(!dcd_wR)));
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)&&(mem_busy)&&(!mem_rdbusy))
`ASSERT(op_opn[0] == 1'b1);
 
always @(*)
if ((dcd_valid)&&(!dcd_M))
`ASSERT((dcd_illegal)||(!dcd_pipe));
 
wire [31:0] f_dcd_mem_addr, f_pipe_addr_diff;
assign f_dcd_mem_addr = w_op_BnI+dcd_I;
assign f_pipe_addr_diff = f_dcd_mem_addr - op_Bv;
 
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)||(mem_busy))&&(dcd_rB)
&&(!op_illegal)&&(!dcd_illegal)
&&(!dbg_clear_pipe)&&(!clear_pipeline))
`ASSERT(f_pipe_addr_diff[AW+1:0] <= 4);
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]);
 
 
// EARLY_BRANCHING
always @(*)
if (!EARLY_BRANCHING)
`ASSERT((!dcd_early_branch)
&&(!dcd_early_branch_stb)
&&(!dcd_ljmp));
 
// IMPLEMENT_DIVIDE
always @(*)
if ((dcd_DIV)&&(dcd_valid)&&(!dcd_illegal))
`ASSERT(dcd_wR);
 
////////////////////////////////////////////////
//
// Assertions about the op stage
//
////////////////////////////////////////////////
//
//
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;
 
f_idecode #(.ADDRESS_WIDTH(AW),
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0),
.OPT_EARLY_BRANCHING(EARLY_BRANCHING),
.OPT_DIVIDE(IMPLEMENT_DIVIDE),
.OPT_FPU(IMPLEMENT_FPU),
.OPT_LOCK(OPT_LOCK),
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS),
.OPT_SIM(1'b0),
.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)
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 (!EARLY_BRANCHING)
assert(!f_op_branch);
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);
`ASSERT(fc_op_break == op_break);
`ASSERT((!wr_reg_ce)||(wr_reg_id != fc_op_Bid)
||(!op_rB)||(fc_op_I == 0));
`ifdef VERILATOR
`ASSERT(fc_op_sim == op_sim);
`ASSERT(fc_op_sim_immv == op_sim_immv);
`endif
 
if ((fc_op_wR)&&(fc_op_Rid[4:0] == { op_gie,
4'he }))
`ASSERT(!pending_sreg_write);
 
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}))
`ASSERT(!op_phase);
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(fc_op_Rid[4:0] == { op_gie, `CPU_PC_REG });
// `ASSERT(fc_op_wR);
// `ASSERT(!fc_op_wF);
`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((!mem_rdbusy)||(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 (!EARLY_BRANCHING)
assert(!f_op_early_branch);
 
 
always @(*)
if (op_ce)
`ASSERT((dcd_valid)||(dcd_illegal)||(dcd_early_branch));
 
 
 
 
 
 
 
always @(*)
if ((op_valid)&&(op_rB)&&(!f_op_zI)&&(wr_reg_ce))
`ASSERT((wr_reg_id != op_Bid)||(dbg_clear_pipe)
||(wr_reg_id[4:0] == { gie, `CPU_PC_REG}));
 
always @(*)
if ((f_past_valid)&&(!f_op_zI)&&(mem_rdbusy)&&(op_valid)&&(op_rB))
`ASSERT((!OPT_DCACHE)||(OPT_MEMPIPE)
||(mem_wreg != op_Bid));
 
always @(posedge i_clk)
if ((op_valid)&&(op_rB)&&(!f_op_zI)&&((mem_rdbusy)||(mem_valid))
&&(mem_wreg != {gie, `CPU_PC_REG}))
begin
if (!OPT_MEMPIPE)
begin
`ASSERT(fc_alu_Aid[4:0] == mem_wreg);
`ASSERT(mem_wreg != op_Bid);
end else if (OPT_DCACHE)
begin
`ASSERT(fc_alu_Aid[4:0] != op_Bid);
// It takes two clocks for the DCACHE to announce
// the value it intends to write to via mem_wreg.
// At that point, we can make this assertion. So,
// if the memory is busy reading a value
if ((!$past(mem_rdbusy))
// and we didn't request the read on the last
// clock,
&&($past(mem_ce)))
begin
// Then the memory should match our last read
// request. There may be several reads
// stuffed within the device, so the
// fc_alu_Bid might not match the mem_wreg,
// but the rest should be good
//
// What we really want to say, isn't valid yet
// `ASSERT(mem_wreg != op_Bid);
end else if (($past(mem_rdbusy))&&(!$past(mem_ce))
&&(!$past(mem_ce,2)))
`ASSERT(mem_wreg != op_Bid);
end else // if (!OPT_DCACHE)
begin
if ((mem_valid)
||($past(mem_rdbusy)))
`ASSERT(mem_wreg != op_Bid);
end
end
 
// always @(posedge i_clk)
// if (($fell(mem_rdbusy))&&(mem_valid))
// begin
// `ASSERT(mem_wreg == fc_alu_Aid[4:0]);
// end
 
always @(posedge i_clk)
if (mem_rdbusy)
begin
`ASSERT(fc_alu_M);
`ASSERT((!OPT_PIPELINED)||(fc_alu_wR));
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(!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));
 
 
//
//
// Op: Memory pipeline assertions
//
//
 
wire [AW-1:0] f_next_mem, f_op_mem_addr;
assign f_next_mem = mem_addr + 1'b1;
assign f_op_mem_addr = op_Bv[AW+1:2];
 
always @(*)
if ((op_valid)&&(!fc_alu_prepipe))
begin
`ASSERT((!op_valid_mem)||(!op_pipe));
end
 
always @(*)
if ((op_valid_mem)&&(op_pipe))
begin
if (mem_rdbusy)
`ASSERT(op_opn[0] == 1'b0);
if ((mem_busy)&&(!mem_rdbusy))
`ASSERT(op_opn[0] == 1'b1);
if (mem_rdbusy)
begin
if (OPT_PIPELINED_BUS_ACCESS)
begin end
else if (OPT_LGDCACHE != 0)
`ASSERT(mem_wreg != op_Bid);
end
end
 
/*
always @(*)
if ((dcd_valid)&&(dcd_pipe))
begin
if ((op_valid_mem)&&(f_op_zI))
`ASSERT(dcd_I <= 4);
if (f_op_zI)
`ASSERT(dcd_I <= 4);
// if ((!op_valid)&&(mem_rdbusy))
// `ASSERT(mem_wreg != dcd_B);
end
*/
 
always @(posedge i_clk)
if ((op_valid_mem)&&(op_pipe))
begin
if ((mem_busy)&&(OPT_LGDCACHE == 0))
`ASSERT((f_op_mem_addr == mem_addr)
||(f_op_mem_addr == f_next_mem));
if (mem_valid)
`ASSERT(op_Bid != mem_wreg);
 
if (alu_busy||alu_valid)
`ASSERT((!alu_wR)||(op_Bid != alu_reg));
 
if (f_past_valid)
begin
if ((mem_busy)&&(OPT_LGDCACHE==0))
`ASSERT((op_Bv[(AW+1):2]==mem_addr[(AW-1):0])
||(op_Bv[(AW+1):2]==mem_addr[(AW-1):0]+1'b1));
 
if ($past(mem_ce))
`ASSERT(op_Bid == $past(op_Bid));
 
`ASSERT((op_Bid[3:1] != 3'h7));
end
 
if ((mem_rdbusy)||(mem_valid))
begin
if (!OPT_MEMPIPE)
begin
`ASSERT(fc_alu_Aid[4:0] == mem_wreg);
`ASSERT(mem_wreg != op_Bid);
end else if (OPT_DCACHE)
begin
`ASSERT(fc_alu_Aid[4:0] != op_Bid);
// It takes two clocks for the DCACHE to announce
// the value it intends to write to via mem_wreg.
// At that point, we can make this assertion. So,
// if the memory is busy reading a value
if ((!$past(mem_rdbusy))
// and we didn't request the read on the last
// clock,
&&($past(mem_ce)))
begin
// Then the memory should match our last read
// request. There may be several reads
// stuffed within the device, so the
// fc_alu_Bid might not match the mem_wreg,
// but the rest should be good
//
// What we really want to say, isn't valid yet
// `ASSERT(mem_wreg != op_Bid);
end else if (($past(mem_rdbusy))&&(!$past(mem_ce))
&&(!$past(mem_ce,2)))
`ASSERT(mem_wreg != op_Bid);
end else // if (!OPT_DCACHE)
begin
if ((mem_valid)
||($past(mem_rdbusy)))
`ASSERT(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));
 
 
////////////////////////////////////////////////
//
// 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)&&((mem_valid)||(div_valid)||(div_busy)
||(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((!bus_lock)&&(!prelock_stall));
always @(*)
if (!IMPLEMENT_DIVIDE)
`ASSERT((!dcd_DIV)&&(!op_valid_div)&&(!div_busy)&&(!div_valid)&&(!div_ce));
always @(*)
if (IMPLEMENT_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(!mem_rdbusy);
`ASSERT(!div_busy);
`ASSERT(!fpu_busy);
end else if (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);
 
 
 
 
wire [31:0] f_Bv;
reg [31:0] f_Av, f_pre_Bv;
 
//
//
// The 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], 1'b0, 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], 1'b1, 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
 
//
//
// The 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], 1'b0,
w_uflags };
if ((fc_op_Bid[4] == op_gie)&&(!fc_op_Bid[4]))
f_pre_Bv[14:0] = (op_gie) ? 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 ((op_valid)&&(!op_illegal)&&(!clear_pipeline))
begin
if (((!wr_reg_ce)||(wr_reg_id!= { gie, `CPU_PC_REG }))
&&(!dbg_clear_pipe)&&(!clear_pipeline)&&(!f_op_branch))
begin
if ((fc_op_rA)&&(fc_op_Aid[3:1] != 3'h7))
`ASSERT(f_Av == op_Av);
`ASSERT(f_Bv == op_Bv);
end
end
 
////////////////////////////////////////////////////////////////
//
//
// Pipeline signaling check
//
//
////////////////////////////////////////////////////////////////
//
//
 
//
// Assertions about the prefetch
// Assertions about the decode stage
// dcd_ce, dcd_valid
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
};
 
////////////////////////////////////////////////
//
// Assertions about the prefetch (output) stage
//
////////////////////////////////////////////////
 
////////////////////////////////////////////////
//
// Assertions about the op stage
//
////////////////////////////////////////////////
// op_valid
// op_ce
// op_stall
wire [4+AW+2+7+4-1:0] f_op_data;
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
 
/////////
//
// CIS instructions, enabled by OPT_CIS
//
/////////
 
////////////////////////////////////////////////
//
// Assertions about the ALU stage
//
////////////////////////////////////////////////
//
//
// alu_valid
// alu_ce
// alu_stall
// ALU stage assertions
 
reg f_alu_branch;
 
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;
 
 
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;
 
f_idecode #(.ADDRESS_WIDTH(AW),
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0),
.OPT_EARLY_BRANCHING(EARLY_BRANCHING),
.OPT_DIVIDE(IMPLEMENT_DIVIDE),
.OPT_FPU(IMPLEMENT_FPU),
.OPT_LOCK(OPT_LOCK),
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS),
.OPT_SIM(1'b0),
.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(!mem_rdbusy);
`ASSERT(!alu_busy);
end else begin
if (!fc_alu_DV)
`ASSERT((!div_valid)&&(!div_busy)&&(!div_error));
if (!fc_alu_M)
`ASSERT(!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(!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 (mem_rdbusy)
begin
if ($past(mem_rdbusy))
`ASSERT(fc_alu_Rid[4] == mem_wreg[4]);
//
end
 
//if ((div_busy)||(fpu_busy))
// `ASSERT(alu_wR);
//else
if ((alu_busy)&&(alu_wR))
`ASSERT(fc_alu_wR);
 
if (alu_busy || mem_rdbusy || div_busy)
begin
if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, `CPU_PC_REG}))
`ASSERT(!alu_phase);
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 || !mem_valid);
`ASSERT(fc_alu_FP || (!fpu_valid)&&(!fpu_error));
`ASSERT((!alu_busy)&&(!div_busy)&&(!fpu_busy));
 
if ((!OPT_PIPELINED_BUS_ACCESS)||((!mem_valid)&&(!mem_rdbusy)))
`ASSERT(fc_alu_Rid[4:0] == wr_reg_id);
if ((!alu_illegal)&&(fc_alu_cond[3])&&(fc_alu_wR)&&(fc_alu_ALU))
`ASSERT(alu_wR);
if (!mem_valid)
`ASSERT(fc_alu_Rid[4:0] == alu_reg);
`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(pending_sreg_write
// == ((OPT_PIPELINED)&&(wr_reg_id[3:1]==3'h7)));
 
`ASSERT(!f_alu_branch);
end
 
always @(posedge i_clk)
if (f_mem_pc)
begin
if ((!OPT_DCACHE)||(!OPT_MEMPIPE))
`ASSERT(!fc_alu_prepipe);
else if ((mem_rdbusy)&&(!$past(mem_ce))&&(!$past(mem_ce,2)))
`ASSERT(!fc_alu_prepipe);
end
 
always @(posedge i_clk)
if (mem_rdbusy)
begin
// In pipelined mode, this is an ongoing load operation
// Otherwise, mem_rdbusy == mem_busy and we have no idea
// what type of operation we are in
`ASSERT(!fc_alu_illegal);
`ASSERT(fc_alu_M);
if (OPT_PIPELINED)
begin
`ASSERT(fc_alu_wR);
end if (!OPT_PIPELINED_BUS_ACCESS)
`ASSERT(fc_alu_Rid[4:0] == mem_wreg);
 
if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, `CPU_PC_REG}))
`ASSERT(!alu_phase);
else
`ASSERT(f_alu_phase == alu_phase);
end else if ((mem_busy)&&(fc_alu_M))
begin
// Ongoing store operation
`ASSERT(!fc_alu_illegal);
`ASSERT(fc_alu_M);
`ASSERT(!fc_alu_wR);
end
 
// always @(posedge i_clk)
// if ((OPT_PIPELINED)&&(cc_invalid_for_dvd))
// begin
// assert((op_valid &&(fc_op_wF
// ||(fc_op_wR &&(fc_op_Aid[3:0]==`CPU_CC_REG))))
// ||fc_alu_wF
// ||((fc_alu_wR &&(fc_alu_Aid[3:0] == `CPU_CC_REG))));
// end
 
 
////////////////////////////////////////////////
//
// Assertions about the writeback stage
//
////////////////////////////////////////////////
//
//
initial assert((!OPT_LOCK)||(OPT_PIPELINED));
 
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 ((f_past_valid)&&(r_halted))
begin
`ASSERT(!div_busy);
`ASSERT(!mem_busy);
`ASSERT(!alu_busy);
`ASSERT(!div_valid);
`ASSERT(!mem_valid);
`ASSERT(!alu_valid);
end
 
always @(*)
if (((wr_reg_ce)||(wr_flags_ce))&&(!dbgv))
`ASSERT(!alu_illegal);
 
always @(*)
if (wr_reg_ce)
begin
`ASSERT(fc_alu_wR);
 
// Since writes are asynchronous, they can create errors later
`ASSERT((!bus_err)||(!mem_valid));
`ASSERT(!fpu_error);
`ASSERT(!div_error);
end
 
 
//////////////////////////////////////////////
//
//
// Tying together the WB requests and acks
//
//
//////////////////////////////////////////////
//
//
always @(*)
begin
if (mem_cyc_gbl)
begin
`ASSERT(f_gbl_mem_nreqs == f_mem_nreqs);
`ASSERT(f_gbl_mem_nacks == f_mem_nacks);
end
if (mem_cyc_lcl)
begin
`ASSERT(f_lcl_mem_nreqs == f_mem_nreqs);
`ASSERT(f_lcl_mem_nacks == f_mem_nacks);
end
 
`ASSERT(f_gbl_pf_nreqs == f_pf_nreqs);
`ASSERT(f_gbl_pf_nacks == f_pf_nreqs);
`ASSERT(f_gbl_pf_outstanding == f_pf_outstanding);
 
`ASSERT(f_lcl_pf_nreqs == 0);
`ASSERT(f_lcl_pf_nacks == 0);
end
 
//////////////////////////////////////////////
//
//
// Ad-hoc (unsorted) properties
//
//
//////////////////////////////////////////////
//
//
//
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(mem_rdbusy))
&&(!$past(mem_valid)||($past(mem_wreg[3:1] != 3'h7))))
`ASSERT(mem_wreg[4] == alu_gie);
always @(posedge i_clk)
if (mem_valid)
`ASSERT(mem_wreg[4] == alu_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(!mem_rdbusy);
// The following two shouldn't be true, but will be true
// following a bus error
if (!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(mem_rdbusy))))
`ASSERT($stable(alu_gie));
 
always @(posedge i_clk)
if (mem_rdbusy)
`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)&&(alu_gie)&&(wr_reg_ce)
// &&((!$past(r_halted))||(!$past(i_dbg_we))))
// `ASSERT(wr_reg_id[4]);
// else if ((alu_gie)&&((alu_busy)||(div_busy)))
// `ASSERT((!alu_wR)||(alu_reg[4]));
 
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)&&(mem_valid)&&(mem_wreg[3:1] == 3'h7)
&&(mem_wreg[4:0] != { gie, `CPU_PC_REG }))
`ASSERT(pending_sreg_write); // !
else if ((OPT_PIPELINED)&&(OPT_DCACHE)&&(mem_rdbusy)
&&($past(mem_rdbusy))
&&($past(mem_rdbusy,2)))
`ASSERT((mem_wreg[3:1] != 3'h7)
||(mem_wreg == { gie, `CPU_PC_REG})
||(pending_sreg_write));
// &&(mem_wreg[4:0] != { gie, `CPU_PC_REG })&&(mem_rdbusy))
// `ASSERT(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(!mem_busy);
`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)
`ASSERT((mem_ce)||(!set_cond));
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
 
//////////////////////////////////////////////
//
//
// Cover statements
//
//
//////////////////////////////////////////////
always @(posedge i_clk)
if ((gie)&&(wr_reg_ce))
begin
// Cover the switch to interrupt
cover((i_interrupt)&&(!alu_phase)&&(!bus_lock));
 
// Cover a "step" instruction
cover(((alu_pc_valid)||(mem_pc_valid))
&&(step)&&(!alu_phase)&&(!bus_lock));
 
// Cover a break instruction
cover((master_ce)&&(break_pending)&&(!break_en));
 
// Cover an illegal instruction
cover((alu_illegal)&&(!clear_pipeline));
 
// Cover a division by zero
cover(div_error);
 
// Cover a bus error
cover(bus_err);
 
// Cover a TRAP instruction to the CC register
cover(((wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT])
&&(wr_reg_id[4])&&(wr_write_cc)));
end
 
//////////////////////////////////////////////
//////////////////////////////////////////////
//
// always @(*)
// if (break_pending)
// `ASSERT((op_valid)&&(op_break));
//////////////////////////////////////////////
//
//
// Problem limiting assumptions
//
//
//////////////////////////////////////////////
//
// Careless assumptions might be located here
 
always @(*)
assume(fc_op_Aid[3:0] != `CPU_CC_REG);
always @(*)
assume(fc_op_Bid[3:0] != `CPU_CC_REG);
 
always @(*)
assume(!i_halt);
 
`endif // FORMAL
//}}}
 
endmodule
/dcache.v
40,15 → 40,15
// virtual page size--lest in the middle of reading a page a TLB miss
// take place referencing only a part of the cacheable page.
//
//
//
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-2019, Gisselquist Technology, LLC
// Copyright (C) 2016, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
67,146 → 67,88
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
`ifdef FORMAL
`define ASSERT assert
 
`ifdef DCACHE
`define ASSUME assume
`else
`define ASSUME assert
`endif
`endif
 
module dcache(i_clk, i_reset, i_pipe_stb, i_lock,
module dcache(i_clk, i_rst, i_pipe_stb, i_lock,
i_op, i_addr, i_data, i_oreg,
o_busy, o_pipe_stalled, o_valid, o_err, o_wreg,o_data,
o_wb_cyc_gbl, o_wb_cyc_lcl, o_wb_stb_gbl, o_wb_stb_lcl,
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data
`ifdef FORMAL
, f_nreqs, f_nacks, f_outstanding, f_pc
`endif
);
o_wb_we, o_wb_addr, o_wb_data,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data);
parameter LGCACHELEN = 8,
ADDRESS_WIDTH=30,
LGNLINES=(LGCACHELEN-3), // Log of the number of separate cache lines
ADDRESS_WIDTH=32,
LGNLINES=5, // Log of the number of separate cache lines
IMPLEMENT_LOCK=0,
NAUX=5; // # of aux d-wires to keep aligned w/memops
parameter [0:0] OPT_LOCAL_BUS=1'b1;
parameter [0:0] OPT_PIPE=1'b1;
parameter [0:0] OPT_LOCK=1'b1;
parameter [0:0] OPT_DUAL_READ_PORT=1'b1;
parameter OPT_FIFO_DEPTH = 4;
localparam SDRAM_BIT = 26;
localparam FLASH_BIT = 22;
localparam BLKRAM_BIT= 15;
localparam AW = ADDRESS_WIDTH; // Just for ease of notation below
localparam CS = LGCACHELEN; // Number of bits in a cache address
localparam LS = CS-LGNLINES; // Bits to spec position w/in cline
parameter F_LGDEPTH=1 + (((!OPT_PIPE)||(LS > OPT_FIFO_DEPTH))
? LS : OPT_FIFO_DEPTH);
localparam LGAUX = 3; // log_2 of the maximum number of piped data
localparam DW = 32; // Bus data width
localparam DP = OPT_FIFO_DEPTH;
//
localparam [1:0] DC_IDLE = 2'b00; // Bus is idle
localparam [1:0] DC_WRITE = 2'b01; // Write
localparam [1:0] DC_READS = 2'b10; // Read a single value(!cachd)
localparam [1:0] DC_READC = 2'b11; // Read a whole cache line
//
input wire i_clk, i_reset;
localparam LGAUX = 3; // log_2 of the maximum number of piped data
input i_clk, i_rst;
// Interface from the CPU
input wire i_pipe_stb, i_lock;
input wire [2:0] i_op;
input wire [(DW-1):0] i_addr;
input wire [(DW-1):0] i_data;
input wire [(NAUX-1):0] i_oreg; // Aux data, such as reg to write to
input i_pipe_stb, i_lock;
input i_op;
input [31:0] i_addr;
input [31:0] i_data;
input [(NAUX-1):0] i_oreg; // Aux data, such as reg to write to
// Outputs, going back to the CPU
output reg o_busy;
output reg o_pipe_stalled;
output reg o_valid, o_err;
output wire o_busy, o_pipe_stalled, o_valid, o_err;
output reg [(NAUX-1):0] o_wreg;
output reg [(DW-1):0] o_data;
output reg [31:0] o_data;
// Wishbone bus master outputs
output wire o_wb_cyc_gbl, o_wb_cyc_lcl;
output reg o_wb_stb_gbl, o_wb_stb_lcl;
output reg o_wb_we;
output reg [(AW-1):0] o_wb_addr;
output reg [(DW-1):0] o_wb_data;
output wire [(DW/8-1):0] o_wb_sel;
output reg [(AW-1):0] o_wb_addr;
output reg [31:0] o_wb_data;
// Wishbone bus slave response inputs
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(DW-1):0] i_wb_data;
`ifdef FORMAL
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
output wire f_pc;
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
 
reg f_past_valid;
`endif
//
// output reg [31:0] o_debug;
 
 
reg cyc, stb, last_ack, end_of_line, last_line_stb;
reg r_wb_cyc_gbl, r_wb_cyc_lcl;
// npending is the number of pending non-cached operations, counted
// from the i_pipe_stb to the o_wb_ack
reg [DP:0] npending;
 
 
reg [((1<<LGNLINES)-1):0] c_v; // One bit per cache line, is it valid?
reg [(AW-LS-1):0] c_vtags [0:((1<<LGNLINES)-1)];
reg [(DW-1):0] c_mem [0:((1<<CS)-1)];
reg set_vflag;
reg [1:0] state;
reg [(CS-1):0] wr_addr;
reg [(DW-1):0] cached_idata, cached_rdata;
reg [DW-1:0] pre_data;
reg lock_gbl, lock_lcl;
reg [31:0] c_mem [0:((1<<CS)-1)];
// reg [((1<<LGNLINES)-1):0] c_wr; // Is the cache line writable?
// reg c_wdata;
// reg c_waddr;
 
 
// To simplify writing to the cache, and the job of the synthesizer to
// recognize that a cache write needs to take place, we'll take an extra
// clock to get there, and use these c_w... registers to capture the
// data in the meantime.
reg c_wr;
reg [(DW-1):0] c_wdata;
reg [(DW/8-1):0] c_wsel;
reg [31:0] c_wdata;
reg [(CS-1):0] c_waddr;
 
reg [(AW-LS-1):0] last_tag;
reg last_tag_valid;
 
 
wire [(LGNLINES-1):0] i_cline;
wire [(CS-1):0] i_caddr;
wire [(AW-LS-1):0] i_ctag;
 
`ifdef FORMAL
reg [F_LGDEPTH-1:0] f_fill;
reg [AW:0] f_return_address;
reg [AW:0] f_pending_addr;
`endif
assign i_cline = i_addr[(CS-1):LS];
assign i_caddr = i_addr[(CS-1):0];
assign i_ctag = i_addr[(AW-1):LS];
 
assign i_cline = i_addr[(CS+1):LS+2];
assign i_caddr = i_addr[(CS+1):2];
 
wire cache_miss_inow, w_cachable;
assign cache_miss_inow = (!last_tag_valid)
||(last_tag != i_addr[(AW+1):LS+2])
||(!c_v[i_cline]);
assign cache_miss_inow = (last_tag != i_addr[31:LS])||(!c_v[i_cline]);
assign w_cachable = (i_addr[31:30]!=2'b11)&&(!i_lock)&&(
((SDRAM_BIT>0)&&(i_addr[SDRAM_BIT]))
||((FLASH_BIT>0)&&(i_addr[FLASH_BIT]))
||((BLKRAM_BIT>0)&&(i_addr[BLKRAM_BIT])));
 
wire raw_cachable_address;
 
iscachable chkaddress(i_addr[AW+1:2], raw_cachable_address);
 
assign w_cachable = ((!OPT_LOCAL_BUS)||(i_addr[(DW-1):(DW-8)]!=8'hff))
&&((!i_lock)||(!OPT_LOCK))&&(raw_cachable_address);
 
reg r_cachable, r_svalid, r_dvalid, r_rd, r_cache_miss,
r_rd_pending;
reg [(AW-1):0] r_addr;
reg r_cachable, r_svalid, r_dvalid, r_rd, r_cache_miss, r_rvalid;
reg [(AW-1):0] r_addr;
reg [31:0] r_idata, r_ddata, r_rdata;
wire [(LGNLINES-1):0] r_cline;
wire [(CS-1):0] r_caddr;
wire [(AW-LS-1):0] r_ctag;
wire [(CS-1):0] r_caddr;
wire [(AW-LS-1):0] r_ctag;
 
assign r_cline = r_addr[(CS-1):LS];
assign r_caddr = r_addr[(CS-1):0];
213,14 → 155,9
assign r_ctag = r_addr[(AW-1):LS];
 
 
reg wr_cstb, r_iv, in_cache;
reg wr_cstb, r_iv, pipeable_op, non_pipeable_op, in_cache;
reg [(AW-LS-1):0] r_itag;
reg [DW/8-1:0] r_sel;
reg [(NAUX+4-1):0] req_data;
reg gie;
 
 
 
//
// The one-clock delayed read values from the cache.
//
228,28 → 165,15
initial r_cachable = 1'b0;
initial r_svalid = 1'b0;
initial r_dvalid = 1'b0;
initial r_cache_miss = 1'b0;
initial r_addr = 0;
initial last_tag_valid = 0;
initial r_rd_pending = 0;
always @(posedge i_clk)
if (i_reset)
begin
r_rd <= 1'b0;
r_cachable <= 1'b0;
r_svalid <= 1'b0;
r_dvalid <= 1'b0;
r_cache_miss <= 1'b0;
r_addr <= 0;
r_rd_pending <= 0;
last_tag_valid <= 0;
end else begin
// The single clock path
r_idata <= c_mem[i_addr[(CS-1):0]];
// The valid for the single clock path
// Only ... we need to wait if we are currently writing
// to our cache.
r_svalid<= (i_pipe_stb)&&(!i_op[0])&&(w_cachable)
&&(!cache_miss_inow)&&(!c_wr)&&(!wr_cstb);
r_svalid<= (!i_op)&&(!cache_miss_inow)&&(w_cachable)
&&(i_pipe_stb)&&(!c_wr)&&(!wr_cstb);
 
//
// The two clock in-cache path
256,37 → 180,25
//
// Some preliminaries that needed to be calculated on the first
// clock
if ((!o_pipe_stalled)&&(!r_rd_pending))
r_addr <= i_addr[(AW+1):2];
if ((!o_pipe_stalled)&&(!r_rd_pending))
if (!o_busy)
begin
r_iv <= c_v[i_cline];
r_itag <= c_vtags[i_cline];
r_cachable <= (!i_op[0])&&(w_cachable)&&(i_pipe_stb);
r_rd_pending <= (i_pipe_stb)&&(!i_op[0])&&(w_cachable)
&&((cache_miss_inow)||(c_wr)||(wr_cstb));
// &&((!c_wr)||(!wr_cstb));
r_addr <= i_addr;
r_cachable <= (!i_op)&&(w_cachable)&&(i_pipe_stb);
end else begin
r_iv <= c_v[r_cline];
r_itag <= c_vtags[r_cline];
r_rd_pending <= (r_rd_pending)
&&((!cyc)||(!i_wb_err))
&&((r_itag != r_ctag)||(!r_iv));
end
r_rd <= (i_pipe_stb)&&(!i_op[0]);
// r_idata still contains the right answer
r_rd <= (i_pipe_stb)&&(!i_op);
r_ddata <= r_idata;
// r_itag contains the tag we didn't have available to us on the
// last clock, r_ctag is a bit select from r_addr containing a
// one clock delayed address.
r_dvalid <= (!r_svalid)&&(!r_dvalid)&&(r_itag == r_ctag)&&(r_iv)
&&(r_cachable)&&(r_rd_pending);
if ((r_itag == r_ctag)&&(r_iv)&&(r_cachable)&&(r_rd_pending))
begin
last_tag_valid <= 1'b1;
r_dvalid <= (r_itag == r_ctag)&&(r_iv)&&(r_cachable);
if ((r_itag == r_ctag)&&(r_iv)&&(r_cachable))
last_tag <= r_ctag;
end else if ((state == DC_READC)
&&(last_tag[CS-LS-1:0]==o_wb_addr[CS-1:LS])
&&((i_wb_ack)||(i_wb_err)))
last_tag_valid <= 1'b0;
 
// r_cache miss takes a clock cycle. It is only ever true for
// something that should be cachable, but isn't in the cache.
302,453 → 214,63
// Two clock path -- misses as well
&&(r_rd)&&(!r_svalid)
&&((r_itag != r_ctag)||(!r_iv));
end
 
initial r_sel = 4'hf;
always @(posedge i_clk)
if (i_reset)
r_sel <= 4'hf;
else if (!o_pipe_stalled)
begin
casez({i_op[2:1], i_addr[1:0]})
4'b0???: r_sel <= 4'b1111;
4'b100?: r_sel <= 4'b1100;
4'b101?: r_sel <= 4'b0011;
4'b1100: r_sel <= 4'b1000;
4'b1101: r_sel <= 4'b0100;
4'b1110: r_sel <= 4'b0010;
4'b1111: r_sel <= 4'b0001;
endcase
r_rdata <= c_mem[r_addr[(CS-1):0]];
r_rvalid<= ((i_wb_ack)&&(last_ack));
end
 
assign o_wb_sel = (state == DC_READC) ? 4'hf : r_sel;
`define DC_IDLE 2'b00
`define DC_WRITE 2'b01
`define DC_READS 2'b10
`define DC_READC 2'b11
reg [1:0] state;
 
initial o_wb_data = 0;
reg [(AW-LS-1):0] wr_wtag, wr_vtag;
reg [31:0] wr_data;
reg [(CS-1):0] wr_addr;
always @(posedge i_clk)
if (i_reset)
o_wb_data <= 0;
else if ((!o_busy)||((stb)&&(!i_wb_stall)))
begin
casez(i_op[2:1])
2'b0?: o_wb_data <= i_data;
2'b10: o_wb_data <= { (2){i_data[15:0]} };
2'b11: o_wb_data <= { (4){i_data[ 7:0]} };
endcase
end
 
generate if (OPT_PIPE)
begin : OPT_PIPE_FIFO
reg [NAUX+4-2:0] fifo_data [0:((1<<OPT_FIFO_DEPTH)-1)];
 
reg [DP:0] wraddr, rdaddr;
 
always @(posedge i_clk)
if (i_pipe_stb)
fifo_data[wraddr[DP-1:0]]
<= { i_oreg[NAUX-2:0], i_op[2:1], i_addr[1:0] };
 
always @(posedge i_clk)
if (i_pipe_stb)
gie <= i_oreg[NAUX-1];
 
`ifdef NO_BKRAM
reg [NAUX+4-2:0] r_req_data, r_last_data;
reg single_write;
 
always @(posedge i_clk)
r_req_data <= fifo_data[rdaddr[DP-1:0]];
 
always @(posedge i_clk)
single_write <= (rdaddr == wraddr)&&(i_pipe_stb);
 
always @(posedge i_clk)
if (i_pipe_stb)
r_last_data <= { i_oreg[NAUX-2:0],
i_op[2:1], i_addr[1:0] };
 
always @(*)
begin
req_data[NAUX+4-1] = gie;
// if ((r_svalid)||(state == DC_READ))
if (single_write)
req_data[NAUX+4-2:0] = r_last_data;
else
req_data[NAUX+4-2:0] = r_req_data;
end
 
always @(*)
`ASSERT(req_data == fifo_data[rdaddr[DP-1:0]]);
`else
always @(*)
req_data[NAUX+4-2:0] = fifo_data[rdaddr[DP-1:0]];
always @(*)
req_data[NAUX+4-1] = gie;
`endif
 
initial wraddr = 0;
always @(posedge i_clk)
if ((i_reset)||((cyc)&&(i_wb_err)))
wraddr <= 0;
else if (i_pipe_stb)
wraddr <= wraddr + 1'b1;
 
initial rdaddr = 0;
always @(posedge i_clk)
if ((i_reset)||((cyc)&&(i_wb_err)))
rdaddr <= 0;
else if ((r_dvalid)||(r_svalid))
rdaddr <= rdaddr + 1'b1;
else if ((state == DC_WRITE)&&(i_wb_ack))
rdaddr <= rdaddr + 1'b1;
else if ((state == DC_READS)&&(i_wb_ack))
rdaddr <= rdaddr + 1'b1;
 
`ifdef FORMAL
reg [AW-1:0] f_fifo_addr [0:((1<<OPT_FIFO_DEPTH)-1)];
reg [F_LGDEPTH-1:0] f_last_wraddr;
reg f_pc_pending;
 
always @(*)
begin
f_fill = 0;
f_fill[DP:0] = wraddr - rdaddr;
end
 
always @(*)
`ASSERT(f_fill <= { 1'b1, {(DP){1'b0}} });
 
always @(*)
if ((r_dvalid)||(r_svalid))
begin
if (r_svalid)
`ASSERT(f_fill == 1);
else if (r_dvalid)
`ASSERT(f_fill == 1);
else
`ASSERT(f_fill == 0);
end else if (r_rd_pending)
`ASSERT(f_fill == 1);
else
`ASSERT(f_fill == npending);
 
 
initial f_pc_pending = 0;
always @(posedge i_clk)
if (i_reset)
f_pc_pending <= 1'b0;
else if (i_pipe_stb)
f_pc_pending <= (!i_op[0])&&(i_oreg[3:1] == 3'h7);
else if (f_fill == 0)
f_pc_pending <= 1'b0;
//else if ((o_valid)&&(o_wreg[3:1] == 3'h7)&&(f_fill == 0))
// f_pc_pending <= 1'b0;
 
always @(posedge i_clk)
if (f_pc_pending)
`ASSUME(!i_pipe_stb);
 
always @(posedge i_clk)
if (state == DC_WRITE)
`ASSERT(!f_pc_pending);
 
always @(*)
begin
f_last_wraddr = 0;
f_last_wraddr[DP:0] = wraddr - 1'b1;
end
 
always @(posedge i_clk)
if (r_rd_pending)
`ASSERT(f_pc_pending == (fifo_data[f_last_wraddr][7:5] == 3'h7));
 
`define INSPECT_FIFO
reg [((1<<(DP+1))-1):0] f_valid_fifo_entry;
 
genvar k;
for(k=0; k<(1<<(DP+1)); k=k+1)
begin
 
always @(*)
begin
f_valid_fifo_entry[k] = 1'b0;
/*
if ((rdaddr[DP] != wraddr[DP])
&&(rdaddr[DP-1:0] == wraddr[DP-1:0]))
f_valid_fifo_entry[k] = 1'b1;
else */
if ((rdaddr < wraddr)&&(k < wraddr)
&&(k >= rdaddr))
f_valid_fifo_entry[k] = 1'b1;
else if ((rdaddr > wraddr)&&(k >= rdaddr))
f_valid_fifo_entry[k] = 1'b1;
else if ((rdaddr > wraddr)&&(k < wraddr))
f_valid_fifo_entry[k] = 1'b1;
end
 
`ifdef INSPECT_FIFO
wire [NAUX+4-2:0] fifo_data_k;
 
assign fifo_data_k = fifo_data[k[DP-1:0]];
always @(*)
if (f_valid_fifo_entry[k])
begin
if (!f_pc_pending)
`ASSERT((o_wb_we)||(fifo_data_k[7:5] != 3'h7));
else if (k != f_last_wraddr)
`ASSERT(fifo_data_k[7:5] != 3'h7);
end
`endif // INSPECT_FIFO
 
end
 
`ifndef INSPECT_FIFO
always @(posedge i_clk)
if ((r_rd_pending)&&(rdaddr[DP:0] != f_last_wraddr[DP-1]))
assume(fifo_data[rdaddr][7:5] != 3'h7);
`endif // INSPECT_FIFO
 
assign f_pc = f_pc_pending;
 
//
//
//
always @(*)
f_pending_addr = f_fifo_addr[rdaddr];
 
//
//
//
always @(posedge i_clk)
if (i_pipe_stb)
f_fifo_addr[wraddr[DP-1:0]] <= i_addr[AW+1:2];
 
always @(*)
begin
f_return_address[AW] = (o_wb_cyc_lcl);
f_return_address[AW-1:0] = f_fifo_addr[rdaddr];
if (state == DC_READC)
f_return_address[LS-1:0]
= (o_wb_addr[LS-1:0] - f_outstanding[LS-1:0]);
end
 
`define TWIN_WRITE_TEST
`ifdef TWIN_WRITE_TEST
(* anyconst *) reg [DP:0] f_twin_base;
reg [DP:0] f_twin_next;
(* anyconst *) reg [AW+NAUX+4-2-1:0] f_twin_first,
f_twin_second;
// reg [AW-1:0] f_fifo_addr [0:((1<<OPT_FIFO_DEPTH)-1)];
// reg [NAUX+4-2:0] fifo_data [0:((1<<OPT_FIFO_DEPTH)-1)];
 
always @(*) f_twin_next = f_twin_base+1;
 
reg f_twin_none, f_twin_single, f_twin_double, f_twin_last;
reg f_twin_valid_one, f_twin_valid_two;
always @(*)
begin
f_twin_valid_one = ((f_valid_fifo_entry[f_twin_base])
&&(f_twin_first == { f_fifo_addr[f_twin_base],
fifo_data[f_twin_base] }));
f_twin_valid_two = ((f_valid_fifo_entry[f_twin_next])
&&(f_twin_second == { f_fifo_addr[f_twin_next],
fifo_data[f_twin_next] }));
end
 
always @(*)
begin
f_twin_none =(!f_twin_valid_one)&&(!f_twin_valid_two);
f_twin_single =( f_twin_valid_one)&&(!f_twin_valid_two);
f_twin_double =( f_twin_valid_one)&&( f_twin_valid_two);
f_twin_last =(!f_twin_valid_one)&&( f_twin_valid_two);
end
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset))||($past(cyc && i_wb_err)))
`ASSERT(f_twin_none);
else if ($past(f_twin_none))
`ASSERT(f_twin_none || f_twin_single || f_twin_last);
else if ($past(f_twin_single))
`ASSERT(f_twin_none || f_twin_single || f_twin_double || f_twin_last);
else if ($past(f_twin_double))
`ASSERT(f_twin_double || f_twin_last);
else if ($past(f_twin_last))
`ASSERT(f_twin_none || f_twin_single || f_twin_last);
 
`endif // TWIN_WRITE_TEST
 
always @(*)
`ASSERT(req_data == { gie, fifo_data[rdaddr[DP-1:0]] });
 
always @(posedge i_clk)
if (r_svalid||r_dvalid || r_rd_pending)
`ASSERT(f_fill == 1);
else if (f_fill > 0)
`ASSERT(cyc);
 
always @(posedge i_clk)
if (state != 0)
`ASSERT(f_fill > 0);
else if (!r_svalid && !r_dvalid && !r_rd_pending)
`ASSERT(f_fill == 0);
 
`endif // FORMAL
 
always @(posedge i_clk)
o_wreg <= req_data[(NAUX+4-1):4];
 
/*
reg fifo_err;
always @(posedge i_clk)
begin
fifo_err <= 1'b0;
if ((!o_busy)&&(rdaddr != wraddr))
fifo_err <= 1'b1;
if ((!r_dvalid)&&(!r_svalid)&&(!r_rd_pending))
fifo_err <= (npending != (wraddr-rdaddr));
end
 
always @(*)
o_debug = { i_pipe_stb, state, cyc, stb, // 5b
fifo_err, i_oreg[3:0], o_wreg, // 10b
rdaddr, wraddr, // 10b
i_wb_ack, i_wb_err, o_pipe_stalled, o_busy,//4b
r_svalid, r_dvalid, r_rd_pending };
*/
end else begin : NO_FIFO
 
always @(posedge i_clk)
if (i_pipe_stb)
req_data <= { i_oreg, i_op[2:1], i_addr[1:0] };
 
always @(*)
o_wreg = req_data[(NAUX+4-1):4];
 
always @(*)
gie = i_oreg[NAUX-1];
 
`ifdef FORMAL
assign f_pc = ((r_rd_pending)||(o_valid))&&(o_wreg[3:1] == 3'h7);
 
//
//
//
initial f_pending_addr = 0;
always @(posedge i_clk)
if (i_reset)
f_pending_addr <= 0;
else if (i_pipe_stb)
begin
f_pending_addr <= { (OPT_LOCAL_BUS)&&(&i_addr[DW-1:DW-8]),
i_addr[(AW+1):2] };
end
 
//
//
always @(*)
begin
f_return_address[AW] = o_wb_cyc_lcl;
f_return_address[AW-1:LS] = o_wb_addr[AW-1:LS];
end
always @(*)
if (state == DC_READS)
f_return_address[LS-1:0] = o_wb_addr[LS-1:0];
else
f_return_address[LS-1:0]
= (o_wb_addr[LS-1:0] - f_outstanding[LS-1:0]);
 
`endif
/*
always @(*)
o_debug = { i_pipe_stb, state, cyc, stb, // 5b
i_oreg, o_wreg, // 10b
10'hb, // 10b
i_wb_ack, i_wb_err, o_pipe_stalled, o_busy,//4b
r_svalid, r_dvalid, r_rd_pending };
*/
 
// verilator lint_off UNUSED
wire unused_no_fifo;
assign unused_no_fifo = gie;
// verilator lint_on UNUSED
end endgenerate
 
 
initial r_wb_cyc_gbl = 0;
initial r_wb_cyc_lcl = 0;
initial o_wb_stb_gbl = 0;
initial o_wb_stb_lcl = 0;
initial c_v = 0;
initial cyc = 0;
initial stb = 0;
initial c_wr = 0;
initial wr_cstb = 0;
initial state = DC_IDLE;
initial set_vflag = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
c_v <= 0;
c_wr <= 1'b0;
c_wsel <= 4'hf;
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 0;
o_wb_stb_lcl <= 0;
wr_cstb <= 1'b0;
last_line_stb <= 1'b0;
end_of_line <= 1'b0;
state <= DC_IDLE;
cyc <= 1'b0;
stb <= 1'b0;
state <= DC_IDLE;
set_vflag <= 1'b0;
end else begin
// By default, update the cache from the write 1-clock ago
// c_wr <= (wr_cstb)&&(wr_wtag == wr_vtag);
// c_waddr <= wr_addr[(CS-1):0];
c_wr <= 0;
c_wr <= (wr_cstb)&&(wr_wtag == wr_vtag);
c_wdata <= wr_data;
c_waddr <= wr_addr[(CS-1):0];
 
set_vflag <= 1'b0;
if ((!cyc)&&(set_vflag))
c_v[c_waddr[(CS-1):LS]] <= 1'b1;
 
wr_cstb <= 1'b0;
wr_vtag <= c_vtags[o_wb_addr[(CS-LS-1):0]];
wr_wtag <= o_wb_addr[(AW-LS-1):0];
wr_data <= o_wb_data;
wr_addr <= o_wb_addr[(CS-1):0];
 
if (!cyc)
wr_addr <= r_addr[(CS-1):0];
else if (i_wb_ack)
wr_addr <= wr_addr + 1'b1;
else
wr_addr <= wr_addr;
 
if (LS <= 0)
if (LS <= 1)
end_of_line <= 1'b1;
else if (!cyc)
end_of_line <= 1'b0;
else if (!end_of_line)
begin
if (i_wb_ack)
end_of_line
<= (c_waddr[(LS-1):0] == {{(LS-2){1'b1}},2'b01});
else
end_of_line
<= (c_waddr[(LS-1):0]=={{(LS-1){1'b1}}, 1'b0});
end
else
end_of_line<=(cyc)&&((c_waddr[(LS-1):1]=={(LS-1){1'b1}})
||((i_wb_ack)
&&(c_waddr[(LS-1):0]=={{(LS-2){1'b1}},2'b01})));
 
if (!cyc)
last_line_stb <= (LS <= 0);
else if ((stb)&&(!i_wb_stall)&&(LS <= 1))
if (LS <= 1)
last_line_stb <= 1'b1;
else if ((stb)&&(!i_wb_stall))
last_line_stb <= (o_wb_addr[(LS-1):1]=={(LS-1){1'b1}});
else if (stb)
last_line_stb <= (o_wb_addr[(LS-1):0]=={(LS){1'b1}});
else
last_line_stb <= (stb)&&
((o_wb_addr[(LS-1):1]=={(LS-1){1'b1}})
||((!i_wb_stall)
&&(o_wb_addr[(LS-1):0]
=={{(LS-2){1'b1}},2'b01})));
 
//
//
if (state == DC_IDLE)
if (state == `DC_IDLE)
pipeable_op <= 1'b0;
if (state == `DC_IDLE)
non_pipeable_op <= 1'b0;
 
 
if (state == `DC_IDLE)
begin
o_wb_we <= 1'b0;
o_wb_data <= i_data;
pipeable_op <= 1'b0;
non_pipeable_op <= 1'b1;
 
cyc <= 1'b0;
stb <= 1'b0;
758,58 → 280,47
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
 
in_cache <= (i_op[0])&&(w_cachable);
if ((i_pipe_stb)&&(i_op[0]))
in_cache <= (i_op)&&(w_cachable);
if ((i_pipe_stb)&&(i_op))
begin // Write operation
state <= DC_WRITE;
o_wb_addr <= i_addr[(AW+1):2];
state <= `DC_WRITE;
o_wb_addr <= i_addr;
o_wb_we <= 1'b1;
pipeable_op <= 1'b1;
 
cyc <= 1'b1;
stb <= 1'b1;
 
if (OPT_LOCAL_BUS)
begin
r_wb_cyc_gbl <= (i_addr[DW-1:DW-8]!=8'hff);
r_wb_cyc_lcl <= (i_addr[DW-1:DW-8]==8'hff);
o_wb_stb_gbl <= (i_addr[DW-1:DW-8]!=8'hff);
o_wb_stb_lcl <= (i_addr[DW-1:DW-8]==8'hff);
end else begin
r_wb_cyc_gbl <= 1'b1;
o_wb_stb_gbl <= 1'b1;
end
r_wb_cyc_gbl <= (i_addr[31:30]!=2'b11);
r_wb_cyc_lcl <= (i_addr[31:30]==2'b11);
o_wb_stb_gbl <= (i_addr[31:30]!=2'b11);
o_wb_stb_lcl <= (i_addr[31:30]==2'b11);
 
end else if (r_cache_miss)
begin
state <= DC_READC;
o_wb_addr <= { r_ctag, {(LS){1'b0}} };
state <= `DC_READC;
o_wb_addr <= { i_ctag, {(LS){1'b0}} };
non_pipeable_op <= 1'b1;
 
c_waddr <= { r_ctag[CS-LS-1:0], {(LS){1'b0}} }-1'b1;
cyc <= 1'b1;
stb <= 1'b1;
r_wb_cyc_gbl <= 1'b1;
o_wb_stb_gbl <= 1'b1;
wr_addr[LS-1:0] <= 0;
end else if ((i_pipe_stb)&&(!w_cachable))
begin // Read non-cachable memory area
state <= DC_READS;
o_wb_addr <= i_addr[(AW+1):2];
state <= `DC_READS;
o_wb_addr <= i_addr;
pipeable_op <= 1'b1;
 
cyc <= 1'b1;
stb <= 1'b1;
if (OPT_LOCAL_BUS)
begin
r_wb_cyc_gbl <= (i_addr[DW-1:DW-8]!=8'hff);
r_wb_cyc_lcl <= (i_addr[DW-1:DW-8]==8'hff);
o_wb_stb_gbl <= (i_addr[DW-1:DW-8]!=8'hff);
o_wb_stb_lcl <= (i_addr[DW-1:DW-8]==8'hff);
end else begin
r_wb_cyc_gbl <= 1'b1;
o_wb_stb_gbl <= 1'b1;
end
r_wb_cyc_gbl <= (i_addr[31:30]!=2'b11);
r_wb_cyc_lcl <= (i_addr[31:30]==2'b11);
o_wb_stb_gbl <= (i_addr[31:30]!=2'b11);
o_wb_stb_lcl <= (i_addr[31:30]==2'b11);
end // else we stay idle
 
end else if (state == DC_READC)
end else if (state == `DC_READC)
begin
// We enter here once we have committed to reading
// data into a cache line.
820,31 → 331,26
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1;
end
 
if ((i_wb_ack)&&(!end_of_line))
c_v[o_wb_addr[(CS-1):LS]] <= 1'b0;
if(stb)
c_v[o_wb_addr[(CS-LS-1):0]] <= 1'b0;
 
c_wr <= (i_wb_ack);
c_wdata <= i_wb_data;
c_waddr <= ((i_wb_ack)?(c_waddr+1'b1):c_waddr);
c_wsel <= 4'hf;
c_wr <= (i_wb_ack);
c_wdata <= o_wb_data;
c_waddr <= ((c_wr)?(c_waddr+1'b1):c_waddr);
 
set_vflag <= !i_wb_err;
if (i_wb_ack)
c_vtags[r_addr[(CS-1):LS]]
<= r_addr[(AW-1):LS];
c_vtags[o_wb_addr[(CS-LS-1):0]]<= o_wb_addr[(AW-LS-1):0];
 
if (((i_wb_ack)&&(end_of_line))||(i_wb_err))
begin
state <= DC_IDLE;
state <= `DC_IDLE;
non_pipeable_op <= 1'b0;
cyc <= 1'b0;
stb <= 1'b0;
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
//
c_v[o_wb_addr[(CS-LS-1):0]] <= i_wb_ack;
end
end else if (state == DC_READS)
end else if (state == `DC_READS)
begin
// We enter here once we have committed to reading
// data that cannot go into a cache line
853,31 → 359,27
stb <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
pipeable_op <= 1'b0;
end
 
if ((!i_wb_stall)&&(i_pipe_stb))
o_wb_addr <= i_addr[(AW+1):2];
o_wb_addr <= i_data;
 
c_wr <= 1'b0;
 
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
begin
state <= DC_IDLE;
state <= `DC_IDLE;
cyc <= 1'b0;
stb <= 1'b0;
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
end
end else if (state == DC_WRITE)
end else if (state == `DC_WRITE)
begin
c_wr <= (stb)&&(c_v[o_wb_addr[CS-1:LS]])
&&(c_vtags[o_wb_addr[CS-1:LS]]==o_wb_addr[AW-1:LS])
&&(stb);
// c_wr <= (c_v[])&&(c_tag[])&&(in_cache)&&(stb);
c_wdata <= o_wb_data;
c_waddr <= r_addr[CS-1:0];
c_wsel <= o_wb_sel;
c_waddr <= (state == `DC_IDLE)?i_caddr
: ((c_wr)?(c_waddr+1'b1):c_waddr);
 
if ((!i_wb_stall)&&(!i_pipe_stb))
begin
884,79 → 386,27
stb <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
pipeable_op <= 1'b0;
end
 
wr_cstb <= (stb)&&(!i_wb_stall)&&(in_cache);
 
if ((stb)&&(!i_wb_stall))
o_wb_addr <= i_addr[(AW+1):2];
 
if (((i_wb_ack)&&(last_ack)
&&((!OPT_PIPE)||(!i_pipe_stb)))
||(i_wb_err))
if ((stb)&&(!i_wb_stall)&&(i_pipe_stb))
o_wb_addr <= i_addr;
if ((stb)&&(!i_wb_stall)&&(i_pipe_stb))
o_wb_data <= i_data;
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
begin
state <= DC_IDLE;
state <= `DC_IDLE;
cyc <= 1'b0;
stb <= 1'b0;
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
end
end
end
 
//
// npending is the number of outstanding (non-cached) read or write
// requests
initial npending = 0;
always @(posedge i_clk)
if ((i_reset)||(!OPT_PIPE)
||((cyc)&&(i_wb_err))
||((!cyc)&&(!i_pipe_stb))
||(state == DC_READC))
npending <= 0;
else if (r_svalid)
npending <= (i_pipe_stb) ? 1:0;
else case({ (i_pipe_stb), (cyc)&&(i_wb_ack) })
2'b01: npending <= npending - 1'b1;
2'b10: npending <= npending + 1'b1;
default: begin end
endcase
 
initial last_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_ack <= 1'b0;
else if (state == DC_IDLE)
begin
last_ack <= 1'b0;
if ((i_pipe_stb)&&(i_op[0]))
last_ack <= 1'b1;
else if (r_cache_miss)
last_ack <= (LS == 0);
else if ((i_pipe_stb)&&(!w_cachable))
last_ack <= 1'b1;
end else if (state == DC_READC)
begin
if (i_wb_ack)
last_ack <= last_ack || (&wr_addr[LS-1:1]);
else
last_ack <= last_ack || (&wr_addr[LS-1:0]);
end else case({ (i_pipe_stb), (i_wb_ack) })
2'b01: last_ack <= (npending <= 2);
2'b10: last_ack <= (!cyc)||(npending == 0);
default: begin end
endcase
 
`ifdef FORMAL
always @(*)
`ASSERT(npending <= { 1'b1, {(DP){1'b0}} });
 
`endif
 
 
//
// Writes to the cache
//
// These have been made as simple as possible. Note that the c_wr
963,19 → 413,10
// line has already been determined, as have the write value and address
// on the last clock. Further, this structure is defined to match the
// block RAM design of as many architectures as possible.
//
//
always @(posedge i_clk)
if (c_wr)
begin
if (c_wsel[0])
c_mem[c_waddr][7:0] <= c_wdata[7:0];
if (c_wsel[1])
c_mem[c_waddr][15:8] <= c_wdata[15:8];
if (c_wsel[2])
c_mem[c_waddr][23:16] <= c_wdata[23:16];
if (c_wsel[3])
c_mem[c_waddr][31:24] <= c_wdata[31:24];
end
if (c_wr)
c_mem[c_waddr] <= c_wdata;
 
//
// Reads from the cache
985,1101 → 426,82
// going to be our output will need to be determined with combinatorial
// logic on the output.
//
generate if (OPT_DUAL_READ_PORT)
begin
reg [31:0] cached_idata, cached_rdata;
always @(posedge i_clk)
cached_idata <= c_mem[i_caddr];
 
always @(posedge i_clk)
cached_idata <= c_mem[i_caddr];
always @(posedge i_clk)
cached_rdata <= c_mem[r_caddr];
 
always @(posedge i_clk)
cached_rdata <= c_mem[r_caddr];
 
end else begin
 
always @(posedge i_clk)
cached_rdata <= c_mem[(o_busy) ? r_caddr : i_caddr];
 
always @(*)
cached_idata = cached_rdata;
 
end endgenerate
 
// o_data can come from one of three places:
// 1. The cache, assuming the data was in the last cache line
// 2. The cache, second clock, assuming the data was in the cache at all
// 3. The cache, after filling the cache
// 4. The wishbone state machine, upon reading the value desired.
always @(*)
always @(posedge i_clk)
if (r_svalid)
pre_data = cached_idata;
else if (state == DC_READS)
pre_data = i_wb_data;
o_data <= cached_idata;
else if ((i_wb_ack)&&(pipeable_op))
o_data <= i_wb_data;
else
pre_data = cached_rdata;
 
o_data <= cached_rdata;
always @(posedge i_clk)
casez(req_data[3:0])
4'b100?: o_data <= { 16'h0, pre_data[31:16] };
4'b101?: o_data <= { 16'h0, pre_data[15: 0] };
4'b1100: o_data <= { 24'h0, pre_data[31:24] };
4'b1101: o_data <= { 24'h0, pre_data[23:16] };
4'b1110: o_data <= { 24'h0, pre_data[15: 8] };
4'b1111: o_data <= { 24'h0, pre_data[ 7: 0] };
default o_data <= pre_data;
endcase
 
initial o_valid = 1'b0;
o_valid <= (r_svalid)||((i_wb_ack)&&(pipeable_op))
||(r_dvalid)||(r_rvalid);
always @(posedge i_clk)
if (i_reset)
o_valid <= 1'b0;
else if (state == DC_READS)
o_valid <= i_wb_ack;
else
o_valid <= (r_svalid)||(r_dvalid);
 
initial o_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_err <= 1'b0;
else
o_err <= (cyc)&&(i_wb_err);
 
initial o_busy = 0;
always @(posedge i_clk)
if ((i_reset)||((cyc)&&(i_wb_err)))
o_busy <= 1'b0;
else if (i_pipe_stb)
o_busy <= 1'b1;
else if ((state == DC_READS)&&(i_wb_ack))
o_busy <= 1'b0;
else if ((r_rd_pending)&&(!r_dvalid))
o_busy <= 1'b1;
else if ((state == DC_WRITE)
&&(i_wb_ack)&&(last_ack)&&(!i_pipe_stb))
o_busy <= 1'b0;
else if (cyc)
o_busy <= 1'b1;
else // if ((r_dvalid)||(r_svalid))
o_busy <= 1'b0;
assign o_busy = (state != `DC_IDLE);
 
//
// We can use our FIFO addresses to pre-calculate when an ACK is going
// to be the last_noncachable_ack.
 
 
always @(*)
if (OPT_PIPE)
o_pipe_stalled = (cyc)&&((!o_wb_we)||(i_wb_stall)||(!stb))
||(r_rd_pending)||(npending[DP]);
else
o_pipe_stalled = o_busy;
 
initial lock_gbl = 0;
initial lock_lcl = 0;
always @(posedge i_clk)
if (i_reset)
begin
lock_gbl <= 1'b0;
lock_lcl<= 1'b0;
end else begin
lock_gbl <= (OPT_LOCK)&&(i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (OPT_LOCK)&&(i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
end
 
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
 
generate if (AW+2 < DW)
begin : UNUSED_BITS
 
// Verilator lint_off UNUSED
wire [DW-AW-2:0] unused;
assign unused = i_addr[DW-1:AW+1];
// Verilator lint_on UNUSED
end endgenerate
 
`ifdef FORMAL
 
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
 
////////////////////////////////////////////////
//
// Reset properties
// Handle our auxilliary data lines.
//
////////////////////////////////////////////////
// These just go into a FIFO upon request, and then get fed back out
// upon completion of an OP.
//
// These are currently designed for handling bursts of writes or
// non-cachable reads.
//
always @(*)
if(!f_past_valid)
`ASSUME(i_reset);
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
// Insist on initial statements matching reset values
`ASSERT(r_rd == 1'b0);
`ASSERT(r_cachable == 1'b0);
`ASSERT(r_svalid == 1'b0);
`ASSERT(r_dvalid == 1'b0);
`ASSERT(r_cache_miss == 1'b0);
`ASSERT(r_addr == 0);
//
`ASSERT(c_wr == 0);
`ASSERT(c_v == 0);
//
// assert(aux_head == 0);
// assert(aux_tail == 0);
//
`ASSERT(lock_gbl == 0);
`ASSERT(lock_lcl == 0);
end
 
////////////////////////////////////////////////
// A very similar structure will be used once we switch to using an
// MMU, in order to make certain memory operations are synchronous
// enough to deal with bus errors.
//
// Assumptions about our inputs
//
////////////////////////////////////////////////
//
//
always @(*)
if (o_pipe_stalled)
`ASSUME(!i_pipe_stb);
 
always @(*)
if (!f_past_valid)
`ASSUME(!i_pipe_stb);
 
reg [(LGAUX-1):0] aux_head, aux_tail;
reg [(NAUX-1):0] aux_fifo [0:((1<<LGAUX)-1)];
initial aux_head = 0;
initial aux_tail = 0;
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(i_pipe_stb))&&($past(o_pipe_stalled)))
begin
`ASSUME($stable(i_pipe_stb));
`ASSUME($stable(i_op[0]));
`ASSUME($stable(i_addr));
if (i_op[0])
`ASSUME($stable(i_data));
if ((i_rst)||(i_wb_err))
aux_head <= 0;
else if ((i_pipe_stb)&&(!o_busy))
aux_head <= aux_head + 1'b1;
aux_fifo[aux_head] <= i_oreg;
end
 
always @(posedge i_clk)
if (o_err)
`ASSUME(!i_pipe_stb);
 
////////////////////////////////////////////////
//
// Wishbone properties
//
////////////////////////////////////////////////
//
//
wire f_cyc, f_stb;
 
assign f_cyc = (o_wb_cyc_gbl)|(o_wb_cyc_lcl);
assign f_stb = (o_wb_stb_gbl)|(o_wb_stb_lcl);
 
always @(*)
begin
// Only one interface can be active at once
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl));
// Strobe may only be active on the active interface
`ASSERT((r_wb_cyc_gbl)||(!o_wb_stb_gbl));
`ASSERT((r_wb_cyc_lcl)||(!o_wb_stb_lcl));
if (o_wb_stb_lcl)
begin
if (o_wb_we)
assert(state == DC_WRITE);
else
assert(state == DC_READS);
end
 
if (cyc)
assert(o_wb_we == (state == DC_WRITE));
if ((i_rst)||(i_wb_err))
aux_tail <= 0;
else if (o_valid) // ||(aux_tail[WBIT])&&(no-mmu-error)
aux_tail <= aux_tail + 1'b1;
o_wreg <= aux_fifo[aux_tail];
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(cyc)&&($past(cyc)))
begin
`ASSERT($stable(r_wb_cyc_gbl));
`ASSERT($stable(r_wb_cyc_lcl));
end
 
 
`ifdef DCACHE
`define FWB_MASTER fwb_master
`else
`define FWB_MASTER fwb_counter
`endif
 
`FWB_MASTER #(
.AW(AW), .DW(DW),
.F_MAX_STALL(2),
.F_MAX_ACK_DELAY(3),
// If you need the proof to run faster, use these
// lines instead of the two that follow
// .F_MAX_STALL(1),
// .F_MAX_ACK_DELAY(1),
.F_LGDEPTH(F_LGDEPTH),
.F_MAX_REQUESTS((OPT_PIPE) ? 0 : (1<<LS)),
`ifdef DCACHE
.F_OPT_SOURCE(1'b1),
`endif
.F_OPT_DISCONTINUOUS(0)
) fwb(i_clk, i_reset,
cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
`ifdef DCACHE // Arbitrary access is specific to local dcache implementation
////////////////////////////////////////////////
//
// Arbitrary address properties
//
////////////////////////////////////////////////
//
//
(* anyconst *) reg [AW:0] f_const_addr;
(* anyconst *) reg f_const_buserr;
wire [AW-LS-1:0] f_const_tag, f_ctag_here, f_wb_tag;
wire [CS-LS-1:0] f_const_tag_addr;
reg [DW-1:0] f_const_data;
wire [DW-1:0] f_cmem_here;
reg f_pending_rd;
wire f_cval_in_cache;
// We can use our FIFO addresses to pre-calculate when an ACK is going
// to be the last_noncachable_ack.
 
assign f_const_tag = f_const_addr[AW-1:LS];
assign f_const_tag_addr = f_const_addr[CS-1:LS];
assign f_cmem_here = c_mem[f_const_addr[CS-1:0]];
assign f_ctag_here = c_vtags[f_const_addr[CS-1:LS]];
assign f_wb_tag = o_wb_addr[AW-1:LS];
 
assign f_cval_in_cache= (c_v[f_const_addr[CS-1:LS]])
&&(f_ctag_here == f_const_tag);
assign o_pipe_stalled=((pipeable_op)&&(i_wb_stall))||(non_pipeable_op);
// pipeable_op must become zero when stb goes low
 
generate if ((AW > DW - 8)&&(OPT_LOCAL_BUS))
begin : UPPER_CONST_ADDR_BITS
 
always @(*)
if (f_const_addr[AW])
assume(&f_const_addr[(AW-1):(DW-8)]);
else
assume(!(&f_const_addr[(AW-1):(DW-8)]));
end endgenerate
 
wire [AW-1:0] wb_start;
assign wb_start = (f_stb) ? (o_wb_addr - f_nreqs) : o_wb_addr;
 
// Data changes upon request
always @(posedge i_clk)
begin
if ((i_pipe_stb)&&(i_addr[(AW+1):2] == f_const_addr[AW-1:0])
&&(f_const_addr[AW] == ((OPT_LOCAL_BUS)
&&(&i_addr[(DW-1):(DW-8)])))
&&(i_op[0]))
begin
casez({ i_op[2:1], i_addr[1:0] })
4'b0???: f_const_data <= i_data;
4'b100?: f_const_data[31:16] <= i_data[15:0];
4'b101?: f_const_data[15: 0] <= i_data[15:0];
4'b1100: f_const_data[31:24] <= i_data[ 7:0];
4'b1101: f_const_data[23:16] <= i_data[ 7:0];
4'b1110: f_const_data[15: 8] <= i_data[ 7:0];
4'b1111: f_const_data[ 7: 0] <= i_data[ 7:0];
endcase
end
 
if (f_cval_in_cache)
assume((!i_wb_err)
||(!i_pipe_stb)
||(f_const_addr[AW-1:0] != i_addr[AW+1:2]));
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
end
 
 
always @(posedge i_clk)
if ((f_past_valid)&&(!i_reset)&&(!f_const_buserr))
begin
if ((cyc)&&(o_wb_we)&&(f_stb)
&&(o_wb_addr[AW-1:0] == f_const_addr[AW-1:0])
&&( o_wb_stb_lcl == f_const_addr[AW]))
begin
 
//
// Changing our data
//
if (o_wb_sel[0])
`ASSERT(o_wb_data[ 7: 0]==f_const_data[ 7: 0]);
if (o_wb_sel[1])
`ASSERT(o_wb_data[15: 8]==f_const_data[15: 8]);
if (o_wb_sel[2])
`ASSERT(o_wb_data[23:16]==f_const_data[23:16]);
if (o_wb_sel[3])
`ASSERT(o_wb_data[31:24]==f_const_data[31:24]);
 
// Check the data in the cache
if ((!f_const_addr[AW])&&(c_v[f_const_tag_addr])
&&(f_ctag_here == o_wb_addr[AW-1:LS]))
begin
if ((!c_wsel[0])&&(!o_wb_sel[0]))
`ASSERT(f_cmem_here[ 7: 0]==f_const_data[ 7: 0]);
if ((!c_wsel[1])&&(!o_wb_sel[1]))
`ASSERT(f_cmem_here[15: 8]==f_const_data[15: 8]);
if ((!c_wsel[2])&&(!o_wb_sel[2]))
`ASSERT(f_cmem_here[23:16]==f_const_data[23:16]);
if ((!c_wsel[3])&&(!o_wb_sel[3]))
`ASSERT(f_cmem_here[31:24]==f_const_data[31:24]);
 
end
end else if ((!f_const_addr[AW])&&(c_v[f_const_tag_addr])
&&(f_ctag_here ==f_const_addr[AW-1:LS]))
begin
// If ...
// 1. Our magic address is cachable
// 2. Our magic address is associated with a valid
// cache line
// 3. The cache tag matches our magic address
 
 
// if ($past(cyc && i_wb_err))
// begin
// Ignore what happens on an error, the result
// becomes undefined anyway
// end else
if ((c_wr)
&&(c_waddr[CS-1:0] == f_const_addr[CS-1:0]))
begin
//
// If we are writing to this valid cache line
//
if (c_wsel[3])
`ASSERT(c_wdata[31:24]
== f_const_data[31:24]);
else
`ASSERT(f_cmem_here[31:24]
== f_const_data[31:24]);
if (c_wsel[2])
`ASSERT(c_wdata[23:16]
== f_const_data[23:16]);
else
`ASSERT(f_cmem_here[23:16] == f_const_data[23:16]);
if (c_wsel[1])
`ASSERT(c_wdata[15:8]
== f_const_data[15:8]);
else
`ASSERT(f_cmem_here[15:8] == f_const_data[15:8]);
if (c_wsel[0])
`ASSERT(c_wdata[7:0]
== f_const_data[7:0]);
else
`ASSERT(f_cmem_here[7:0] == f_const_data[7:0]);
end else
`ASSERT(f_cmem_here == f_const_data);
end
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(state == DC_READC))
begin
`ASSERT(f_wb_tag == r_ctag);
if ((wb_start[AW-1:LS] == f_const_tag)
&&(!c_v[f_const_tag_addr])
&&(f_const_addr[AW] == r_wb_cyc_lcl)
&&(f_nacks > f_const_addr[LS-1:0]))
begin
// We are reading the cache line containing our
// constant address f_const_addr. Make sure the data
// is correct.
if ((c_wr)&&(c_waddr[CS-1:0] == f_const_addr[CS-1:0]))
`ASSERT(c_wdata == f_const_data);
else
`ASSERT(f_cmem_here == f_const_data);
end
 
if (f_nacks > 0)
`ASSERT(!c_v[wb_start[CS-1:LS]]);
end
 
always @(posedge i_clk)
if ((state == DC_READC)&&(f_nacks > 0))
begin
`ASSERT(c_vtags[wb_start[(CS-1):LS]] <= wb_start[(AW-1):LS]);
`ASSERT(c_vtags[wb_start[(CS-1):LS]] <= r_addr[AW-1:LS]);
end
 
reg [AW-1:0] f_cache_waddr;
wire f_this_cache_waddr;
 
always @(*)
begin
// f_cache_waddr[AW-1:LS] = c_vtags[c_waddr[CS-1:CS-LS]];
f_cache_waddr[AW-1:LS] = wb_start[AW-1:LS];
f_cache_waddr[CS-1: 0] = c_waddr[CS-1:0];
end
 
assign f_this_cache_waddr = (!f_const_addr[AW])
&&(f_cache_waddr == f_const_addr[AW-1:0]);
always @(posedge i_clk)
if ((f_past_valid)&&(state == DC_READC))
begin
if ((c_wr)&&(c_waddr[LS-1:0] != 0)&&(f_this_cache_waddr))
`ASSERT(c_wdata == f_const_data);
end
 
always @(posedge i_clk)
if ((OPT_PIPE)&&(o_busy)&&(i_pipe_stb))
begin
`ASSUME(i_op[0] == o_wb_we);
if (o_wb_cyc_lcl)
assume(&i_addr[DW-1:DW-8]);
else
assume(!(&i_addr[DW-1:DW-8]));
end
 
initial f_pending_rd = 0;
always @(posedge i_clk)
if (i_reset)
f_pending_rd <= 0;
else if (i_pipe_stb)
f_pending_rd <= (!i_op[0]);
else if ((o_valid)&&((!OPT_PIPE)
||((state != DC_READS)&&(!r_svalid)&&(!$past(i_pipe_stb)))))
f_pending_rd <= 1'b0;
 
always @(*)
if ((state == DC_READC)&&(!f_stb))
`ASSERT(f_nreqs == (1<<LS));
 
always @(*)
if ((state == DC_READC)&&(f_stb))
`ASSERT(f_nreqs == { 1'b0, o_wb_addr[LS-1:0] });
 
always @(posedge i_clk)
if (state == DC_READC)
begin
if (($past(i_wb_ack))&&(!$past(f_stb)))
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] });
else if (f_nacks > 0)
begin
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] });
`ASSERT(c_waddr[CS-1:LS] == o_wb_addr[CS-1:LS]);
end else begin
`ASSERT(c_waddr[CS-1:LS] == o_wb_addr[CS-1:LS]-1'b1);
`ASSERT(&c_waddr[LS-1:0]);
end
end
 
always @(*)
if (r_rd_pending)
`ASSERT(r_addr == f_pending_addr[AW-1:0]);
 
always @(*)
if (f_pending_addr[AW])
begin
`ASSERT(state != DC_READC);
`ASSERT((!o_wb_we)||(!o_wb_cyc_gbl));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(o_valid)&&($past(f_pending_addr) == f_const_addr))
begin
if (f_const_buserr)
`ASSERT(o_err);
else if (f_pending_rd)
begin
casez($past(req_data[3:0]))
4'b0???: `ASSERT(o_data ==f_const_data);
4'b101?: `ASSERT(o_data =={16'h00,f_const_data[15: 0]});
4'b100?: `ASSERT(o_data =={16'h00,f_const_data[31:16]});
4'b1100: `ASSERT(o_data =={24'h00,f_const_data[31:24]});
4'b1101: `ASSERT(o_data =={24'h00,f_const_data[23:16]});
4'b1110: `ASSERT(o_data =={24'h00,f_const_data[15: 8]});
4'b1111: `ASSERT(o_data =={24'h00,f_const_data[ 7: 0]});
endcase
end
end
 
wire f_this_return;
 
assign f_this_return = (f_return_address == f_const_addr);
always @(*)
if ((f_cyc)&&(
((state == DC_READC)
&&(f_return_address[AW-1:LS] == f_const_addr[AW-1:LS]))
||(f_this_return))&&(f_cyc))
begin
if (f_const_buserr)
assume(!i_wb_ack);
else begin
assume(!i_wb_err);
assume(i_wb_data == f_const_data);
end
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(last_tag == f_const_tag)&&(f_const_buserr)
&&(!f_const_addr[AW]))
`ASSERT(!last_tag_valid);
 
always @(*)
if (f_const_buserr)
begin
`ASSERT((!c_v[f_const_tag_addr])||(f_const_addr[AW])
||(f_ctag_here != f_const_tag));
 
if ((state == DC_READC)&&(wb_start[AW-1:LS] == f_const_tag))
begin
`ASSERT(f_nacks <= f_const_tag[LS-1:0]);
if (f_nacks == f_const_tag[LS-1:0])
assume(!i_wb_ack);
end
end
 
`endif // DCACHE
 
////////////////////////////////////////////////
//
// Checking the lock
//
////////////////////////////////////////////////
//
//
 
always @(*)
`ASSERT((!lock_gbl)||(!lock_lcl));
always @(*)
if (!OPT_LOCK)
`ASSERT((!lock_gbl)&&(!lock_lcl));
 
////////////////////////////////////////////////
//
// State based properties
//
////////////////////////////////////////////////
//
//
reg [F_LGDEPTH-1:0] f_rdpending;
 
initial f_rdpending = 0;
always @(posedge i_clk)
if ((i_reset)||(o_err))
f_rdpending <= 0;
else case({ (i_pipe_stb)&&(!i_op[0]), o_valid })
2'b01: f_rdpending <= f_rdpending - 1'b1;
2'b10: f_rdpending <= f_rdpending + 1'b1;
default: begin end
endcase
 
wire f_wb_cachable;
iscachable #(.ADDRESS_WIDTH(AW))
f_chkwb_addr(o_wb_addr, f_wb_cachable);
 
 
always @(*)
if (state == DC_IDLE)
begin
`ASSERT(!r_wb_cyc_gbl);
`ASSERT(!r_wb_cyc_lcl);
 
`ASSERT(!cyc);
 
if ((r_rd_pending)||(r_dvalid)||(r_svalid))
`ASSERT(o_busy);
 
if (!OPT_PIPE)
begin
if (r_rd_pending)
`ASSERT(o_busy);
else if (r_svalid)
`ASSERT(o_busy);
else if (o_valid)
`ASSERT(!o_busy);
else if (o_err)
`ASSERT(!o_busy);
end
end else begin
`ASSERT(o_busy);
`ASSERT(cyc);
end
 
 
 
always @(posedge i_clk)
if (state == DC_IDLE)
begin
if (r_svalid)
begin
`ASSERT(!r_dvalid);
`ASSERT(!r_rd_pending);
if (!OPT_PIPE)
`ASSERT(!o_valid);
else if (o_valid)
`ASSERT(f_rdpending == 2);
end
 
if (r_dvalid)
begin
`ASSERT(!r_rd_pending);
`ASSERT(npending == 0);
`ASSERT(f_rdpending == 1);
end
 
if (r_rd_pending)
begin
if ((OPT_PIPE)&&(o_valid))
`ASSERT(f_rdpending <= 2);
else
`ASSERT(f_rdpending == 1);
 
end else if ((OPT_PIPE)&&(o_valid)&&($past(r_dvalid|r_svalid)))
`ASSERT(f_rdpending <= 2);
else
`ASSERT(f_rdpending <= 1);
end
 
always @(posedge i_clk)
if (state == DC_READC)
begin
`ASSERT( o_wb_cyc_gbl);
`ASSERT(!o_wb_cyc_lcl);
`ASSERT(!o_wb_we);
`ASSERT(f_wb_cachable);
 
`ASSERT(r_rd_pending);
`ASSERT(r_cachable);
if (($past(cyc))&&(!$past(o_wb_stb_gbl)))
`ASSERT(!o_wb_stb_gbl);
 
if ((OPT_PIPE)&&(o_valid))
`ASSERT(f_rdpending == 2);
else
`ASSERT(f_rdpending == 1);
end
 
always @(*)
if (state == DC_READS)
begin
`ASSERT(!o_wb_we);
 
if (OPT_PIPE)
begin
if (o_valid)
`ASSERT((f_rdpending == npending + 1)
||(f_rdpending == npending));
else
`ASSERT(f_rdpending == npending);
end
end else if (state == DC_WRITE)
`ASSERT(o_wb_we);
 
always @(posedge i_clk)
if ((state == DC_READS)||(state == DC_WRITE))
begin
`ASSERT(o_wb_we == (state == DC_WRITE));
`ASSERT(!r_rd_pending);
if (o_wb_we)
`ASSERT(f_rdpending == 0);
 
if (OPT_PIPE)
begin
casez({ $past(i_pipe_stb), f_stb })
2'b00: `ASSERT(npending == f_outstanding);
2'b1?: `ASSERT(npending == f_outstanding + 1);
2'b01: `ASSERT(npending == f_outstanding + 1);
endcase
 
if (state == DC_WRITE)
`ASSERT(!o_valid);
end else
`ASSERT(f_outstanding <= 1);
end
 
always @(*)
if (OPT_PIPE)
`ASSERT(f_rdpending <= 2);
else
`ASSERT(f_rdpending <= 1);
 
always @(posedge i_clk)
if ((!OPT_PIPE)&&(o_valid))
`ASSERT(f_rdpending == 1);
else if (o_valid)
`ASSERT(f_rdpending >= 1);
 
 
always @(*)
if ((!o_busy)&&(!o_err)&&(!o_valid))
`ASSERT(f_rdpending == 0);
 
always @(*)
`ASSERT(cyc == ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)));
 
always @(*)
if ((!i_reset)&&(f_nreqs == f_nacks)&&(!f_stb))
`ASSERT(!cyc);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_err)))
`ASSUME(!i_lock);
else if ((f_past_valid)&&(OPT_LOCK)&&($past(i_lock))
&&((!$past(o_valid)) || ($past(i_pipe_stb))))
`ASSUME($stable(i_lock));
 
 
////////////////////////////////////////////////
//
// Ad-hoc properties
//
////////////////////////////////////////////////
//
//
always @(*)
if ((OPT_PIPE)&&(state == DC_WRITE)&&(!i_wb_stall)&&(stb)
&&(!npending[DP]))
`ASSERT(!o_pipe_stalled);
 
always @(posedge i_clk)
if (state == DC_WRITE)
`ASSERT(o_wb_we);
else if ((state == DC_READS)||(state == DC_READC))
`ASSERT(!o_wb_we);
 
always @(*)
if (cyc)
`ASSERT(f_cyc);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(cyc))&&(!c_wr)&&(last_tag_valid)
&&(!r_rd_pending))
`ASSERT((c_v[last_tag[(CS-LS-1):0]])
&&(c_vtags[last_tag[(CS-LS-1):0]] == last_tag));
 
always @(*)
if (!OPT_LOCAL_BUS)
begin
`ASSERT(r_wb_cyc_lcl == 1'b0);
`ASSERT(o_wb_stb_lcl == 1'b0);
`ASSERT(lock_lcl == 1'b0);
end
 
always @(posedge i_clk)
if ((state == DC_READC)&&(!stb))
begin
`ASSERT(o_wb_addr[LS-1:0] == 0);
`ASSERT(o_wb_addr[AW-1:CS] == r_addr[AW-1:CS]);
end else if ((state == DC_READC)&&(stb))
begin
`ASSERT(o_wb_addr[AW-1:CS] == r_addr[AW-1:CS]);
`ASSERT(o_wb_addr[LS-1:0] == f_nreqs[LS-1:0]);
end
 
wire [CS-1:0] f_expected_caddr;
assign f_expected_caddr = { r_ctag[CS-LS-1:0], {(LS){1'b0}} }-1
+ f_nacks;
always @(posedge i_clk)
if (state == DC_READC)
begin
if (LS == 0)
`ASSERT(end_of_line);
else if (f_nacks < (1<<LS)-1)
`ASSERT(!end_of_line);
else if (f_nacks == (1<<LS)-1)
`ASSERT(end_of_line);
`ASSERT(f_nacks <= (1<<LS));
`ASSERT(f_nreqs <= (1<<LS));
if (f_nreqs < (1<<LS))
begin
`ASSERT(o_wb_stb_gbl);
`ASSERT(o_wb_addr[(LS-1):0] == f_nreqs[LS-1:0]);
end else
`ASSERT(!f_stb);
`ASSERT((f_nreqs == 0)||(f_nacks <= f_nreqs));
`ASSERT(c_waddr == f_expected_caddr);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(r_rd)&&(!$past(i_reset)))
begin
`ASSERT((o_busy)||(r_svalid));
end
 
always @(posedge i_clk)
if (!$past(o_busy))
`ASSERT(!r_dvalid);
 
always @(posedge i_clk)
if ((state == DC_READC)&&(c_wr))
`ASSERT(c_wsel == 4'hf);
 
always @(*)
if (c_wr)
`ASSERT((c_wsel == 4'hf)
||(c_wsel == 4'hc)
||(c_wsel == 4'h3)
||(c_wsel == 4'h8)
||(c_wsel == 4'h4)
||(c_wsel == 4'h2)
||(c_wsel == 4'h1));
 
always @(*)
if (!OPT_PIPE)
`ASSERT(o_pipe_stalled == o_busy);
else if (o_pipe_stalled)
`ASSERT(o_busy);
 
//
// Only ever abort on reset
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(cyc))&&(!$past(i_wb_err)))
begin
if (($past(i_pipe_stb))&&(!$past(o_pipe_stalled)))
`ASSERT(cyc);
else if ($past(f_outstanding > 1))
`ASSERT(cyc);
else if (($past(f_outstanding == 1))
&&((!$past(i_wb_ack))
||(($past(f_stb))
&&(!$past(i_wb_stall)))))
`ASSERT(cyc);
else if (($past(f_outstanding == 0))
&&($past(f_stb)&&(!$past(i_wb_ack))))
`ASSERT(cyc);
end
 
always @(posedge i_clk)
if ((OPT_PIPE)&&(f_past_valid)&&(!$past(i_reset))&&(state != DC_READC))
begin
if ($past(cyc && i_wb_err))
begin
`ASSERT(npending == 0);
end else if (($past(i_pipe_stb))||($past(i_wb_stall && stb)))
`ASSERT((npending == f_outstanding+1)
||(npending == f_outstanding+2));
else
`ASSERT(npending == f_outstanding);
end
 
always @(posedge i_clk)
if ((OPT_PIPE)&&(state != DC_READC)&&(state != DC_IDLE))
`ASSERT(last_ack == (npending <= 1));
 
always @(*)
`ASSERT(stb == f_stb);
 
always @(*)
if (r_rd_pending)
`ASSERT(!r_svalid);
 
always @(*)
if (o_err)
`ASSUME(!i_pipe_stb);
 
always @(*)
if (last_tag_valid)
`ASSERT(|c_v);
 
always @(posedge i_clk)
if ((cyc)&&(state == DC_READC)&&($past(f_nacks > 0)))
`ASSERT(!c_v[o_wb_addr[CS-1:LS]]);
 
always @(*)
if (last_tag_valid)
begin
`ASSERT((!cyc)||(o_wb_we)||(state == DC_READS)
||(o_wb_addr[AW-1:LS] != last_tag));
end
 
wire f_cachable_last_tag, f_cachable_r_addr;
 
iscachable #(.ADDRESS_WIDTH(AW))
fccheck_last_tag({last_tag, {(LS){1'b0}}}, f_cachable_last_tag);
 
iscachable #(.ADDRESS_WIDTH(AW))
fccheck_r_cachable(r_addr, f_cachable_r_addr);
 
always @(*)
if ((r_cachable)&&(r_rd_pending))
begin
`ASSERT(state != DC_WRITE);
// `ASSERT(state != DC_READS);
`ASSERT(f_cachable_r_addr);
if (cyc)
`ASSERT(o_wb_addr[AW-1:LS] == r_addr[AW-1:LS]);
end
 
always @(*)
if (last_tag_valid)
begin
`ASSERT(f_cachable_last_tag);
`ASSERT(c_v[last_tag[CS-LS-1:0]]);
`ASSERT(c_vtags[last_tag[CS-LS-1:0]]==last_tag);
`ASSERT((state != DC_READC)||(last_tag != o_wb_addr[AW-1:LS]));
end
 
 
////////////////////////////////////////////////
//
// Cover statements
//
////////////////////////////////////////////////
//
//
 
always @(posedge i_clk)
cover(o_valid);
 
always @(posedge i_clk)
if (f_past_valid)
cover($past(r_svalid));
 
generate if (OPT_PIPE)
begin : PIPE_COVER
 
wire recent_reset;
reg [2:0] recent_reset_sreg;
initial recent_reset_sreg = -1;
always @(posedge i_clk)
if (i_reset)
recent_reset_sreg <= -1;
else
recent_reset_sreg <= { recent_reset_sreg[1:0], 1'b0 };
 
assign recent_reset = (i_reset)||(|recent_reset_sreg);
 
//
//
wire f_cvr_cread = (!recent_reset)&&(i_pipe_stb)&&(!i_op[0])
&&(w_cachable);
 
wire f_cvr_cwrite = (!recent_reset)&&(i_pipe_stb)&&(i_op[0])
&&(!cache_miss_inow);
 
wire f_cvr_writes = (!recent_reset)&&(i_pipe_stb)&&(i_op[0])
&&(!w_cachable);
wire f_cvr_reads = (!recent_reset)&&(i_pipe_stb)&&(!i_op[0])
&&(!w_cachable);
wire f_cvr_test = (!recent_reset)&&(cyc);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid)))
cover(o_valid); // !
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_pipe_stb)))
cover(i_pipe_stb);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&($past(o_valid,2)))
cover(o_valid);
 
always @(posedge i_clk)
cover(($past(f_cvr_cread))&&(f_cvr_cread));
 
always @(posedge i_clk)
cover(($past(f_cvr_cwrite))&&(f_cvr_cwrite));
 
always @(posedge i_clk)
cover(($past(f_cvr_writes))&&(f_cvr_writes));
 
/*
* This cover statement will never pass. Why not? Because
* cache reads must be separated from non-cache reads. Hence,
* we can only allow a single non-cache read at a time, otherwise
* we'd bypass the cache read logic.
*
always @(posedge i_clk)
cover(($past(f_cvr_reads))&&(f_cvr_reads));
*/
 
//
// This is unrealistic, as it depends upon the Wishbone
// acknoledging the request on the same cycle
always @(posedge i_clk)
cover(($past(f_cvr_reads,2))&&(f_cvr_reads));
 
always @(posedge i_clk)
cover(($past(r_dvalid))&&(r_svalid));
 
//
// A minimum of one clock must separate two dvalid's.
// This option is rather difficult to cover, since it means
// we must first load two separate cache lines before
// this can even be tried.
always @(posedge i_clk)
cover(($past(r_dvalid,2))&&(r_dvalid));
 
//
// This is the optimal configuration we want:
// i_pipe_stb
// ##1 i_pipe_stb && r_svalid
// ##1 r_svalid && o_valid
// ##1 o_valid
// It proves that we can handle a 2 clock delay, but that
// we can also pipelin these cache accesses, so this
// 2-clock delay becomes a 1-clock delay between pipelined
// memory reads.
//
always @(posedge i_clk)
cover(($past(r_svalid))&&(r_svalid));
 
//
// While we'd never do this (it breaks the ZipCPU's pipeline
// rules), it's nice to know we could.
// i_pipe_stb && (!i_op[0]) // a read
// ##1 i_pipe_stb && (i_op[0]) && r_svalid // a write
// ##1 o_valid
always @(posedge i_clk)
cover(($past(r_svalid))&&(f_cvr_writes));
 
/* Unreachable
*
always @(posedge i_clk)
cover(($past(f_cvr_writes))&&(o_valid));
 
always @(posedge i_clk)
cover(($past(f_cvr_writes,2))&&(o_valid));
 
always @(posedge i_clk)
cover(($past(f_cvr_writes,3))&&(o_valid));
 
always @(posedge i_clk)
cover(($past(r_dvalid,3))&&(r_dvalid));
 
*/
 
always @(posedge i_clk)
cover(($past(f_cvr_writes,4))&&(o_valid));
 
end endgenerate
 
////////////////////////////////////////////////
//
// Carelesss assumption section
//
////////////////////////////////////////////////
//
//
 
//
// Can't jump from local to global mid lock
always @(*)
if((OPT_LOCK)&&(OPT_LOCAL_BUS))
begin
if ((i_lock)&&(o_wb_cyc_gbl)&&(i_pipe_stb))
assume(!(&i_addr[(DW-1):(DW-8)]));
else if ((i_lock)&&(o_wb_cyc_lcl)&&(i_pipe_stb))
assume(&i_addr[(DW-1):(DW-8)]);
end
 
always @(*)
if ((OPT_PIPE)&&(o_busy || i_lock)&&(!o_pipe_stalled))
begin
if (i_pipe_stb)
assume((!OPT_LOCAL_BUS)
||(f_pending_addr[AW]==(&i_addr[DW-1:DW-8])));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(cyc))&&(!cyc))
assume((!i_wb_err)&&(!i_wb_ack));
 
`endif
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
endmodule
/pfcache.v
5,37 → 5,15
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: Keeping our CPU fed with instructions, at one per clock and
// with only a minimum number stalls. The entire cache may also
// be cleared (if necessary).
// with no stalls. An unusual feature of this cache is the
// requirement that the entire cache may be cleared (if necessary).
//
// This logic is driven by a couple realities:
// 1. It takes a clock to read from a block RAM address, and hence a clock
// to read from the cache.
// 2. It takes another clock to check that the tag matches
//
// Our goal will be to avoid this second check if at all possible.
// Hence, we'll test on the clock of any given request whether
// or not the request matches the last tag value, and on the next
// clock whether it new tag value (if it has changed). Hence,
// for anything found within the cache, there will be a one
// cycle delay on any branch.
//
//
// Address Words are separated into three components:
// [ Tag bits ] [ Cache line number ] [ Cache position w/in the line ]
//
// On any read from the cache, only the second two components are required.
// On any read from memory, the first two components will be fixed across
// the bus, and the third component will be adjusted from zero to its
// maximum value.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
59,62 → 37,36
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module pfcache(i_clk, i_reset, i_new_pc, i_clear_cache,
module pfcache(i_clk, i_rst, i_new_pc, i_clear_cache,
// i_early_branch, i_from_addr,
i_stall_n, i_pc, o_insn, o_pc, o_valid,
i_stall_n, i_pc, o_i, o_pc, o_v,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data,
o_illegal
`ifdef NOT_YET_READY
, i_mmu_ack, i_mmu_we, i_mmu_paddr
`endif
`ifdef FORMAL
, f_pc_wb
`endif
);
`ifdef FORMAL
parameter LGCACHELEN = 4, ADDRESS_WIDTH=30,
LGLINES=2; // Log of the number of separate cache lines
`else
parameter LGCACHELEN = 12, ADDRESS_WIDTH=30,
LGLINES=6; // Log of the number of separate cache lines
`endif
localparam CACHELEN=(1<<LGCACHELEN); //Wrd Size of our cache memory
o_illegal);
parameter LGCACHELEN = 8, ADDRESS_WIDTH=24,
LGLINES=5; // Log of the number of separate cache lines
localparam CACHELEN=(1<<LGCACHELEN); // Size of our cache memory
localparam CW=LGCACHELEN; // Short hand for LGCACHELEN
localparam LS=LGCACHELEN-LGLINES; // Size of a cache line
localparam PW=LGCACHELEN-LGLINES; // Size of a cache line
localparam BUSW = 32; // Number of data lines on the bus
localparam AW=ADDRESS_WIDTH; // Shorthand for ADDRESS_WIDTH
input i_clk, i_rst, i_new_pc;
input i_clear_cache;
input i_stall_n;
input [(AW-1):0] i_pc;
output wire [(BUSW-1):0] o_i;
output wire [(AW-1):0] o_pc;
output wire o_v;
//
input wire i_clk, i_reset;
//
// The interface with the rest of the CPU
input wire i_new_pc;
input wire i_clear_cache;
input wire i_stall_n;
input wire [(AW+1):0] i_pc;
output wire [(BUSW-1):0] o_insn;
output wire [(AW+1):0] o_pc;
output wire o_valid;
//
// The wishbone bus interface
output reg o_wb_cyc, o_wb_stb;
output wire o_wb_we;
output reg o_wb_cyc, o_wb_stb;
output wire o_wb_we;
output reg [(AW-1):0] o_wb_addr;
output wire [(BUSW-1):0] o_wb_data;
//
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(BUSW-1):0] i_wb_data;
input i_wb_ack, i_wb_stall, i_wb_err;
input [(BUSW-1):0] i_wb_data;
//
// o_illegal will be true if this instruction was the result of a
// bus error (This is also part of the CPU interface)
output reg o_illegal;
//
`ifdef NOT_YET_READY
input wire i_mmu_ack, i_mmu_we;
input wire [(PAW-1):0] i_mmu_paddr;
`endif
 
// Fixed bus outputs: we read from the bus only, never write.
// Thus the output data is ... irrelevant and don't care. We set it
122,132 → 74,64
assign o_wb_we = 1'b0;
assign o_wb_data = 0;
 
`ifdef NOT_YET_READY
// These wires will be used below as part of the cache invalidation
// routine, should the MMU be used. This allows us to snoop on the
// physical side of the MMU bus, and invalidate any results should
// we need to do so.
wire mmu_inval;
wire [(PAW-CW-1):0] mmu_mskaddr;
`endif
`ifdef FORMAL
output wire [AW-1:0] f_pc_wb;
assign f_pc_wb = i_pc[AW+1:2];
`endif
 
 
wire r_v;
reg [(BUSW-1):0] cache [0:((1<<CW)-1)];
reg [(AW-CW-1):0] cache_tags [0:((1<<(LGLINES))-1)];
reg [((1<<(LGLINES))-1):0] valid_mask;
reg [(AW-CW-1):0] tags [0:((1<<(LGLINES))-1)];
reg [((1<<(LGLINES))-1):0] vmask;
 
reg r_v_from_pc, r_v_from_last, r_new_request;
reg rvsrc;
wire w_v_from_pc, w_v_from_last;
reg [(AW+1):0] lastpc;
reg [(CW-1):0] wraddr;
reg [(AW-1):0] lastpc;
reg [(CW-1):0] rdaddr;
reg [(AW-1):CW] tagvalipc, tagvallst;
wire [(AW-1):CW] tagval;
wire [(AW-1):LS] lasttag;
wire [(AW-1):PW] lasttag;
reg illegal_valid;
reg [(AW-1):LS] illegal_cache;
reg [(AW-1):PW] illegal_cache;
 
// initial o_i = 32'h76_00_00_00; // A NOOP instruction
// initial o_pc = 0;
reg [(BUSW-1):0] r_pc_cache, r_last_cache;
reg [(AW+1):0] r_pc, r_lastpc;
reg isrc;
reg [1:0] delay;
reg svmask, last_ack, needload, last_addr,
bus_abort;
reg [(LGLINES-1):0] saddr;
 
wire w_advance;
assign w_advance = (i_new_pc)||((r_v)&&(i_stall_n));
 
/////////////////////////////////////////////////
//
// Read the instruction from the cache
//
/////////////////////////////////////////////////
//
//
// We'll read two values from the cache, the first is the value if
// i_pc contains the address we want, the second is the value we'd read
// if lastpc (i.e. $past(i_pc)) was the address we wanted.
initial r_pc = 0;
initial r_lastpc = 0;
reg [(AW-1):0] r_pc, r_lastpc;
reg isrc;
always @(posedge i_clk)
begin
// We don't have the logic to select what to read, we must
// read both the value at i_pc and lastpc. cache[i_pc] is
// the value we return if the last cache request was in the
// cache on the last clock, cacne[lastpc] is the value we
// return if we've been stalled, weren't valid, or had to wait
// a clock or two.
// the value we return if the cache is good, cacne[lastpc] is
// the value we return if we've been stalled, weren't valid,
// or had to wait a clock or two. (Remember i_pc can't stop
// changing for a clock, so we need to keep track of the last
// one from before it stopped.)
//
// Part of the issue here is that i_pc is going to increment
// on this clock before we know whether or not the cache entry
// we've just read is valid. We can't stop this. Hence, we
// need to read from the lastpc entry.
//
//
// Here we keep track of which answer we want/need.
// If we reported a valid value to the CPU on the last clock,
// and the CPU wasn't stalled, then we want to use i_pc.
// Likewise if the CPU gave us an i_new_pc request, then we'll
// want to return the value associated with reading the cache
// at i_pc.
isrc <= w_advance;
// Here we keep track of which answer we want/need
isrc <= ((r_v)&&(i_stall_n))||(i_new_pc);
 
// Here we read both cache entries, at i_pc and lastpc.
// We'll select from among these cache possibilities on the
// next clock
r_pc_cache <= cache[i_pc[(CW+1):2]];
r_last_cache <= cache[lastpc[(CW+1):2]];
//
// Let's also register(delay) the r_pc and r_lastpc values
// for the next clock, so we can accurately report the address
// of the cache value we just looked up.
// Here we read both, and select which was write using isrc
// on the next clock.
r_pc_cache <= cache[i_pc[(CW-1):0]];
r_last_cache <= cache[lastpc[(CW-1):0]];
r_pc <= i_pc;
r_lastpc <= lastpc;
end
assign o_pc = (isrc) ? r_pc : r_lastpc;
assign o_i = (isrc) ? r_pc_cache : r_last_cache;
 
// On our next clock, our result with either be the registered i_pc
// value from the last clock (if isrc), otherwise r_lastpc
assign o_pc = (isrc) ? r_pc : r_lastpc;
// The same applies for determining what the next output instruction
// will be. We just read it in the last clock, now we just need to
// select between the two possibilities we just read.
assign o_insn= (isrc) ? r_pc_cache : r_last_cache;
 
 
/////////////////////////////////////////////////
//
// Read the tag value associated with this tcache line
//
/////////////////////////////////////////////////
//
//
 
//
// Read the tag value associated with this i_pc value
reg tagsrc;
always @(posedge i_clk)
// It may be possible to recover a clock once the cache line
// has been filled, but our prior attempt to do so has lead
// to a race condition, so we keep this logic simple.
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc))
tagsrc <= 1'b1;
else
tagsrc <= 1'b0;
initial tagvalipc = 0;
always @(posedge i_clk)
tagvalipc <= cache_tags[i_pc[(CW+1):LS+2]];
 
 
//
// Read the tag value associated with the lastpc value, from what
// i_pc was when we could not tell if this value was in our cache or
// not, or perhaps from when we determined that i was not in the cache.
tagvalipc <= tags[i_pc[(CW-1):PW]];
initial tagvallst = 0;
always @(posedge i_clk)
tagvallst <= cache_tags[lastpc[(CW+1):LS+2]];
tagvallst <= tags[lastpc[(CW-1):PW]];
assign tagval = (tagsrc)?tagvalipc : tagvallst;
 
// Select from between these two values on the next clock
assign tagval = (isrc)?tagvalipc : tagvallst;
 
// i_pc will only increment when everything else isn't stalled, thus
// we can set it without worrying about that. Doing this enables
// us to work in spite of stalls. For example, if the next address
255,724 → 139,170
// anyway.
initial lastpc = 0;
always @(posedge i_clk)
if (w_advance)
lastpc <= i_pc;
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc))
lastpc <= i_pc;
 
assign lasttag = lastpc[(AW+1):LS+2];
assign lasttag = lastpc[(AW-1):PW];
 
/////////////////////////////////////////////////
//
// Use the tag value to determine if our output instruction will be
// valid.
//
/////////////////////////////////////////////////
//
//
assign w_v_from_pc = ((i_pc[(AW+1):LS+2] == lasttag)
&&(tagval == i_pc[(AW+1):CW+2])
&&(valid_mask[i_pc[(CW+1):LS+2]]));
assign w_v_from_last = ((tagval == lastpc[(AW+1):CW+2])
&&(valid_mask[lastpc[(CW+1):LS+2]]));
wire w_v_from_pc, w_v_from_last;
assign w_v_from_pc = ((i_pc[(AW-1):PW] == lasttag)
&&(tagvalipc == i_pc[(AW-1):CW])
&&(vmask[i_pc[(CW-1):PW]]));
assign w_v_from_last = (
//(lastpc[(AW-1):PW] == lasttag)&&
(tagval == lastpc[(AW-1):CW])
&&(vmask[lastpc[(CW-1):PW]]));
 
reg [1:0] delay;
 
initial delay = 2'h3;
reg rvsrc;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache)||(w_advance))
begin
// Source our valid signal from i_pc
rvsrc <= 1'b1;
// Delay at least two clocks before declaring that
// we have an invalid result. This will give us time
// to check the tag value of what's in the cache.
delay <= 2'h2;
end else if ((!r_v)&&(!o_illegal)) begin
// If we aren't sourcing our valid signal from the
// i_pc clock, then we are sourcing it from the
// lastpc clock (one clock later). If r_v still
// isn't valid, we may need to make a bus request.
// Apply our timer and timeout.
rvsrc <= 1'b0;
 
// Delay is two once the bus starts, in case the
// bus transaction needs to be restarted upon completion
// This might happen if, after we start loading the
// cache, we discover a branch. The cache load will
// still complete, but the branches address needs to be
// the onen we jump to. This may mean we need to load
// the cache twice.
if (o_wb_cyc)
if ((i_rst)||(i_clear_cache)||(i_new_pc)||((r_v)&&(i_stall_n)))
begin
// r_v <= r_v_from_pc;
rvsrc <= 1'b1;
delay <= 2'h2;
else if (delay != 0)
delay <= delay + 2'b11; // i.e. delay -= 1;
end else begin
// After sourcing our output from i_pc, if it wasn't
// accepted, source the instruction from the lastpc valid
// determination instead
rvsrc <= 1'b0;
if (o_illegal)
delay <= 2'h2;
end
end else if (~r_v) begin // Otherwise, r_v was true and we were
// stalled, hence only if ~r_v
rvsrc <= 1'b0;
if (o_wb_cyc)
delay <= 2'h2;
else if (delay != 0)
delay <= delay + 2'b11; // i.e. delay -= 1;
end
reg r_v_from_pc, r_v_from_last;
always @(posedge i_clk)
r_v_from_pc <= w_v_from_pc;
always @(posedge i_clk)
r_v_from_last <= w_v_from_last;
 
wire w_invalidate_result;
assign w_invalidate_result = (i_reset)||(i_clear_cache);
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last));
assign o_v = (((rvsrc)?(r_v_from_pc):(r_v_from_last))
||((o_illegal)&&(~o_wb_cyc)))
&&(~i_new_pc)&&(~i_rst);
 
reg r_prior_illegal;
initial r_prior_illegal = 0;
initial r_new_request = 0;
initial r_v_from_pc = 0;
initial r_v_from_last = 0;
reg last_ack;
initial last_ack = 1'b0;
always @(posedge i_clk)
begin
r_new_request <= w_invalidate_result;
r_v_from_pc <= (w_v_from_pc)&&(!w_invalidate_result)
&&(!o_illegal);
r_v_from_last <= (w_v_from_last)&&(!w_invalidate_result);
last_ack <= (o_wb_cyc)&&(
(rdaddr[(PW-1):1]=={(PW-1){1'b1}})
&&((rdaddr[0])||(i_wb_ack)));
 
r_prior_illegal <= (o_wb_cyc)&&(i_wb_err);
end
 
// Now use rvsrc to determine which of the two valid flags we'll be
// using: r_v_from_pc (the i_pc address), or r_v_from_last (the lastpc
// address)
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last))&&(!r_new_request);
assign o_valid = (((rvsrc)?(r_v_from_pc):(r_v_from_last))
||(o_illegal))
&&(!i_new_pc)&&(!r_prior_illegal);
 
/////////////////////////////////////////////////
//
// If the instruction isn't in our cache, then we need to load
// a new cache line from memory.
//
/////////////////////////////////////////////////
//
//
reg needload;
initial needload = 1'b0;
always @(posedge i_clk)
if ((i_clear_cache)||(o_wb_cyc))
needload <= 1'b0;
else if ((w_advance)&&(!o_illegal))
needload <= 1'b0;
else
needload <= (delay==0)&&(!w_v_from_last)
// Prevent us from reloading an illegal address
// (i.e. one that produced a bus error) over and over
// and over again
&&((!illegal_valid)
||(lastpc[(AW+1):LS+2] != illegal_cache));
needload <= ((~r_v)&&(delay==0)
&&((tagvallst != lastpc[(AW-1):CW])
||(~vmask[lastpc[(CW-1):PW]]))
&&((~illegal_valid)
||(lastpc[(AW-1):PW] != illegal_cache)));
 
//
// Working from the rule that you want to keep complex logic out of
// a state machine if possible, we calculate a "last_stb" value one
// clock ahead of time. Hence, any time a request is accepted, if
// last_stb is also true we'll know we need to drop the strobe line,
// having finished requesting a complete cache line.
reg last_addr;
initial last_addr = 1'b0;
always @(posedge i_clk)
if (!o_wb_cyc)
last_addr <= 1'b0;
else if ((o_wb_addr[(LS-1):1] == {(LS-1){1'b1}})
&&((!i_wb_stall)|(o_wb_addr[0])))
last_addr <= 1'b1;
last_addr <= (o_wb_cyc)&&(o_wb_addr[(PW-1):1] == {(PW-1){1'b1}})
&&((~i_wb_stall)|(o_wb_addr[0]));
 
//
// "last_ack" is almost identical to last_addr, save that this
// will be true on the same clock as the last acknowledgment from the
// bus. The state machine logic will use this to determine when to
// get off the bus and end the wishbone bus cycle.
initial last_ack = 1'b0;
always @(posedge i_clk)
last_ack <= (o_wb_cyc)&&(
(wraddr[(LS-1):1]=={(LS-1){1'b1}})
&&((wraddr[0])||(i_wb_ack)));
 
initial bus_abort = 1'b0;
always @(posedge i_clk)
if (!o_wb_cyc)
bus_abort <= 1'b0;
else if ((i_clear_cache)||(i_new_pc))
bus_abort <= 1'b1;
 
//
// Here's the difficult piece of state machine logic--the part that
// determines o_wb_cyc and o_wb_stb. We've already moved most of the
// complicated logic off of this statemachine, calculating it one cycle
// early. As a result, this is a fairly easy piece of logic.
initial o_wb_cyc = 1'b0;
initial o_wb_stb = 1'b0;
initial o_wb_addr = {(AW){1'b0}};
initial rdaddr = 0;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end else if (o_wb_cyc)
begin
if (i_wb_err)
if ((i_rst)||(i_clear_cache))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
else if ((o_wb_stb)&&(!i_wb_stall)&&(last_addr))
o_wb_stb <= 1'b0;
end else if (o_wb_cyc)
begin
if (i_wb_err)
o_wb_stb <= 1'b0;
else if ((o_wb_stb)&&(~i_wb_stall)&&(last_addr))
o_wb_stb <= 1'b0;
 
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
o_wb_cyc <= 1'b0;
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
o_wb_cyc <= 1'b0;
 
end else if ((needload)&&(!i_new_pc))
begin
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end
// else if (rdaddr[(PW-1):1] == {(PW-1){1'b1}})
// tags[lastpc[(CW-1):PW]] <= lastpc[(AW-1):CW];
 
// If we are reading from this cache line, then once we get the first
// acknowledgement, this cache line has the new tag value
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
cache_tags[o_wb_addr[(CW-1):LS]] <= o_wb_addr[(AW-1):CW];
end else if (needload)
begin
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end
 
 
// On each acknowledgment, increment the address we use to write into
// our cache. Hence, this is the write address into our cache block
// RAM.
initial wraddr = 0;
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack)&&(!last_ack))
wraddr <= wraddr + 1'b1;
else if (!o_wb_cyc)
wraddr <= { lastpc[(CW+1):LS+2], {(LS){1'b0}} };
 
//
// The wishbone request address. This has meaning anytime o_wb_stb
// is active, and needs to be incremented any time an address is
// accepted--WITH THE EXCEPTION OF THE LAST ADDRESS. We need to keep
// this steady for that last address, unless the last address returns
// a bus error. In that case, the whole cache line will be marked as
// invalid--but we'll need the value of this register to know how
// to do that propertly.
initial o_wb_addr = {(AW){1'b0}};
if (o_wb_cyc) // &&(i_wb_ack)
tags[o_wb_addr[(CW-1):PW]] <= o_wb_addr[(AW-1):CW];
always @(posedge i_clk)
if ((o_wb_stb)&&(!i_wb_stall)&&(!last_addr))
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1;
else if (!o_wb_cyc)
o_wb_addr <= { lastpc[(AW+1):LS+2], {(LS){1'b0}} };
if ((o_wb_cyc)&&(i_wb_ack))
rdaddr <= rdaddr + 1;
else if (~o_wb_cyc)
rdaddr <= { lastpc[(CW-1):PW], {(PW){1'b0}} };
always @(posedge i_clk)
if ((o_wb_stb)&&(~i_wb_stall)&&(~last_addr))
o_wb_addr[(PW-1):0] <= o_wb_addr[(PW-1):0]+1;
else if (~o_wb_cyc)
o_wb_addr <= { lastpc[(AW-1):PW], {(PW){1'b0}} };
 
// Since it is impossible to initialize an array, our cache will start
// up cache uninitialized. We'll also never get a valid ack without
// cyc being active, although we might get one on the clock after
// cyc was active--so we need to test and gate on whether o_wb_cyc
// is true.
//
// wraddr will advance forward on every clock cycle where ack is true,
// hence we don't need to check i_wb_ack here. This will work because
// multiple writes to the same address, ending with a valid write,
// will always yield the valid write's value only after our bus cycle
// is over.
// Can't initialize an array, so leave cache uninitialized
// We'll also never get an ack without sys being active, so skip
// that check. Or rather, let's just use o_wb_cyc instead. This
// will work because multiple writes to the same address, ending with
// a valid write, aren't a problem.
always @(posedge i_clk)
if (o_wb_cyc)
cache[wraddr] <= i_wb_data;
if (o_wb_cyc) // &&(i_wb_ack)
cache[rdaddr] <= i_wb_data;
 
// VMask ... is a section loaded?
// Note "svmask". It's purpose is to delay the valid_mask setting by
// one clock, so that we can insure the right value of the cache is
// loaded before declaring that the cache line is valid. Without
// this, the cache line would get read, and the instruction would
// read from the last cache line.
initial valid_mask = 0;
// Note "svmask". It's purpose is to delay the vmask setting by one
// clock, so that we can insure the right value of the cache is loaded
// before declaring that the cache line is valid. Without this, the
// cache line would get read, and the instruction would read from the
// last cache line.
reg svmask;
initial vmask = 0;
initial svmask = 1'b0;
reg [(LGLINES-1):0] saddr;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache))
begin
valid_mask <= 0;
svmask<= 1'b0;
end else begin
svmask <= ((o_wb_cyc)&&(i_wb_ack)&&(last_ack)&&(!bus_abort));
 
if (svmask)
valid_mask[saddr] <= (!bus_abort);
if ((!o_wb_cyc)&&(needload))
valid_mask[lastpc[(CW+1):LS+2]] <= 1'b0;
`ifdef NOT_YET_READY
//
// MMU code
//
if (mmu_inval)
valid_mask[mmu_mskadr] <= 1'b0;
`endif
end
 
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
saddr <= wraddr[(CW-1):LS];
// MMU code
//
//
`ifdef NOT_YET_READY
parameter [0:0] USE_MMU = 1'b1;
generate if (USE_MMU)
begin
reg [(PAW-CW-1):0] ptag [0:((1<<(LGLINES))-1)];
reg mmu_pre_inval, r_mmu_inval;
reg [(PAW-CW-1):0] mmu_pre_tag, mmu_pre_padr;
reg [(CW-LS-1):0] r_mmu_mskadr;
 
initial mmu_pre_inval = 0;
initial mmu_pre_tag = 0;
initial mmu_pre_padr = 0;
initial mmu_pre2_inval = 0;
initial mmu_pre2_mskadr = 0;
 
always @(posedge i_clk)
if ((o_wb_cyc)&&(!last_addr)&&(i_mmu_ack))
ptag[i_mmu_paddr[(CW-1):LS]] <= i_mmu_paddr[(PAW-1):CW];
 
always @(posedge i_clk)
if (i_reset)
if ((i_rst)||(i_clear_cache))
begin
mmu_pre_inval <= 0;
r_mmu_inval <= 0;
end else begin
mmu_pre_inval <= (i_mmu_ack)&&(i_mmu_we);
r_mmu_inval <= (mmu_pre_inval)&&(mmu_pre_inval)
&&(mmu_pre_tag == mmu_pre_paddr);
vmask <= 0;
svmask<= 1'b0;
end
 
always @(posedge i_clk)
mmu_pre_tag <= ptag[i_mmu_paddr[(CW-1):LS]];
 
always @(posedge i_clk)
begin
mmu_pre_padr <= i_mmu_paddr[(PAW-1):CW];
r_mmu_mskadr <= mmu_pre_padr[(PAW-LS-1):(CW-LS)];
else begin
svmask <= ((o_wb_cyc)&&(i_wb_ack)&&(last_ack));
if (svmask)
vmask[saddr] <= 1'b1;
if ((~o_wb_cyc)&&(needload))
vmask[lastpc[(CW-1):PW]] <= 1'b0;
end
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
saddr <= rdaddr[(CW-1):PW];
 
assign mmu_inval = r_mmu_inval;
assign mmu_mskadr = r_mmu_mskadr;
end else begin
assign mmu_inval = 0;
assign mmu_mskadr = 0;
end endgenerate
`endif
 
/////////////////////////////////////////////////
//
// Handle bus errors here. If a bus read request
// returns an error, then we'll mark the entire
// line as having a (valid) illegal value.
//
/////////////////////////////////////////////////
//
//
//
//
initial illegal_cache = 0;
initial illegal_valid = 0;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache))
begin
illegal_cache <= 0;
illegal_valid <= 0;
end else if ((o_wb_cyc)&&(i_wb_err))
begin
illegal_cache <= o_wb_addr[(AW-1):LS];
illegal_valid <= 1'b1;
end
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(i_clear_cache)||(i_new_pc))
o_illegal <= 1'b0;
else if ((o_illegal)||((o_valid)&&(i_stall_n)))
o_illegal <= 1'b0;
else
o_illegal <= (illegal_valid)
&&(illegal_cache == lastpc[(AW+1):LS+2]);
 
`ifdef FORMAL
//
//
// Generic setup
//
//
`ifdef PFCACHE
`define ASSUME assume
`else
`define ASSUME assert
`define STEP_CLOCK
`endif
 
// Keep track of a flag telling us whether or not $past()
// will return valid results
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid = 1'b1;
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
 
/////////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
/////////////////////////////////////////////////
 
 
`ifdef PFCACHE
//
// Assume that resets, new-pc commands, and clear-cache commands
// are never more than pulses--one clock wide at most.
//
// It may be that the CPU treats us differently. We'll only assume
// our solver to this here.
always @(posedge i_clk)
if (!f_past_valid)
begin
if ($past(i_reset))
assume(!i_reset);
if ($past(i_new_pc))
assume(!i_new_pc);
if ($past(i_clear_cache))
assume(!i_clear_cache);
end
`endif
 
//
// Assume we start from a reset condition
initial `ASSUME(i_reset);
 
// Assume that any reset is either accompanied by a new address,
// or a new address immediately follows it.
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
`ASSUME(i_new_pc);
//
// Let's make some assumptions about how long it takes our
// phantom bus and phantom CPU to respond.
//
// These delays need to be long enough to flush out any potential
// errors, yet still short enough that the formal method doesn't
// take forever to solve.
//
localparam F_CPU_DELAY = 4;
reg [4:0] f_cpu_delay;
 
// Now, let's repeat this bit but now looking at the delay the CPU
// takes to accept an instruction.
always @(posedge i_clk)
// If no instruction is ready, then keep our counter at zero
if ((!o_valid)||(i_stall_n))
f_cpu_delay <= 0;
else
// Otherwise, count the clocks the CPU takes to respond
f_cpu_delay <= f_cpu_delay + 1'b1;
 
`ifdef PFCACHE
always @(posedge i_clk)
assume(f_cpu_delay < F_CPU_DELAY);
`endif
 
always @(posedge i_clk)
if ($past(i_reset || i_clear_cache))
assume(i_stall_n);
else if ($past(i_stall_n && !o_valid))
assume(i_stall_n);
else if (i_new_pc)
assume(i_stall_n);
 
/////////////////////////////////////////////////
//
//
// Assertions about our outputs
//
//
/////////////////////////////////////////////////
 
localparam F_LGDEPTH=LS+1;
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
 
fwb_master #(.AW(AW), .DW(BUSW), .F_LGDEPTH(F_LGDEPTH),
.F_MAX_STALL(2), .F_MAX_ACK_DELAY(3),
.F_MAX_REQUESTS(1<<LS), .F_OPT_SOURCE(1),
.F_OPT_RMW_BUS_OPTION(0),
.F_OPT_DISCONTINUOUS(0))
f_wbm(i_clk, i_reset,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
// writes are also illegal for a prefetch.
always @(posedge i_clk)
if (o_wb_stb)
assert(!o_wb_we);
 
always @(posedge i_clk)
begin
assert(f_nreqs <= (1<<LS));
if ((o_wb_cyc)&&(o_wb_stb))
assert(f_nreqs == o_wb_addr[(LS-1):0]);
if ((f_past_valid)&&($past(o_wb_cyc))
&&(!o_wb_stb)&&(!$past(i_wb_err || i_reset || i_clear_cache)))
assert(f_nreqs == (1<<LS));
end
 
always @(posedge i_clk)
if (f_past_valid)
begin
if ((!o_wb_cyc)&&($past(o_wb_cyc))&&(!$past(i_reset))
&&(!$past(i_clear_cache)) &&(!$past(i_wb_err)))
assert(f_nacks == (1<<LS));
else if (o_wb_cyc)
assert(f_nacks[(LS-1):0] == wraddr[(LS-1):0]);
end
 
// The last-ack line
always @(posedge i_clk)
if (o_wb_cyc)
assert(last_ack == (f_nacks == ((1<<LS)-1)));
 
// The valid line for whats being read
always @(posedge i_clk)
if (o_wb_cyc)
assert(!valid_mask[o_wb_addr[CW-1:LS]]);
 
always @(posedge i_clk)
if ((illegal_valid)&&(o_wb_cyc))
assert(o_wb_addr[AW-1:LS] != illegal_cache);
 
reg [((1<<(LGLINES))-1):0] f_past_valid_mask;
initial f_past_valid_mask = 0;
always @(posedge i_clk)
f_past_valid_mask = valid_mask;
 
always @(posedge i_clk)
if ((o_valid)&&($past(!o_valid || !o_illegal)))
assert((!o_wb_cyc)
||(o_wb_addr[AW-1:LS] != o_pc[AW+1:LS+2]));
always @(posedge i_clk)
if (illegal_valid)
begin
assert((!o_wb_cyc)
||(o_wb_addr[AW-1:LS] != illegal_cache));
 
// The illegal cache line should never be valid within our
// cache
assert((!valid_mask[illegal_cache[CW-1:LS]])
||(cache_tags[illegal_cache[CW-1:LS]]
!= illegal_cache[AW-1:CW]));
end
 
/////////////////////////////////////////////////////
//
//
// Assertions about our return responses to the CPU
//
//
/////////////////////////////////////////////////////
 
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_wb_cyc)))
assert(o_wb_addr[(AW-1):LS] == $past(o_wb_addr[(AW-1):LS]));
 
// Consider it invalid to present the CPU with the same instruction
// twice in a row.
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n))&&(o_valid))
assert(o_pc != $past(o_pc));
 
always @(posedge i_clk)
if (o_valid)
begin
if (!o_illegal)
if ((i_rst)||(i_clear_cache))
begin
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]);
assert(valid_mask[o_pc[(CW+1):LS+2]] || (o_illegal));
assert(o_insn == cache[o_pc[(CW+1):2]]);
assert((!illegal_valid)
||((illegal_cache != o_pc[(AW+1):LS+2])));
illegal_cache <= 0;
illegal_valid <= 0;
end else if ((o_wb_cyc)&&(i_wb_err))
begin
illegal_cache <= o_wb_addr[(AW-1):PW];
illegal_valid <= 1'b1;
end
 
assert(o_illegal == ($past(illegal_valid)
&&($past(illegal_cache)== o_pc[(AW+1):LS+2])));
end
 
always @(*)
begin
`ASSUME(i_pc[1:0] == 2'b00);
assert(o_pc[1:0] == 2'b00);
assert(r_pc[1:0] == 2'b00);
assert(r_lastpc[1:0] == 2'b00);
end
 
reg [(AW+1):0] f_next_pc;
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
begin
if (isrc)
assert(lastpc == r_pc);
if ((i_rst)||(i_clear_cache)||(o_wb_cyc))
o_illegal <= 1'b0;
else
assert(lastpc + 4== r_pc);
end
o_illegal <= (illegal_valid)
&&(illegal_cache == i_pc[(AW-1):PW]);
 
always @(posedge i_clk)
if (i_new_pc)
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
else if ((i_stall_n)&&(r_v))
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
always @(*)
if (!i_new_pc)
`ASSUME(i_pc == f_next_pc);
 
always @(posedge i_clk)
if ((f_past_valid)&&(o_valid)&&($past(o_valid))
&&(!$past(i_reset))
&&(!$past(i_new_pc))
&&(!$past(i_stall_n))
&&(!o_illegal))
begin
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]);
end
 
//
// If an instruction is accepted, we should *always* move on to another
// instruction. The only exception is following an i_new_pc (or
// other invalidator), at which point the next instruction should
// be invalid.
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n)))
begin
// Should always advance the instruction
assert((!o_valid)||(o_pc != $past(o_pc)));
end
 
//
// Once an instruction becomes valid, it should never become invalid
// unless there's been a request for a new instruction.
always @(posedge i_clk)
if ((f_past_valid)&&($past(!i_reset && !i_clear_cache && !i_new_pc))
&&($past(o_valid && !i_stall_n))
&&(!i_new_pc))
begin
if ((!$past(o_illegal))&&(!$past(o_wb_cyc && i_wb_err)))
begin
assert(o_valid);
assert($stable(o_illegal));
assert($stable(o_insn));
end else
assert((o_illegal)||(!o_valid));
end
`ifdef PFCACHE
/////////////////////////////////////////////////////
//
//
// Assertions associated with a response to a known
// address request
//
//
/////////////////////////////////////////////////////
 
 
(* anyconst *) reg [AW:0] f_const_addr;
(* anyconst *) reg [BUSW-1:0] f_const_insn;
 
wire f_this_pc, f_this_insn, f_this_data, f_this_line,
f_this_ack, f_this_tag; // f_this_addr;
assign f_this_pc = (o_pc == { f_const_addr[AW-1:0], 2'b00 });
// assign f_this_addr = (o_wb_addr == f_const_addr[AW-1:0] );
assign f_this_insn = (o_insn == f_const_insn);
assign f_this_data = (i_wb_data == f_const_insn);
assign f_this_line = (o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS]);
assign f_this_ack = (f_this_line)&&(f_nacks == f_const_addr[LS-1:0]);
assign f_this_tag = (tagval == f_const_addr[AW-1:CW]);
 
always @(posedge i_clk)
if ((o_valid)&&(f_this_pc)&&(!$past(o_illegal)))
begin
assert(o_illegal == f_const_addr[AW]);
if (!o_illegal)
begin
assert(f_this_insn);
assert(f_this_tag);
end
end
 
always @(*)
if ((valid_mask[f_const_addr[CW-1:LS]])
&&(cache_tags[f_const_addr[(CW-1):LS]]==f_const_addr[AW-1:CW]))
assert(f_const_insn == cache[f_const_addr[CW-1:0]]);
else if ((o_wb_cyc)&&(o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS])
&&(f_nacks > f_const_addr[LS-1:0]))
begin
assert(f_const_insn == cache[f_const_addr[CW-1:0]]);
end
 
always @(*)
if (o_wb_cyc)
assert(wraddr[CW-1:LS] == o_wb_addr[CW-1:LS]);
 
always @(*)
if (!f_const_addr[AW])
assert((!illegal_valid)
||(illegal_cache != f_const_addr[AW-1:LS]));
else
assert((cache_tags[f_const_addr[CW-1:LS]]!=f_const_addr[AW-1:CW])
||(!valid_mask[f_const_addr[CW-1:LS]]));
 
always @(*)
if ((f_this_line)&&(o_wb_cyc))
begin
if (f_const_addr[AW])
assume(!i_wb_ack);
else
assume(!i_wb_err);
 
if ((f_this_ack)&&(i_wb_ack))
assume(f_this_data);
end
 
always @(*)
if ((f_this_line)&&(!f_const_addr[AW]))
assume(!i_wb_err);
 
always @(*)
if (!f_const_addr[AW])
assume((!valid_mask[f_const_addr[CW-1:LS]])
||(cache_tags[f_const_addr[CW-1:LS]] != f_const_addr[AW-1:CW]));
`endif
 
//
//
// Cover properties
//
//
reg f_valid_legal;
always @(*)
f_valid_legal = o_valid && (!o_illegal);
always @(posedge i_clk) // Trace 0
cover((o_valid)&&( o_illegal));
always @(posedge i_clk) // Trace 1
cover(f_valid_legal);
always @(posedge i_clk) // Trace 2
cover((f_valid_legal)
&&($past(!o_valid && !i_new_pc))
&&($past(i_new_pc,2)));
always @(posedge i_clk) // Trace 3
cover((f_valid_legal)&&($past(i_stall_n))&&($past(i_new_pc)));
always @(posedge i_clk) // Trace 4
cover((f_valid_legal)&&($past(f_valid_legal && i_stall_n)));
always @(posedge i_clk) // Trace 5
cover((f_valid_legal)
&&($past(f_valid_legal && i_stall_n))
&&($past(f_valid_legal && i_stall_n,2))
&&($past(f_valid_legal && i_stall_n,3)));
always @(posedge i_clk) // Trace 6
cover((f_valid_legal)
&&($past(f_valid_legal && i_stall_n))
&&($past(f_valid_legal && i_stall_n,2))
&&($past(!o_illegal && i_stall_n && i_new_pc,3))
&&($past(f_valid_legal && i_stall_n,4))
&&($past(f_valid_legal && i_stall_n,5))
&&($past(f_valid_legal && i_stall_n,6)));
 
`endif // FORMAL
endmodule
/pipefetch.v
30,7 → 30,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015,2017,2019 Gisselquist Technology, LLC
// Copyright (C) 2015,2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
54,8 → 54,6
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module pipefetch(i_clk, i_rst, i_new_pc, i_clear_cache, i_stall_n, i_pc,
o_i, o_pc, o_v,
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
64,9 → 62,9
parameter RESET_ADDRESS=32'h0010_0000,
LGCACHELEN = 6, ADDRESS_WIDTH=24,
CACHELEN=(1<<LGCACHELEN), BUSW=32, AW=ADDRESS_WIDTH;
input wire i_clk, i_rst, i_new_pc,
input i_clk, i_rst, i_new_pc,
i_clear_cache, i_stall_n;
input wire [(AW-1):0] i_pc;
input [(AW-1):0] i_pc;
output reg [(BUSW-1):0] o_i;
output reg [(AW-1):0] o_pc;
output wire o_v;
76,11 → 74,11
output reg [(AW-1):0] o_wb_addr;
output wire [(BUSW-1):0] o_wb_data;
//
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(BUSW-1):0] i_wb_data;
input i_wb_ack, i_wb_stall, i_wb_err;
input [(BUSW-1):0] i_wb_data;
//
// Is the (data) memory unit also requesting access to the bus?
input wire i_wb_request;
input i_wb_request;
output wire o_illegal;
 
// Fixed bus outputs: we read from the bus only, never write.
122,7 → 120,7
+(3<<(LGCACHELEN-2)))
&&(|r_nvalid[(LGCACHELEN):(LGCACHELEN-1)]);
 
initial r_cache_base = RESET_ADDRESS[(AW+1):2];
initial r_cache_base = RESET_ADDRESS;
always @(posedge i_clk)
begin
if ((i_rst)||(i_clear_cache)||((o_wb_cyc)&&(i_wb_err)))
303,4 → 301,5
 
assign o_illegal = (o_pc == ill_address)&&(~i_rst)&&(~i_new_pc)&&(~i_clear_cache);
 
 
endmodule
/pipemem.v
17,7 → 17,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
41,35 → 41,22
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module pipemem(i_clk, i_reset, i_pipe_stb, i_lock,
module pipemem(i_clk, i_rst, i_pipe_stb, i_lock,
i_op, i_addr, i_data, i_oreg,
o_busy, o_pipe_stalled, o_valid, o_err, o_wreg, o_result,
o_wb_cyc_gbl, o_wb_cyc_lcl,
o_wb_stb_gbl, o_wb_stb_lcl,
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data
`ifdef FORMAL
, f_nreqs, f_nacks, f_outstanding, f_pc
`endif
);
parameter ADDRESS_WIDTH=30;
parameter [0:0] IMPLEMENT_LOCK=1'b1,
WITH_LOCAL_BUS=1'b1,
OPT_ZERO_ON_IDLE=1'b0,
// OPT_ALIGNMENT_ERR
OPT_ALIGNMENT_ERR=1'b0;
localparam AW=ADDRESS_WIDTH,
FLN=4;
parameter [(FLN-1):0] OPT_MAXDEPTH=4'hd;
input wire i_clk, i_reset;
input wire i_pipe_stb, i_lock;
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data);
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst;
input i_pipe_stb, i_lock;
// CPU interface
input wire [2:0] i_op;
input wire [31:0] i_addr;
input wire [31:0] i_data;
input wire [4:0] i_oreg;
input [2:0] i_op;
input [31:0] i_addr;
input [31:0] i_data;
input [4:0] i_oreg;
// CPU outputs
output wire o_busy;
output wire o_pipe_stalled;
86,201 → 73,134
output reg [31:0] o_wb_data;
output reg [3:0] o_wb_sel;
// Wishbone inputs
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [31:0] i_wb_data;
// Formal
parameter F_LGDEPTH=5;
`ifdef FORMAL
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
output reg f_pc;
`endif
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
 
 
reg cyc;
reg r_wb_cyc_gbl, r_wb_cyc_lcl, fifo_full;
reg [(FLN-1):0] rdaddr, wraddr;
wire [(FLN-1):0] nxt_rdaddr, fifo_fill;
reg [(3+5-1):0] fifo_oreg [0:15];
reg fifo_gie;
reg cyc;
reg r_wb_cyc_gbl, r_wb_cyc_lcl;
reg [3:0] rdaddr, wraddr;
wire [3:0] nxt_rdaddr;
reg [(4+5-1):0] fifo_oreg [0:15];
initial rdaddr = 0;
initial wraddr = 0;
 
reg misaligned;
 
always @(*)
if (OPT_ALIGNMENT_ERR)
begin
casez({ i_op[2:1], i_addr[1:0] })
4'b01?1: misaligned = i_pipe_stb;
4'b0110: misaligned = i_pipe_stb;
4'b10?1: misaligned = i_pipe_stb;
default: misaligned = i_pipe_stb;
endcase
end else
misaligned = 1'b0;
 
always @(posedge i_clk)
fifo_oreg[wraddr] <= { i_oreg[3:0], i_op[2:1], i_addr[1:0] };
fifo_oreg[wraddr] <= { i_oreg, i_op[2:1], i_addr[1:0] };
 
always @(posedge i_clk)
if (i_pipe_stb)
fifo_gie <= i_oreg[4];
 
initial wraddr = 0;
always @(posedge i_clk)
if (i_reset)
wraddr <= 0;
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned)))
if ((i_rst)||(i_wb_err))
wraddr <= 0;
else if (i_pipe_stb)
wraddr <= wraddr + 1'b1;
 
initial rdaddr = 0;
else if (i_pipe_stb)
wraddr <= wraddr + 1'b1;
always @(posedge i_clk)
if (i_reset)
rdaddr <= 0;
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned)))
rdaddr <= 0;
else if ((i_wb_ack)&&(cyc))
rdaddr <= rdaddr + 1'b1;
 
assign fifo_fill = wraddr - rdaddr;
 
initial fifo_full = 0;
always @(posedge i_clk)
if (i_reset)
fifo_full <= 0;
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned)))
fifo_full <= 0;
else if (i_pipe_stb)
fifo_full <= (fifo_fill >= OPT_MAXDEPTH-1);
else
fifo_full <= (fifo_fill >= OPT_MAXDEPTH);
 
if ((i_rst)||(i_wb_err))
rdaddr <= 0;
else if ((i_wb_ack)&&(cyc))
rdaddr <= rdaddr + 1'b1;
assign nxt_rdaddr = rdaddr + 1'b1;
 
wire gbl_stb, lcl_stb, lcl_bus;
assign lcl_bus = (i_addr[31:24]==8'hff)&&(WITH_LOCAL_BUS);
assign lcl_stb = (lcl_bus)&&(!misaligned);
assign gbl_stb = ((!lcl_bus)||(!WITH_LOCAL_BUS))&&(!misaligned);
wire gbl_stb, lcl_stb;
assign lcl_stb = (i_addr[31:24]==8'hff);
assign gbl_stb = (~lcl_stb);
//= ((i_addr[31:8]!=24'hc00000)||(i_addr[7:5]!=3'h0));
 
initial cyc = 0;
initial r_wb_cyc_lcl = 0;
initial r_wb_cyc_gbl = 0;
initial o_wb_stb_lcl = 0;
initial o_wb_stb_gbl = 0;
always @(posedge i_clk)
if (i_reset)
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
cyc <= 1'b0;
end else if (cyc)
begin
if (((!i_wb_stall)&&(!i_pipe_stb)&&(!misaligned))
||(i_wb_err))
if (i_rst)
begin
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
end
 
if (((i_wb_ack)&&(nxt_rdaddr == wraddr)
&&((!i_pipe_stb)||(misaligned)))
||(i_wb_err))
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
cyc <= 1'b0;
end else if (cyc)
begin
if ((~i_wb_stall)&&(~i_pipe_stb))
begin
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
// end else if ((i_pipe_stb)&&(~i_wb_stall))
// begin
// o_wb_addr <= i_addr[(AW-1):0];
// o_wb_data <= i_data;
end
 
if (((i_wb_ack)&&(nxt_rdaddr == wraddr))||(i_wb_err))
begin
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
cyc <= 1'b0;
end
end else if (i_pipe_stb) // New memory operation
begin // Grab the wishbone
r_wb_cyc_lcl <= lcl_stb;
r_wb_cyc_gbl <= gbl_stb;
o_wb_stb_lcl <= lcl_stb;
o_wb_stb_gbl <= gbl_stb;
cyc <= 1'b1;
// o_wb_addr <= i_addr[(AW-1):0];
// o_wb_data <= i_data;
// o_wb_we <= i_op
end
end else if (i_pipe_stb) // New memory operation
begin // Grab the wishbone
r_wb_cyc_lcl <= lcl_stb;
r_wb_cyc_gbl <= gbl_stb;
o_wb_stb_lcl <= lcl_stb;
o_wb_stb_gbl <= gbl_stb;
cyc <= (!misaligned);
end
 
always @(posedge i_clk)
if ((!cyc)||(!i_wb_stall))
begin
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb))
o_wb_addr <= 0;
else
if ((!cyc)||(!i_wb_stall))
begin
o_wb_addr <= i_addr[(AW+1):2];
if (!i_op[0]) // Always select everything on reads
o_wb_sel <= 4'b1111; // Op is even
else casez({ i_op[2:1], i_addr[1:0] })
4'b100?: o_wb_sel <= 4'b1100; // Op = 5
4'b101?: o_wb_sel <= 4'b0011; // Op = 5
4'b1100: o_wb_sel <= 4'b1000; // Op = 5
4'b1101: o_wb_sel <= 4'b0100; // Op = 7
4'b1110: o_wb_sel <= 4'b0010; // Op = 7
4'b1111: o_wb_sel <= 4'b0001; // Op = 7
default: o_wb_sel <= 4'b1111; // Op = 7
endcase
 
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb))
o_wb_sel <= 4'b0000;
else casez({ i_op[2:1], i_addr[1:0] })
4'b100?: o_wb_sel <= 4'b1100; // Op = 5
4'b101?: o_wb_sel <= 4'b0011; // Op = 5
4'b1100: o_wb_sel <= 4'b1000; // Op = 5
4'b1101: o_wb_sel <= 4'b0100; // Op = 7
4'b1110: o_wb_sel <= 4'b0010; // Op = 7
4'b1111: o_wb_sel <= 4'b0001; // Op = 7
default: o_wb_sel <= 4'b1111; // Op = 7
endcase
casez({ i_op[2:1], i_addr[1:0] })
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 };
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] };
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 };
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 };
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 };
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] };
default: o_wb_data <= i_data;
endcase
 
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb))
o_wb_data <= 0;
else casez({ i_op[2:1], i_addr[1:0] })
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 };
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] };
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 };
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 };
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 };
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] };
default: o_wb_data <= i_data;
endcase
end
end
 
always @(posedge i_clk)
if ((i_pipe_stb)&&(!cyc))
o_wb_we <= i_op[0];
else if ((OPT_ZERO_ON_IDLE)&&(!cyc))
o_wb_we <= 1'b0;
if ((i_pipe_stb)&&(~cyc))
o_wb_we <= i_op[0];
 
initial o_valid = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_valid <= 1'b0;
else
o_valid <= (cyc)&&(i_wb_ack)&&(!o_wb_we);
 
o_valid <= (cyc)&&(i_wb_ack)&&(~o_wb_we);
initial o_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_err <= 1'b0;
else
o_err <= ((cyc)&&(i_wb_err))||((i_pipe_stb)&&(misaligned));
o_err <= (cyc)&&(i_wb_err);
assign o_busy = cyc;
 
wire [7:0] w_wreg;
wire [8:0] w_wreg;
assign w_wreg = fifo_oreg[rdaddr];
always @(posedge i_clk)
o_wreg <= { fifo_gie, w_wreg[7:4] };
o_wreg <= w_wreg[8:4];
always @(posedge i_clk)
if ((OPT_ZERO_ON_IDLE)&&((!cyc)||((!i_wb_ack)&&(!i_wb_err))))
o_result <= 0;
else begin
casez(w_wreg[3:0])
4'b1100: o_result <= { 24'h00, i_wb_data[31:24] };
4'b1101: o_result <= { 24'h00, i_wb_data[23:16] };
4'b1110: o_result <= { 24'h00, i_wb_data[15: 8] };
4'b1111: o_result <= { 24'h00, i_wb_data[ 7: 0] };
4'b100?: o_result <= { 16'h00, i_wb_data[31:16] };
4'b101?: o_result <= { 16'h00, i_wb_data[15: 0] };
default: o_result <= i_wb_data[31:0];
4'b1100: o_result = { 24'h00, i_wb_data[31:24] };
4'b1101: o_result = { 24'h00, i_wb_data[23:16] };
4'b1110: o_result = { 24'h00, i_wb_data[15: 8] };
4'b1111: o_result = { 24'h00, i_wb_data[ 7: 0] };
4'b100?: o_result = { 16'h00, i_wb_data[31:16] };
4'b101?: o_result = { 16'h00, i_wb_data[15: 0] };
default: o_result = i_wb_data[31:0];
endcase
end
 
assign o_pipe_stalled = ((cyc)&&(fifo_full))||((cyc)
&&((i_wb_stall)||((!o_wb_stb_lcl)&&(!o_wb_stb_gbl))));
assign o_pipe_stalled = (cyc)
&&((i_wb_stall)||((~o_wb_stb_lcl)&&(~o_wb_stb_gbl)));
 
generate
if (IMPLEMENT_LOCK != 0)
290,12 → 210,7
initial lock_gbl = 1'b0;
initial lock_lcl = 1'b0;
always @(posedge i_clk)
if ((i_reset)||((i_wb_err)&&(cyc))
||((i_pipe_stb)&&(misaligned)))
begin
lock_gbl <= 1'b0;
lock_lcl <= 1'b0;
end else begin
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
end
302,319 → 217,10
 
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
 
end else begin
assign o_wb_cyc_gbl = (r_wb_cyc_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl);
end endgenerate
 
// Make verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = i_lock;
// verilator lint_on UNUSED
 
`ifdef FORMAL
`define ASSERT assert
`ifdef PIPEMEM
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
reg f_past_valid;
initial f_past_valid = 0;
always @(posedge i_clk)
f_past_valid = 1'b1;
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
 
initial `ASSUME( i_reset);
initial `ASSUME(!i_pipe_stb);
 
wire f_cyc, f_stb;
assign f_cyc = cyc;
assign f_stb = (o_wb_stb_gbl)||(o_wb_stb_lcl);
 
`ifdef PIPEMEM
`define MASTER fwb_master
`else
`define MASTER fwb_counter
`endif
`MASTER #(.AW(AW), .F_LGDEPTH(F_LGDEPTH),
// .F_MAX_REQUESTS(14), // Not quite true, can do more
.F_OPT_RMW_BUS_OPTION(IMPLEMENT_LOCK),
.F_OPT_DISCONTINUOUS(IMPLEMENT_LOCK))
fwb(i_clk, i_reset,
cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel,
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err,
f_nreqs, f_nacks, f_outstanding);
 
 
//
// Assumptions about inputs
//
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
`ASSERT(!o_err);
`ASSERT(!o_busy);
`ASSERT(!o_pipe_stalled);
`ASSERT(!o_valid);
end
 
always @(posedge i_clk)
if (o_pipe_stalled)
`ASSUME(!i_pipe_stb);
 
// On any pipe request, the new address is the same or plus one
always @(posedge i_clk)
if ((f_past_valid)&&(f_cyc)&&(!i_wb_stall)&&(i_pipe_stb))
begin
`ASSUME( (i_addr[(AW+1):2] == o_wb_addr)
||(i_addr[(AW+1):2] == o_wb_addr+1));
`ASSUME(i_op[0] == o_wb_we);
end
 
always @(posedge i_clk)
if ((r_wb_cyc_gbl)&&(i_pipe_stb))
`ASSUME(gbl_stb);
 
always @(posedge i_clk)
if ((r_wb_cyc_lcl)&&(i_pipe_stb))
`ASSUME(lcl_stb);
 
// If stb is false, then either lock is on or there are no more STB's
always @(posedge i_clk)
if ((f_cyc)&&(!f_stb))
`ASSUME((i_lock)||(!i_pipe_stb));
 
//always @(posedge i_clk)
// if ((f_past_valid)&&($past(f_cyc))&&(!$past(i_lock)))
// `ASSUME(!i_lock);
 
wire [3:0] f_pipe_used;
assign f_pipe_used = wraddr - rdaddr;
always @(*)
`ASSERT(f_pipe_used == fifo_fill);
always @(posedge i_clk)
if (f_pipe_used == OPT_MAXDEPTH)
 
begin
`ASSUME(!i_pipe_stb);
`ASSERT((o_busy)&&(o_pipe_stalled));
end
 
always @(*)
`ASSERT(fifo_fill <= OPT_MAXDEPTH);
 
always @(*)
if (!IMPLEMENT_LOCK)
`ASSUME(!i_lock);
 
`ifndef VERILATOR
always @(*)
if ((WITH_LOCAL_BUS)&&(o_wb_cyc_gbl|o_wb_cyc_lcl)
&&(i_pipe_stb))
begin
if (o_wb_cyc_lcl)
// `ASSUME(i_addr[31:24] == 8'hff);
assume(i_addr[31:24] == 8'hff);
else
assume(i_addr[31:24] != 8'hff);
end
`endif
 
always @(*)
if (!WITH_LOCAL_BUS)
begin
assert(!r_wb_cyc_lcl);
assert(!o_wb_cyc_lcl);
assert(!o_wb_stb_lcl);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(f_cyc))&&(!$past(i_pipe_stb)))
`ASSERT(f_pipe_used == 0);
 
always @(*)
if (!f_cyc)
`ASSERT(f_pipe_used == 0);
 
always @(posedge i_clk)
if (f_pipe_used >= 13)
`ASSUME(!i_pipe_stb);
 
always @(posedge i_clk)
if ((f_cyc)&&(f_pipe_used >= 13))
`ASSERT((o_busy)&&(o_pipe_stalled));
 
 
always @(posedge i_clk)
`ASSERT((!r_wb_cyc_gbl)||(!r_wb_cyc_lcl));
 
always @(posedge i_clk)
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl));
 
always @(posedge i_clk)
`ASSERT((!o_wb_stb_gbl)||(!o_wb_stb_lcl));
 
always @(*)
if (!WITH_LOCAL_BUS)
begin
assert(!o_wb_cyc_lcl);
assert(!o_wb_stb_lcl);
if (o_wb_stb_lcl)
assert(o_wb_addr[(AW-1):22] == {(8-(30-AW)){1'b1}});
end
 
always @(posedge i_clk)
if (o_wb_stb_gbl)
`ASSERT(o_wb_cyc_gbl);
 
always @(posedge i_clk)
if (o_wb_stb_lcl)
`ASSERT(o_wb_cyc_lcl);
 
always @(posedge i_clk)
`ASSERT(cyc == (r_wb_cyc_gbl|r_wb_cyc_lcl));
 
always @(posedge i_clk)
`ASSERT(cyc == (r_wb_cyc_lcl)|(r_wb_cyc_gbl));
always @(posedge i_clk)
if ((f_past_valid)&&(!i_reset)&&(!$past(misaligned)))
begin
if (f_stb)
`ASSERT(f_pipe_used == f_outstanding + 4'h1);
else
`ASSERT(f_pipe_used == f_outstanding);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(r_wb_cyc_gbl||r_wb_cyc_lcl))
&&(!$past(f_stb)))
`ASSERT(!f_stb);
 
always @(*)
`ASSERT((!lcl_stb)||(!gbl_stb));
 
reg [(1<<FLN)-1:0] f_mem_used;
wire [(1<<FLN)-1:0] f_zero;
//
// insist that we only ever accept memory requests for the same GIE
// (i.e. 4th bit of register)
//
always @(*)
if ((i_pipe_stb)&&(wraddr != rdaddr))
`ASSUME(i_oreg[4] == fifo_gie);
 
initial f_pc = 1'b0;
always @(posedge i_clk)
if(i_reset)
f_pc <= 1'b0;
else if (i_pipe_stb)
f_pc <= (((f_pc)&&(f_cyc))
||((!i_op[0])&&(i_oreg[3:1] == 3'h7)));
else if (!f_cyc)
f_pc <= 1'b0;
 
always @(posedge i_clk)
if ((f_cyc)&&(o_wb_we))
`ASSERT(!f_pc);
 
always @(*)
if ((f_pc)&&(f_cyc))
`ASSUME(!i_pipe_stb);
 
always @(*)
if (wraddr == rdaddr)
begin
`ASSERT(!r_wb_cyc_gbl);
`ASSERT(!r_wb_cyc_lcl);
end else if (f_cyc)
begin
`ASSERT(fifo_fill == f_outstanding + ((f_stb)?1:0));
end
 
 
`define FIFOCHECK
`ifdef FIFOCHECK
wire [3:0] lastaddr = wraddr - 1'b1;
 
integer k;
always @(*)
begin
f_mem_used = 0;
for(k = 0 ; k < (1<<FLN); k=k+1)
begin
if (wraddr == rdaddr)
f_mem_used[k] = 1'b0;
else if (wraddr > rdaddr)
begin
if ((k < wraddr)&&(k >= rdaddr))
f_mem_used[k] = 1'b1;
end else if (k < wraddr)
f_mem_used[k] = 1'b1;
else if (k >= rdaddr)
f_mem_used[k] = 1'b1;
end
end
 
 
always @(*)
begin
for(k=0; k<(1<<FLN); k=k+1)
if ((f_mem_used[k])&&(!o_wb_we)&&((!f_pc)||(k!=lastaddr)))
`ASSERT(fifo_oreg[k][7:5] != 3'h7);
end
 
initial assert(!fifo_full);
 
always @(posedge i_clk)
cover(cyc && !fifo_full);
 
always @(posedge i_clk)
cover((f_cyc)&&(f_stb)&&(!i_wb_stall)&&(!i_wb_ack)
&&(!o_pipe_stalled));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(f_stb))&&($past(f_cyc)))
cover((f_cyc)&&(i_wb_ack));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(f_stb))&&($past(f_cyc)))
cover($past(i_wb_ack)&&(i_wb_ack));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_valid)))
cover(o_valid);
 
`endif // FIFOCHECK
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(f_past_valid))&&($past(f_cyc))&&($past(f_cyc,2)))
`ASSERT($stable(o_wreg[4]));
 
always @(*)
`ASSERT((!f_cyc)||(!o_valid)||(o_wreg[3:1]!=3'h7));
 
`endif // FORMAL
endmodule
//
//
// Usage (from yosys): (Before) (A,!OPTZ) (A,OPTZ)
// Cells: 302 314 391
// FDRE 138 140 140
// LUT1 2 2 2
// LUT2 38 41 61
// LUT3 13 16 33
// LUT4 3 8 12
// LUT5 22 10 8
// LUT6 52 59 81
// MUXCY 6 6 6
// MUXF7 10 13 21
// MUXF8 1 2 10
// RAM64X1D 9 9 9
// XORCY 8 8 8
//
//
/.
. Property changes : Deleted: svn:ignore ## -1 +0,0 ## -obj_dir

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.