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
    from Rev 209 to Rev 205
    Reverse comparison

Rev 209 → Rev 205

/README.md File deleted
/ex/wbarbiter.v File deleted
/ex/fwb_slave.v File deleted
/ex/busdelay.v File deleted
/ex/fwb_master.v File deleted
/ex/wbdblpriarb.v File deleted
/ex/wbpriarbiter.v File deleted
/ex/fwb_counter.v File deleted
/core/iscachable.v File deleted
/core/slowmpy.v File deleted
/core/README.md File deleted
/core/mpyop.v File deleted
/core/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
/core/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
//
/core/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
 
/core/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
/core/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
//
//
/core/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
/core/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
/core/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
/core/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
/core/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
/core/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
//
//
core Property changes : Deleted: svn:ignore ## -1 +0,0 ## -obj_dir Index: cpudefs.v =================================================================== --- cpudefs.v (revision 209) +++ cpudefs.v (revision 205) @@ -28,7 +28,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2015-2019, Gisselquist Technology, LLC +// Copyright (C) 2015-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 @@ -60,7 +60,22 @@ // it handles various instructions within the set: // // +// OPT_ILLEGAL_INSTRUCTION is part of a new section of code that is supposed +// to recognize illegal instructions and interrupt the CPU whenever one such +// instruction is encountered. The goal is to create a soft floating point +// unit via this approach, that can then be replaced with a true floating point +// unit. As I'm not there yet, it just catches illegal instructions and +// interrupts the CPU on any such instruction--when defined. Otherwise, +// illegal instructions are quietly ignored and their behaviour is ... +// undefined. (Many get treated like NOOPs ...) // +// I recommend setting this flag so highly, that I'm likely going to remove +// the option to turn this off in future versions of this CPU. +// +`define OPT_ILLEGAL_INSTRUCTION +// +// +// // OPT_MULTIPLY controls whether or not the multiply is built and included // in the ALU by default. Set this option and a parameter will be set that // includes the multiply. (This parameter may still be overridden, as with @@ -224,15 +239,6 @@ // // // -// OPT_DCACHE enables a CPU data cache for (hopefully) better performance -// in terms of speed. It requires telling the CPU which parts of memory -// can be cachable in terms of three separate address regions: one for the -// SDRAM, one for the flash, and another for the block RAM. -// -`define OPT_DCACHE -// -// -// // OPT_PIPELINED_BUS_ACCESS controls whether or not LOD/STO instructions // can take advantaged of pipelined bus instructions. To be eligible, the // operations must be identical (cannot pipeline loads and stores, just loads @@ -255,13 +261,7 @@ `endif // OPT_SINGLE_FETCH // // -// [EXPERIMENTAL--and not (yet) finished] -// OPT_MMU determines whether or not an MMU will be included in the ZipSystem -// containing the ZipCPU. When set, the ZipCPU will route all memory accesses -// through the MMU as an address translator, creating a form of Virtual memory. // -// `define OPT_MMU -// // Now let's talk about peripherals for a moment. These next two defines // control whether the DMA controller is included in the Zip System, and // whether or not the 8 accounting timers are also included. Set these to Index: Makefile =================================================================== --- Makefile (revision 209) +++ Makefile (revision 205) @@ -1,48 +1,42 @@ ################################################################################ -## -## Filename: Makefile -## -## Project: Zip CPU -- a small, lightweight, RISC CPU soft core -## -## Purpose: This makefile builds a verilator simulation of the zipsystem. -## It does not make the system within Vivado or Quartus. -## -## -## Creator: Dan Gisselquist, Ph.D. -## Gisselquist Technology, LLC -## +# +# Filename: Makefile +# +# Project: Zip CPU -- a small, lightweight, RISC CPU soft core +# +# Purpose: This makefile builds a verilator simulation of the zipsystem. +# It does not make the system within Vivado or Quartus. +# +# +# Creator: Dan Gisselquist, Ph.D. +# 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 -## your option) any later version. -## -## This program is distributed in the hope that it will be useful, but WITHOUT -## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or -## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -## for more details. -## -## You should have received a copy of the GNU General Public License along -## 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 -## for a copy. -## -## License: GPL, v3, as defined and found on www.gnu.org, -## http://www.gnu.org/licenses/gpl.html -## -## +# +# 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 +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# License: GPL, v3, as defined and found on www.gnu.org, +# http://www.gnu.org/licenses/gpl.html +# +# ################################################################################ -## -## +# .PHONY: all all: zipsystem zipbones cpudefs.h div zipmmu cpuops pfcache CORED:= core PRPHD:= peripherals -EXD := ex +AUXD := aux VSRC := zipsystem.v cpudefs.v \ $(PRPHD)/wbdmac.v $(PRPHD)/icontrol.v \ $(PRPHD)/zipcounter.v $(PRPHD)/zipjiffies.v \ @@ -52,8 +46,8 @@ $(CORED)/pfcache.v \ $(CORED)/memops.v $(CORED)/pipemem.v \ $(CORED)/div.v \ - $(EXD)/busdelay.v \ - $(EXD)/wbdblpriarb.v $(EXD)/wbpriarbiter.v \ + $(AUXD)/busdelay.v \ + $(AUXD)/wbdblpriarb.v $(AUXD)/wbpriarbiter.v \ $(CORED)/idecode.v $(CORED)/cpuops.v VZIP := zipbones.v cpudefs.v \ $(CORED)/zipcpu.v $(CORED)/cpuops.v $(CORED)/idecode.v \ @@ -61,20 +55,13 @@ $(CORED)/pfcache.v \ $(CORED)/memops.v $(CORED)/pipemem.v \ $(CORED)/div.v \ - $(EXD)/busdelay.v $(EXD)/wbdblpriarb.v \ + $(AUXD)/busdelay.v $(AUXD)/wbdblpriarb.v \ $(CORED)/idecode.v $(CORED)/cpuops.v VOBJ := obj_dir SUBMAKE := $(MAKE) --no-print-directory --directory=$(VOBJ) -f -ifeq ($(VERILATOR_ROOT),) -VERILATOR := verilator -else -VERILATOR := $(VERILATOR_ROOT)/bin/verilator -endif -# VFLAGS := -Wall -MMD -D__WORDSIZE=64 --trace -cc -y $(CORED) -y $(PRPHD) -y $(EXD) -VFLAGS := -Wall -MMD --trace -cc -y $(CORED) -y $(PRPHD) -y $(EXD) -VERILATE=$(VERILATOR) $(VFLAGS) +VERILATE=verilator --trace -cc -y $(CORED) -y $(PRPHD) -y $(AUXD) $(VOBJ)/Vzipsystem.cpp: $(VSRC) $(VERILATE) zipsystem.v @@ -150,12 +137,3 @@ .PHONY: clean clean: rm -rf $(VOBJ) cpudefs.h - -# -# Note Verilator's dependency created information, and include it here if we -# can -DEPS := $(wildcard $(VOBJ)/*.d) - -ifneq ($(DEPS),) -include $(DEPS) -endif
/peripherals/README.md File deleted
/peripherals/icontrol.v
52,7 → 52,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
76,23 → 76,20
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module icontrol(i_clk, i_reset, i_wr, i_data, o_data,
module icontrol(i_clk, i_reset, i_wr, i_proc_bus, o_proc_bus,
i_brd_ints, o_interrupt);
parameter IUSED = 15, BW=32;
input wire i_clk, i_reset;
input wire i_wr;
input wire [BW-1:0] i_data;
output reg [BW-1:0] o_data;
input wire [(IUSED-1):0] i_brd_ints;
parameter IUSED = 15;
input i_clk, i_reset;
input i_wr;
input [31:0] i_proc_bus;
output wire [31:0] o_proc_bus;
input [(IUSED-1):0] i_brd_ints;
output wire o_interrupt;
 
reg [(IUSED-1):0] r_int_state;
reg [(IUSED-1):0] r_int_enable;
wire [(IUSED-1):0] nxt_int_state;
reg r_interrupt, r_gie;
wire w_any;
reg r_any, r_interrupt, r_gie;
 
assign nxt_int_state = (r_int_state|i_brd_ints);
initial r_int_state = 0;
100,7 → 97,7
if (i_reset)
r_int_state <= 0;
else if (i_wr)
r_int_state <= i_brd_ints | (r_int_state & (~i_data[(IUSED-1):0]));
r_int_state <= nxt_int_state & (~i_proc_bus[(IUSED-1):0]);
else
r_int_state <= nxt_int_state;
initial r_int_enable = 0;
107,10 → 104,10
always @(posedge i_clk)
if (i_reset)
r_int_enable <= 0;
else if ((i_wr)&&(i_data[BW-1]))
r_int_enable <= r_int_enable | i_data[(16+IUSED-1):16];
else if ((i_wr)&&(!i_data[BW-1]))
r_int_enable <= r_int_enable & (~ i_data[(16+IUSED-1):16]);
else if ((i_wr)&&(i_proc_bus[31]))
r_int_enable <= r_int_enable | i_proc_bus[(16+IUSED-1):16];
else if ((i_wr)&&(~i_proc_bus[31]))
r_int_enable <= r_int_enable & (~ i_proc_bus[(16+IUSED-1):16]);
 
initial r_gie = 1'b0;
always @(posedge i_clk)
117,98 → 114,46
if (i_reset)
r_gie <= 1'b0;
else if (i_wr)
r_gie <= i_data[BW-1];
r_gie <= i_proc_bus[31];
 
assign w_any = ((r_int_state & r_int_enable) != 0);
 
initial r_any = 1'b0;
always @(posedge i_clk)
r_any <= ((r_int_state & r_int_enable) != 0);
initial r_interrupt = 1'b0;
always @(posedge i_clk)
if (i_reset)
r_interrupt <= 1'b0;
else
r_interrupt <= (r_gie)&&(w_any);
r_interrupt <= r_gie & r_any;
 
generate
if (IUSED < 15)
begin
always @(posedge i_clk)
o_data <= {
assign o_proc_bus = {
r_gie, { {(15-IUSED){1'b0}}, r_int_enable },
w_any, { {(15-IUSED){1'b0}}, r_int_state } };
r_any, { {(15-IUSED){1'b0}}, r_int_state } };
end else begin
always @(posedge i_clk)
o_data <= { r_gie, r_int_enable, w_any, r_int_state };
assign o_proc_bus = { r_gie, r_int_enable, r_any, r_int_state };
end endgenerate
 
assign o_interrupt = r_interrupt;
 
// Make verilator happy
// verilator lint_off UNUSED
wire [30:0] unused;
assign unused = i_data[30:0];
// verilator lint_on UNUSED
 
`ifdef FORMAL
`ifdef ICONTROL
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
reg f_past_valid;
 
initial f_past_valid = 1'b0;
/*
reg int_condition;
initial int_condition = 1'b0;
initial o_interrupt_strobe = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
if (i_reset)
begin
int_condition <= 1'b0;
o_interrupt_strobe <= 1'b0;
end else if (~r_interrupt) // This might end up generating
begin // many, many, (wild many) interrupts
int_condition <= 1'b0;
o_interrupt_strobe <= 1'b0;
end else if ((~int_condition)&&(r_interrupt))
begin
int_condition <= 1'b1;
o_interrupt_strobe <= 1'b1;
end else
o_interrupt_strobe <= 1'b0;
*/
 
initial `ASSUME(i_reset);
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
assign o_interrupt = r_interrupt;
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
assert(w_any == 0);
assert(r_interrupt == 0);
assert(r_gie == 0);
assert(r_int_enable == 0);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset)))
assert((r_int_state & $past(i_brd_ints))==$past(i_brd_ints));
 
always @(posedge i_clk)
if (((f_past_valid)&&(!$past(i_reset)))
&&($past(r_int_state) & $past(r_int_enable))
&&($past(r_gie)) )
assert(o_interrupt);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(w_any))&&(!$past(i_wr))&&(!$past(i_reset)))
assert(w_any);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(r_gie)))
assert(!o_interrupt);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(w_any)))
assert(!o_interrupt);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr)))
begin
// Interrupts cannot be cleared, unless they are set
assert(r_int_state == (($past(i_brd_ints))
|(($past(r_int_state))&(~$past(i_data[IUSED-1:0])))));
assert(r_gie == $past(i_data[BW-1]));
end else if ((f_past_valid)&&(!$past(i_reset)))
begin
assert(r_int_state == ($past(r_int_state)|$past(i_brd_ints)));
assert(r_gie == $past(r_gie));
end
 
`endif
endmodule
/peripherals/wbdmac.v
56,11 → 56,7
// The minimum transfer length is one, while zero
// is mapped to a transfer length of 1kW.
//
// Write 32'hffed00 to halt an ongoing transaction, completing anything
// remaining, or 32'hafed00 to abort the current transaction leaving
// any unfinished read/write in an undetermined state.
//
//
// To use this, follow this checklist:
// 1. Wait for any prior DMA operation to complete
// (Read address 0, wait 'till either top bit is set or cfg_len==0)
86,7 → 82,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
118,7 → 114,7
`define DMA_WRITE_REQ 3'b101
`define DMA_WRITE_ACK 3'b110
 
module wbdmac(i_clk, i_reset,
module wbdmac(i_clk, i_rst,
i_swb_cyc, i_swb_stb, i_swb_we, i_swb_addr, i_swb_data,
o_swb_ack, o_swb_stall, o_swb_data,
o_mwb_cyc, o_mwb_stb, o_mwb_we, o_mwb_addr, o_mwb_data,
125,14 → 121,13
i_mwb_ack, i_mwb_stall, i_mwb_data, i_mwb_err,
i_dev_ints,
o_interrupt);
parameter ADDRESS_WIDTH=30, LGMEMLEN = 10, DW=32;
localparam LGDV=5;
localparam AW=ADDRESS_WIDTH;
input wire i_clk, i_reset;
parameter ADDRESS_WIDTH=32, LGMEMLEN = 10,
DW=32, LGDV=5,AW=ADDRESS_WIDTH;
input i_clk, i_rst;
// Slave/control wishbone inputs
input wire i_swb_cyc, i_swb_stb, i_swb_we;
input wire [1:0] i_swb_addr;
input wire [(DW-1):0] i_swb_data;
input i_swb_cyc, i_swb_stb, i_swb_we;
input [1:0] i_swb_addr;
input [(DW-1):0] i_swb_data;
// Slave/control wishbone outputs
output reg o_swb_ack;
output wire o_swb_stall;
142,11 → 137,11
output reg [(AW-1):0] o_mwb_addr;
output reg [(DW-1):0] o_mwb_data;
// Master/DMA wishbone responses from the bus
input wire i_mwb_ack, i_mwb_stall;
input wire [(DW-1):0] i_mwb_data;
input wire i_mwb_err;
input i_mwb_ack, i_mwb_stall;
input [(DW-1):0] i_mwb_data;
input i_mwb_err;
// The interrupt device interrupt lines
input wire [(DW-1):0] i_dev_ints;
input [(DW-1):0] i_dev_ints;
// An interrupt to be set upon completion
output reg o_interrupt;
// Need to release the bus for a higher priority user
158,42 → 153,7
// input i_other_busmaster_requests_bus;
//
 
wire s_cyc, s_stb, s_we;
wire [1:0] s_addr;
wire [31:0] s_data;
`define DELAY_ACCESS
`ifdef DELAY_ACCESS
reg r_s_cyc, r_s_stb, r_s_we;
reg [1:0] r_s_addr;
reg [31:0] r_s_data;
 
always @(posedge i_clk)
r_s_cyc <= i_swb_cyc;
always @(posedge i_clk)
if (i_reset)
r_s_stb <= 1'b0;
else
r_s_stb <= i_swb_stb;
always @(posedge i_clk)
r_s_we <= i_swb_we;
always @(posedge i_clk)
r_s_addr<= i_swb_addr;
always @(posedge i_clk)
r_s_data<= i_swb_data;
 
assign s_cyc = r_s_cyc;
assign s_stb = r_s_stb;
assign s_we = r_s_we;
assign s_addr= r_s_addr;
assign s_data= r_s_data;
`else
assign s_cyc = i_swb_cyc;
assign s_stb = i_swb_stb;
assign s_we = i_swb_we;
assign s_addr= i_swb_addr;
assign s_data= i_swb_data;
`endif
 
reg [2:0] dma_state;
reg cfg_err, cfg_len_nonzero;
reg [(AW-1):0] cfg_waddr, cfg_raddr, cfg_len;
216,46 → 176,53
 
initial dma_state = `DMA_IDLE;
initial o_interrupt = 1'b0;
initial cfg_len = {(AW){1'b0}};
initial cfg_blocklen_sub_one = {(LGMEMLEN){1'b1}};
initial cfg_on_dev_trigger = 1'b0;
initial cfg_len_nonzero = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
dma_state <= `DMA_IDLE;
cfg_on_dev_trigger <= 1'b0;
cfg_incs <= 1'b0;
cfg_incd <= 1'b0;
end else case(dma_state)
case(dma_state)
`DMA_IDLE: begin
o_mwb_addr <= cfg_raddr;
nwritten <= 0;
nread <= 0;
nracks <= 0;
nwacks <= 0;
cfg_len_nonzero <= (|cfg_len);
 
// When the slave wishbone writes, and we are in this
// (ready) configuration, then allow the DMA to be controlled
// and thus to start.
if ((s_stb)&&(s_we))
if ((i_swb_stb)&&(i_swb_we))
begin
case(s_addr)
case(i_swb_addr)
2'b00: begin
if ((s_data[27:16] == 12'hfed)
&&(s_data[31:30] == 2'b00)
if ((i_swb_data[31:16] == 16'h0fed)
&&(cfg_len_nonzero))
dma_state <= `DMA_WAIT;
cfg_blocklen_sub_one
<= s_data[(LGMEMLEN-1):0]
<= i_swb_data[(LGMEMLEN-1):0]
+ {(LGMEMLEN){1'b1}};
// i.e. -1;
cfg_dev_trigger <= s_data[14:10];
cfg_on_dev_trigger <= s_data[15];
cfg_incs <= !s_data[29];
cfg_incd <= !s_data[28];
cfg_dev_trigger <= i_swb_data[14:10];
cfg_on_dev_trigger <= i_swb_data[15];
cfg_incs <= ~i_swb_data[29];
cfg_incd <= ~i_swb_data[28];
end
2'b01: begin end // This is done elsewhere
2'b10: cfg_raddr <= s_data[(AW+2-1):2];
2'b11: cfg_waddr <= s_data[(AW+2-1):2];
2'b01: begin
cfg_len <= i_swb_data[(AW-1):0];
cfg_len_nonzero <= (|i_swb_data[(AW-1):0]);
end
2'b10: cfg_raddr <= i_swb_data[(AW-1):0];
2'b11: cfg_waddr <= i_swb_data[(AW-1):0];
endcase
end end
`DMA_WAIT: begin
o_mwb_addr <= cfg_raddr;
nracks <= 0;
nwacks <= 0;
nwritten <= 0;
nread <= 0;
if (abort)
dma_state <= `DMA_IDLE;
else if (user_halt)
264,10 → 231,13
dma_state <= `DMA_READ_REQ;
end
`DMA_READ_REQ: begin
if (!i_mwb_stall)
nwritten <= 0;
 
if (~i_mwb_stall)
begin
// Number of read acknowledgements needed
if ((last_read_request)||(user_halt))
nracks <= nracks+1;
if (last_read_request)
//((nracks == {1'b0, cfg_blocklen_sub_one})||(bus_nracks == cfg_len-1))
// Wishbone interruptus
dma_state <= `DMA_READ_ACK;
276,21 → 246,33
+ {{(AW-1){1'b0}},1'b1};
end
 
if ((i_mwb_err)||(abort))
if (user_halt)
dma_state <= `DMA_READ_ACK;
if (i_mwb_err)
begin
cfg_len <= 0;
dma_state <= `DMA_IDLE;
else if (i_mwb_ack)
end
 
if (abort)
dma_state <= `DMA_IDLE;
if (i_mwb_ack)
begin
nread <= nread+1;
if (cfg_incs)
cfg_raddr <= cfg_raddr
+ {{(AW-1){1'b0}},1'b1};
if (last_read_ack)
dma_state <= `DMA_PRE_WRITE;
end end
`DMA_READ_ACK: begin
if ((i_mwb_err)||(abort))
nwritten <= 0;
 
if (i_mwb_err)
begin
cfg_len <= 0;
dma_state <= `DMA_IDLE;
else if (i_mwb_ack)
end else if (i_mwb_ack)
begin
nread <= nread+1;
if (last_read_ack) // (nread+1 == nracks)
dma_state <= `DMA_PRE_WRITE;
if (user_halt)
299,6 → 281,8
cfg_raddr <= cfg_raddr
+ {{(AW-1){1'b0}},1'b1};
end
if (abort)
dma_state <= `DMA_IDLE;
end
`DMA_PRE_WRITE: begin
o_mwb_addr <= cfg_waddr;
305,8 → 289,9
dma_state <= (abort)?`DMA_IDLE:`DMA_WRITE_REQ;
end
`DMA_WRITE_REQ: begin
if (!i_mwb_stall)
if (~i_mwb_stall)
begin
nwritten <= nwritten+1;
if (last_write_request) // (nwritten == nread-1)
// Wishbone interruptus
dma_state <= `DMA_WRITE_ACK;
319,20 → 304,39
end
end
 
if ((i_mwb_err)||(abort))
if (i_mwb_err)
begin
cfg_len <= 0;
dma_state <= `DMA_IDLE;
else if ((i_mwb_ack)&&(last_write_ack))
dma_state <= (cfg_len <= 1)?`DMA_IDLE:`DMA_WAIT;
else if (!cfg_len_nonzero)
end
if (i_mwb_ack)
begin
nwacks <= nwacks+1;
cfg_len <= cfg_len +{(AW){1'b1}}; // -1
end
if (user_halt)
dma_state <= `DMA_WRITE_ACK;
if (abort)
dma_state <= `DMA_IDLE;
end
`DMA_WRITE_ACK: begin
if ((i_mwb_err)||(abort))
if (i_mwb_err)
begin
cfg_len <= 0;
nread <= 0;
dma_state <= `DMA_IDLE;
else if ((i_mwb_ack)&&(last_write_ack))
// (nwacks+1 == nwritten)
dma_state <= (cfg_len <= 1)?`DMA_IDLE:`DMA_WAIT;
else if (!cfg_len_nonzero)
end else if (i_mwb_ack)
begin
nwacks <= nwacks+1;
cfg_len <= cfg_len +{(AW){1'b1}};//cfg_len -= 1;
if (last_write_ack) // (nwacks+1 == nwritten)
begin
nread <= 0;
dma_state <= (cfg_len == 1)?`DMA_IDLE:`DMA_WAIT;
end
end
 
if (abort)
dma_state <= `DMA_IDLE;
end
default:
341,150 → 345,71
 
initial o_interrupt = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_interrupt <= 1'b0;
else
o_interrupt <= ((dma_state == `DMA_WRITE_ACK)&&(i_mwb_ack)
&&(last_write_ack)
&&(cfg_len == {{(AW-1){1'b0}},1'b1}))
||((dma_state != `DMA_IDLE)&&(i_mwb_err));
 
 
initial cfg_len = 0;
initial cfg_len_nonzero = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
cfg_len <= 0;
cfg_len_nonzero <= 1'b0;
end else if ((dma_state == `DMA_IDLE)
&&(s_stb)&&(s_we)&&(s_addr == 2'b01))
begin
cfg_len <= s_data[(AW-1):0];
cfg_len_nonzero <= (|s_data[(AW-1):0]);
end else if ((o_mwb_cyc)&&(o_mwb_we)&&(i_mwb_ack))
begin
cfg_len <= cfg_len - 1'b1;
cfg_len_nonzero <= (cfg_len > 1);
end
 
initial nracks = 0;
always @(posedge i_clk)
if (i_reset)
nracks <= 0;
else if ((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT))
nracks <= 0;
else if ((o_mwb_stb)&&(!o_mwb_we)&&(!i_mwb_stall))
nracks <= nracks + 1'b1;
 
initial nread = 0;
always @(posedge i_clk)
if (i_reset)
nread <= 0;
else if ((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT))
nread <= 0;
else if ((!o_mwb_we)&&(i_mwb_ack))
nread <= nread + 1'b1;
 
initial nwritten = 0;
always @(posedge i_clk)
if (i_reset)
nwritten <= 0;
else if ((!o_mwb_cyc)||(!o_mwb_we))
nwritten <= 0;
else if ((o_mwb_stb)&&(!i_mwb_stall))
nwritten <= nwritten + 1'b1;
 
initial nwacks = 0;
always @(posedge i_clk)
if (i_reset)
nwacks <= 0;
else if ((!o_mwb_cyc)||(!o_mwb_we))
nwacks <= 0;
else if (i_mwb_ack)
nwacks <= nwacks + 1'b1;
 
initial cfg_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
cfg_err <= 1'b0;
else if (dma_state == `DMA_IDLE)
begin
if ((s_stb)&&(s_we)&&(s_addr==2'b00))
cfg_err <= 1'b0;
end else if (((i_mwb_err)&&(o_mwb_cyc))||(abort))
cfg_err <= 1'b1;
if (dma_state == `DMA_IDLE)
begin
if ((i_swb_stb)&&(i_swb_we)&&(i_swb_addr==2'b00))
cfg_err <= 1'b0;
end else if (((i_mwb_err)&&(o_mwb_cyc))||(abort))
cfg_err <= 1'b1;
 
initial last_read_request = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_read_request <= 1'b0;
else if ((dma_state == `DMA_WAIT)||(dma_state == `DMA_READ_REQ))
begin
if ((!i_mwb_stall)&&(dma_state == `DMA_READ_REQ))
if ((dma_state == `DMA_WAIT)||(dma_state == `DMA_READ_REQ))
begin
last_read_request <=
(nracks + 1 == { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-2);
if ((~i_mwb_stall)&&(dma_state == `DMA_READ_REQ))
begin
last_read_request <=
(nracks + 1 == { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-2);
end else
last_read_request <=
(nracks== { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-1);
end else
last_read_request <=
(nracks== { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-1);
end else
last_read_request <= 1'b0;
last_read_request <= 1'b0;
 
 
wire [(LGMEMLEN):0] next_nread;
assign next_nread = nread + 1'b1;
 
initial last_read_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_read_ack <= 0;
else if (dma_state == `DMA_READ_REQ)
begin
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall)))
last_read_ack <= (next_nread == { 1'b0, cfg_blocklen_sub_one });
else
last_read_ack <= (nread == { 1'b0, cfg_blocklen_sub_one });
end else if (dma_state == `DMA_READ_ACK)
begin
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall)))
last_read_ack <= (nread+2 == nracks);
else
last_read_ack <= (next_nread == nracks);
end else
last_read_ack <= 1'b0;
if ((dma_state == `DMA_READ_REQ)||(dma_state == `DMA_READ_ACK))
begin
if ((i_mwb_ack)&&((~o_mwb_stb)||(i_mwb_stall)))
last_read_ack <= (nread+2 == nracks);
else
last_read_ack <= (nread+1 == nracks);
end else
last_read_ack <= 1'b0;
 
initial last_write_request = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_write_request <= 1'b0;
else if (dma_state == `DMA_PRE_WRITE)
last_write_request <= (nread <= 1);
else if (dma_state == `DMA_WRITE_REQ)
begin
if (i_mwb_stall)
last_write_request <= (nwritten >= nread-1);
else
last_write_request <= (nwritten >= nread-2);
end else
last_write_request <= 1'b0;
if (dma_state == `DMA_PRE_WRITE)
last_write_request <= (nread <= 1);
else if (dma_state == `DMA_WRITE_REQ)
begin
if (i_mwb_stall)
last_write_request <= (nwritten >= nread-1);
else
last_write_request <= (nwritten >= nread-2);
end else
last_write_request <= 1'b0;
 
initial last_write_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_write_ack <= 1'b0;
else if((dma_state == `DMA_WRITE_REQ)||(dma_state == `DMA_WRITE_ACK))
begin
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall)))
last_write_ack <= (nwacks+2 == nwritten);
else
last_write_ack <= (nwacks+1 == nwritten);
end else
last_write_ack <= 1'b0;
if((dma_state == `DMA_WRITE_REQ)||(dma_state == `DMA_WRITE_ACK))
begin
if ((i_mwb_ack)&&((~o_mwb_stb)||(i_mwb_stall)))
last_write_ack <= (nwacks+2 == nwritten);
else
last_write_ack <= (nwacks+1 == nwritten);
end else
last_write_ack <= 1'b0;
 
 
assign o_mwb_cyc = (dma_state == `DMA_READ_REQ)
||(dma_state == `DMA_READ_ACK)
||(dma_state == `DMA_WRITE_REQ)
505,41 → 430,32
// want to read from, unless we are idling. So ... the math is touchy.
//
reg [(LGMEMLEN-1):0] rdaddr;
 
initial rdaddr = 0;
always @(posedge i_clk)
if (i_reset)
rdaddr <= 0;
else if((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT)
||(dma_state == `DMA_WRITE_ACK))
rdaddr <= 0;
else if ((dma_state == `DMA_PRE_WRITE)
||((dma_state==`DMA_WRITE_REQ)&&(!i_mwb_stall)))
rdaddr <= rdaddr + {{(LGMEMLEN-1){1'b0}},1'b1};
 
if((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT)
||(dma_state == `DMA_WRITE_ACK))
rdaddr <= 0;
else if ((dma_state == `DMA_PRE_WRITE)
||((dma_state==`DMA_WRITE_REQ)&&(~i_mwb_stall)))
rdaddr <= rdaddr + {{(LGMEMLEN-1){1'b0}},1'b1};
always @(posedge i_clk)
if (i_reset)
o_mwb_data <= 0;
else if ((dma_state != `DMA_WRITE_REQ)||(!i_mwb_stall))
o_mwb_data <= dma_mem[rdaddr];
if ((dma_state != `DMA_WRITE_REQ)||(~i_mwb_stall))
o_mwb_data <= dma_mem[rdaddr];
always @(posedge i_clk)
if((dma_state == `DMA_READ_REQ)||(dma_state == `DMA_READ_ACK))
dma_mem[nread[(LGMEMLEN-1):0]] <= i_mwb_data;
 
always @(posedge i_clk)
if (i_reset)
o_swb_data <= 0;
else casez(s_addr)
casez(i_swb_addr)
2'b00: o_swb_data <= { (dma_state != `DMA_IDLE), cfg_err,
!cfg_incs, !cfg_incd,
~cfg_incs, ~cfg_incd,
1'b0, nread,
cfg_on_dev_trigger, cfg_dev_trigger,
cfg_blocklen_sub_one
};
2'b01: o_swb_data <= { {(DW-AW){1'b0}}, cfg_len };
2'b10: o_swb_data <= { {(DW-2-AW){1'b0}}, cfg_raddr, 2'b00 };
2'b11: o_swb_data <= { {(DW-2-AW){1'b0}}, cfg_waddr, 2'b00 };
endcase
2'b10: o_swb_data <= { {(DW-AW){1'b0}}, cfg_raddr};
2'b11: o_swb_data <= { {(DW-AW){1'b0}}, cfg_waddr};
endcase
 
// This causes us to wait a minimum of two clocks before starting: One
// to go into the wait state, and then one while in the wait state to
546,291 → 462,30
// develop the trigger.
initial trigger = 1'b0;
always @(posedge i_clk)
if (i_reset)
trigger <= 1'b0;
else
trigger <= (dma_state == `DMA_WAIT)
&&((!cfg_on_dev_trigger)
&&((~cfg_on_dev_trigger)
||(i_dev_ints[cfg_dev_trigger]));
 
// Ack any access. We'll quietly ignore any access where we are busy,
// but ack it anyway. In other words, before writing to the device,
// double check that it isn't busy, and then write.
initial o_swb_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_swb_ack <= 1'b0;
else if (!i_swb_cyc)
o_swb_ack <= 1'b0;
else
o_swb_ack <= (s_stb);
o_swb_ack <= (i_swb_stb);
 
assign o_swb_stall = 1'b0;
 
initial abort = 1'b0;
always @(posedge i_clk)
if (i_reset)
abort <= 1'b0;
else if (dma_state == `DMA_IDLE)
abort <= 1'b0;
else
abort <= ((s_stb)&&(s_we)
&&(s_addr == 2'b00)
&&(s_data == 32'hffed0000));
abort <= (i_rst)||((i_swb_stb)&&(i_swb_we)
&&(i_swb_addr == 2'b00)
&&(i_swb_data == 32'hffed0000));
 
initial user_halt = 1'b0;
always @(posedge i_clk)
user_halt <= ((user_halt)&&(dma_state != `DMA_IDLE))
||((s_stb)&&(s_we)&&(dma_state != `DMA_IDLE)
&&(s_addr == 2'b00)
&&(s_data == 32'hafed0000));
||((i_swb_stb)&&(i_swb_we)&&(dma_state != `DMA_IDLE)
&&(i_swb_addr == 2'b00)
&&(i_swb_data == 32'hafed0000));
 
endmodule
 
// Make verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = s_cyc;
// 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;
 
always @(*)
if (!f_past_valid)
assume(i_reset);
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
assert(dma_state == `DMA_IDLE);
 
parameter F_SLV_LGDEPTH = 3;
parameter F_MSTR_LGDEPTH = LGMEMLEN+1;
 
wire [F_SLV_LGDEPTH-1:0] f_swb_nreqs, f_swb_nacks, f_swb_outstanding;
wire [F_MSTR_LGDEPTH-1:0] f_mwb_nreqs, f_mwb_nacks, f_mwb_outstanding;
 
fwb_slave #(
.AW(2), .DW(32), .F_MAX_STALL(0), .F_MAX_ACK_DELAY(2),
.F_LGDEPTH(F_SLV_LGDEPTH)
) control_port(i_clk, i_reset,
i_swb_cyc, i_swb_stb, i_swb_we, i_swb_addr, i_swb_data,4'b1111,
o_swb_ack, o_swb_stall, o_swb_data, 1'b0,
f_swb_nreqs, f_swb_nacks, f_swb_outstanding);
always @(*)
assert(o_swb_stall == 0);
`ifdef DELAY_ACCESS
always @(*)
if ((!i_reset)&&(i_swb_cyc))
begin
if ((!s_stb)&&(!o_swb_ack))
assert(f_swb_outstanding == 0);
else if ((s_stb)&&(!o_swb_ack))
assert(f_swb_outstanding == 1);
else if ((!s_stb)&&(o_swb_ack))
assert(f_swb_outstanding == 1);
else if ((s_stb)&&(o_swb_ack))
assert(f_swb_outstanding == 2);
end
`else
always @(*)
if ((!i_reset)&&(!o_swb_ack))
assert(f_swb_outstanding == 0);
`endif
 
fwb_master #(.AW(AW), .DW(32), .F_MAX_STALL(4), .F_MAX_ACK_DELAY(8),
.F_LGDEPTH(F_MSTR_LGDEPTH),
.F_OPT_RMW_BUS_OPTION(1'b1),
.F_OPT_DISCONTINUOUS(1'b0),
.F_OPT_SOURCE(1'b1)
) external_bus(i_clk, i_reset,
o_mwb_cyc, o_mwb_stb, o_mwb_we, o_mwb_addr, o_mwb_data,4'b1111,
i_mwb_ack, i_mwb_stall, i_mwb_data, i_mwb_err,
f_mwb_nreqs, f_mwb_nacks, f_mwb_outstanding);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(dma_state == `DMA_IDLE)))
assert(o_mwb_cyc == 1'b0);
 
always @(*)
if ((o_mwb_cyc)&&(!o_mwb_we))
begin
assert(nracks == f_mwb_nreqs);
assert(nread == f_mwb_nacks);
end
 
always @(*)
if ((o_mwb_cyc)&&(o_mwb_we))
begin
assert(nwacks == f_mwb_nacks);
assert(nwritten == f_mwb_nreqs);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(abort))&&($past(dma_state != `DMA_IDLE)))
assert(dma_state == `DMA_IDLE);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_mwb_cyc))&&(o_mwb_cyc)&&(
(( !cfg_incs)&&(!o_mwb_we))
||((!cfg_incd)&&( o_mwb_we))))
begin
assert(o_mwb_addr == $past(o_mwb_addr));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_mwb_cyc))&&(
((!cfg_incs)||($past(o_mwb_we))||(!$past(i_mwb_ack)))))
assert(cfg_raddr == $past(cfg_raddr));
 
always @(posedge i_clk)
if ((f_past_valid)&&(dma_state == `DMA_WRITE_REQ))
assert(cfg_waddr == o_mwb_addr);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(o_mwb_stb))&&(!$past(i_mwb_stall)))
begin
assert( ((!cfg_incs)&&(!$past(o_mwb_we)))
||((!cfg_incd)&&( $past(o_mwb_we)))
||(o_mwb_addr==$past(o_mwb_addr)+1'b1));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_mwb_cyc))&&(o_mwb_cyc))
begin
if (o_mwb_we)
assert(o_mwb_addr == cfg_waddr);
else
assert(o_mwb_addr == cfg_raddr);
end
 
always @(*)
assert(cfg_len_nonzero == (cfg_len != 0));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
begin
assert(cfg_len == 0);
assert(!cfg_len_nonzero);
end else if ((f_past_valid)&&($past(o_mwb_cyc)))
begin
if (($past(i_mwb_ack))&&($past(o_mwb_we)))
assert(cfg_len == $past(cfg_len)-1'b1);
else
assert(cfg_len == $past(cfg_len));
end else if ((f_past_valid)&&(($past(dma_state) != `DMA_IDLE)
||(!$past(s_stb))||(!$past(s_we))
||($past(s_addr)!=2'b01)))
assert(cfg_len == $past(cfg_len));
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_mwb_cyc))&&($past(cfg_len == 0))
&&(!$past(user_halt)))
begin
assert(cfg_len == 0);
assert((dma_state != $past(dma_state))||(!o_mwb_cyc));
end
 
always @(posedge i_clk)
if (cfg_len == 0)
assert(!o_mwb_stb);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(dma_state) != `DMA_IDLE))
begin
assert(cfg_incs == $past(cfg_incs));
assert(cfg_incd == $past(cfg_incd));
assert(cfg_blocklen_sub_one == $past(cfg_blocklen_sub_one));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(dma_state) == `DMA_IDLE))
assert(cfg_len_nonzero == (cfg_len != 0));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(o_mwb_cyc))||(!$past(o_mwb_we)))
assert((nwritten == 0)&&(nwacks == 0));
always @(posedge i_clk)
if ((o_mwb_cyc)&&(!o_mwb_we))
assert(bus_nracks <= cfg_len);
always @(posedge i_clk)
if ((o_mwb_cyc)&&(!o_mwb_we))
assert(nread <= nracks);
always @(posedge i_clk)
if ((o_mwb_cyc)&&(o_mwb_we))
assert(nwritten-nwacks
+((o_mwb_stb)? 1'b1:1'b0)
- f_mwb_outstanding
// -((i_mwb_ack)? 1'b1:1'b0)
<= cfg_len);
always @(*)
assert(f_mwb_outstanding
+ ((o_mwb_stb)? 1'b1:1'b0) <= cfg_len);
 
wire [LGMEMLEN:0] f_cfg_blocklen;
assign f_cfg_blocklen = { 1'b0, cfg_blocklen_sub_one} + 1'b1;
 
always @(*)
if (dma_state == `DMA_WAIT)
assert(cfg_len > 0);
 
always @(*)
if ((o_mwb_stb)&&(o_mwb_we))
assert(nread == nracks);
 
always @(*)
if (o_mwb_stb)
assert(nwritten <= cfg_blocklen_sub_one);
always @(posedge i_clk)
assert(nwritten <= f_cfg_blocklen);
 
always @(*)
if ((o_mwb_stb)&&(!o_mwb_we))
assert(nracks < f_cfg_blocklen);
else
assert(nracks <= f_cfg_blocklen);
always @(*)
if ((o_mwb_cyc)&&(i_mwb_ack)&&(!o_mwb_we))
assert(nread < f_cfg_blocklen);
always @(*)
assert(nread <= nracks);
 
always @(*)
if ((o_mwb_cyc)&&(o_mwb_we)&&(!user_halt))
assert(nread == nracks);
 
always @(*)
if ((o_mwb_cyc)&&(o_mwb_we))
assert(nwritten >= nwacks);
 
always @(*)
if (dma_state == `DMA_WRITE_REQ)
assert(last_write_request == (nwritten == nread-1));
 
always @(*)
assert(nwritten >= nwacks);
 
always @(*)
assert(nread >= nwritten);
 
always @(*)
assert(nracks >= nread);
 
wire [LGMEMLEN:0] f_npending;
assign f_npending = nread-nwacks;
always @(*)
if (dma_state != `DMA_IDLE)
assert({ {(AW-LGMEMLEN-1){1'b0}}, f_npending} <= cfg_len);
 
always @(posedge i_clk)
begin
assert(cfg_len_nonzero == (cfg_len != 0));
if ((f_past_valid)&&($past(dma_state != `DMA_IDLE))&&($past(cfg_len == 0)))
assert(cfg_len == 0);
end
 
 
`endif
endmodule
/peripherals/wbwatchdog.v
6,24 → 6,32
//
// Purpose: A Zip timer, redesigned to be a bus watchdog
//
// This is basically a timer, but there are some unique features to it.
// This is a **really** stripped down Zip Timer. All options for external
// control have been removed. This timer may be reset, and ... that's
// about it. The goal is that this stripped down timer be used as a bus
// watchdog element. Even at that, it's not really fully featured. The
// rest of the important features can be found in the zipsystem module.
//
// 1. There is no way to "write" the timeout to this watchdog. It is
// fixed with an input (that is assumed to be constant)
// 2. The counter returns to i_timer and the interrupt is cleared on any
// reset.
// 3. Between resets, the counter counts down to zero. Once (and if) it
// hits zero, it will remain at zero until reset.
// 4. Any time the counter is at zero, and until the reset that resets
// the counter, the output interrupt will be set.
// As a historical note, the wishbone watchdog timer began as a normal
// timer, with some fixed inputs. This makes sense, if you think about it:
// if the goal is to interrupt a stalled wishbone transaction by inserting
// a bus error, then you can't use the bus to set it up or configure it
// simply because the bus in question is ... well, unreliable. You're
// trying to make it reliable.
//
// The problem with using the ziptimer in a stripped down implementation
// was that the fixed inputs caused the synthesis tool to complain about
// the use of registers values would never change. This solves that
// problem by explicitly removing the cruft that would otherwise
// just create synthesis warnings and errors.
//
//
// 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
47,84 → 55,28
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module wbwatchdog(i_clk, i_reset, i_timeout, o_int);
module wbwatchdog(i_clk, i_rst, i_ce, i_timeout, o_int);
parameter BW = 32;
input wire i_clk, i_reset;
input i_clk, i_rst, i_ce;
// Inputs (these were at one time wishbone controlled ...)
input wire [(BW-1):0] i_timeout;
input [(BW-1):0] i_timeout;
// Interrupt line
output reg o_int;
 
reg [(BW-1):0] r_value;
initial r_value = {(BW){1'b1}};
initial r_value = 0;
always @(posedge i_clk)
if (i_reset)
if (i_rst)
r_value <= i_timeout[(BW-1):0];
else if (!o_int)
else if ((i_ce)&&(~o_int))
r_value <= r_value + {(BW){1'b1}}; // r_value - 1;
 
// Set the interrupt on our last tick.
initial o_int = 1'b0;
always @(posedge i_clk)
if (i_reset)
if ((i_rst)||(~i_ce))
o_int <= 1'b0;
else if (!o_int)
else
o_int <= (r_value == { {(BW-1){1'b0}}, 1'b1 });
 
`ifdef FORMAL
reg f_past_valid;
 
initial f_past_valid = 1'b0;
always @(posedge f_past_valid)
f_past_valid <= 1'b1;
 
///////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
///////////////////////////////////////////////
always @(*)
assume(i_timeout > 1);
always @(posedge i_clk)
if (f_past_valid)
assume(i_timeout == $past(i_timeout));
 
//
//
///////////////////////////////////////////////
//
//
// Assertions about our internal state and our outputs
//
//
///////////////////////////////////////////////
//
//
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_int))&&(!$past(i_reset)))
assert(o_int);
 
always @(*)
assert(o_int == (r_value == 0));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(o_int)))
begin
assert(r_value == $past(r_value)-1'b1);
end
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
if (!f_past_valid)
assert(r_value == {(BW){1'b1}});
else // if ($past(i_reset))
assert(r_value == $past(i_timeout));
assert(!o_int);
end
`endif
endmodule
/peripherals/zipcounter.v
27,7 → 27,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
51,20 → 51,15
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module zipcounter(i_clk, i_reset, i_event,
module zipcounter(i_clk, i_ce,
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data,
o_wb_ack, o_wb_stall, o_wb_data,
o_int);
parameter BW = 32;
//
localparam F_LGDEPTH = 2;
//
input wire i_clk, i_reset, i_event;
input i_clk, i_ce;
// Wishbone inputs
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
75,11 → 70,9
initial o_int = 0;
initial o_wb_data = 32'h00;
always @(posedge i_clk)
if (i_reset)
{ o_int, o_wb_data } <= 0;
else if ((i_wb_stb)&&(i_wb_we))
if ((i_wb_stb)&&(i_wb_we))
{ o_int, o_wb_data } <= { 1'b0, i_wb_data };
else if (i_event)
else if (i_ce)
{ o_int, o_wb_data } <= o_wb_data+{{(BW-1){1'b0}},1'b1};
else
o_int <= 1'b0;
86,124 → 79,6
 
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_wb_ack <= 1'b0;
else
o_wb_ack <= i_wb_stb;
o_wb_ack <= (i_wb_stb);
assign o_wb_stall = 1'b0;
 
 
// Make verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = i_wb_cyc;
// 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;
 
always @(*)
if (!f_past_valid)
assume(i_reset);
 
////////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
////////////////////////////////////////////////
//
 
////////////////////////////////////////////////
//
//
// Bus interface properties
//
//
////////////////////////////////////////////////
//
 
// We never stall the bus
always @(*)
assert(!o_wb_stall);
 
// We always ack every transaction on the following clock
always @(posedge i_clk)
assert(o_wb_ack == ((f_past_valid)&&(!$past(i_reset))
&&($past(i_wb_stb))));
 
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
 
fwb_slave #( .AW(1), .F_MAX_STALL(0),
.F_MAX_ACK_DELAY(1), .F_LGDEPTH(F_LGDEPTH)
) fwbi(i_clk, i_reset,
i_wb_cyc, i_wb_stb, i_wb_we, 1'b0, i_wb_data, 4'hf,
o_wb_ack, o_wb_stall, o_wb_data, 1'b0,
f_nreqs, f_nacks, f_outstanding);
 
always @(*)
if ((o_wb_ack)&&(i_wb_cyc))
assert(f_outstanding==1);
else
assert(f_outstanding == 0);
 
////////////////////////////////////////////////
//
//
// Assumptions about our outputs
//
//
////////////////////////////////////////////////
//
 
// Drop the interrupt line and reset the counter on any reset
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
assert((!o_int)&&(o_wb_data == 0));
 
// Clear the interrupt and set the counter on any write (other than
// during a reset)
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(i_wb_stb))&&($past(i_wb_we)))
assert((!o_int)&&(o_wb_data == $past(i_wb_data)));
 
// Normal logic of the routine itself
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_wb_stb)))
begin
if (!$past(i_event))
begin
// If the CE line wasn't set on the last clock, then the
// counter must not change, and the interrupt line must
// be low.
assert(o_wb_data == $past(o_wb_data));
assert(!o_int);
end else // if ($past(i_event))
begin
// Otherwise, if the CE line was high on the last clock,
// then our counter should have incremented.
assert(o_wb_data == $past(o_wb_data) + 1'b1);
 
// Likewise, if the counter rolled over, then the
// output interrupt, o_int, should be true.
if ($past(o_wb_data)=={(BW){1'b1}})
assert(o_int);
else
// In all other circumstances it should be clear
assert(!o_int);
end
end
 
//
// The output interrupt should never be true two clocks in a row
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_int)))
assert(!o_int);
 
`endif
endmodule
/peripherals/zipjiffies.v
45,7 → 45,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
69,17 → 69,15
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module zipjiffies(i_clk, i_reset, i_ce,
module zipjiffies(i_clk, i_ce,
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data,
o_wb_ack, o_wb_stall, o_wb_data,
o_int);
parameter BW = 32;
input wire i_clk, i_reset, i_ce;
input i_clk, i_ce;
// Wishbone inputs
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
96,11 → 94,8
// together, one clock at a time.
//
reg [(BW-1):0] r_counter;
initial r_counter = 0;
always @(posedge i_clk)
if (i_reset)
r_counter <= 0;
else if (i_ce)
if (i_ce)
r_counter <= r_counter+1;
 
//
115,13 → 110,9
 
initial new_set = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
new_set <= 1'b0;
new_when <= 0;
end else begin
// Delay WB commands (writes) by a clock to simplify our logic
new_set <= ((i_wb_stb)&&(i_wb_we));
// Delay things by a clock to simplify our logic
new_set <= ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we));
// new_when is a don't care when new_set = 0, so don't worry
// about setting it at all times.
new_when<= i_wb_data;
130,11 → 121,7
initial o_int = 1'b0;
initial int_set = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
o_int <= 0;
int_set <= 0;
end else begin
o_int <= 1'b0;
if ((i_ce)&&(int_set)&&(r_counter == int_when))
// Interrupts are self-clearing
146,151 → 133,18
int_set <= 1'b1;
else if ((i_ce)&&(r_counter == int_when))
int_set <= 1'b0;
 
if ((new_set)&&(till_wb > 0)&&((till_wb<till_when)||(~int_set)))
int_when <= new_when;
end
 
always @(posedge i_clk)
if ((new_set)&&(till_wb > 0)&&((till_wb<till_when)||(!int_set)))
int_when <= new_when;
 
//
// Acknowledge any wishbone accesses -- everything we did took only
// one clock anyway.
//
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_wb_ack <= 1'b0;
else
o_wb_ack <= i_wb_stb;
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb);
 
assign o_wb_data = r_counter;
assign o_wb_stall = 1'b0;
 
// Make verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = i_wb_cyc;
// 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;
 
////////////////////////////////////////////////
//
//
// Assumptions about our inputs
//
//
////////////////////////////////////////////////
//
// Some basic WB assumtions
 
// We will not start out in a wishbone cycle
initial assume(!i_wb_cyc);
 
// Following any reset the cycle line will be low
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
assume(!i_wb_cyc);
 
// Anytime the stb is high, the cycle line must also be high
always @(posedge i_clk)
assume((!i_wb_stb)||(i_wb_cyc));
 
 
////////////////////////////////////////////////
//
//
// Assumptions about our bus outputs
//
//
////////////////////////////////////////////////
//
 
// We never stall the bus
always @(*)
assert(!o_wb_stall);
// We always ack every transaction on the following clock
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb)))
assert(o_wb_ack);
else
assert(!o_wb_ack);
 
 
////////////////////////////////////////////////
//
//
// Assumptions about our internal state and our outputs
//
//
////////////////////////////////////////////////
//
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
begin
assert(!o_wb_ack);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb))
&&($past(i_wb_we)))
assert(new_when == $past(i_wb_data));
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb))
&&($past(i_wb_we)))
assert(new_set);
else
assert(!new_set);
 
//
//
//
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
assert(!o_int);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
begin
assert(!int_set);
assert(!new_set);
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(new_set))
&&(!$past(till_wb[BW-1]))
&&($past(till_wb) > 0))
assert(int_set);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))
&&($past(r_counter)==$past(int_when)))
begin
assert((o_int)||(!$past(int_set)));
assert((!int_set)||($past(new_set)));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(new_set))&&(!$past(int_set)))
assert(!int_set);
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
assert(!o_int);
else if (($past(new_set))&&($past(till_wb) < 0))
assert(o_int);
 
always @(posedge i_clk)
if ((f_past_valid)&&
((!$past(new_set))
||($past(till_wb[BW-1]))
||($past(till_wb == 0))))
assert(int_when == $past(int_when));
//
`endif
endmodule
/peripherals/zipmmu.v
8,7 → 8,7
// unit, that is configured from one wishbone bus and modifies a
// separate wishbone bus. Both busses will not be active at the same time.
//
// The idea is that the CPU can use one portion of its peripheral
// The idea is that the CPU can use one portion of its peripheral
// system memory space to configure the MMU, and another portion of its
// memory space to access the MMU. Even more, configuring the MMU is to
// be done when the CPU is in supervisor mode. This means that all
33,7 → 33,7
// clock cycle. Further, multiple accesses to the same page
// should not take any longer than the one cycle delay. Accesses
// to other pages should take a minimum number of clocks.
// Accesses from one page to the next, such as from one page to
// Accesses from one page to the next, such as from one page to
// the next subsequent one, should cost no delays.
//
// 2. One independent control word to set the current context
48,7 → 48,7
// bus of width (lgpage+17), or a memory space of (2^(lgpage+17)).
// Under this formula, the number of valid address bits can range
// from 17 to 32.
// - Contains 4 RdOnly bits indicating log_2 TLB table size.
// - Contains 4 RdOnly bits indicating log_2 TLB table size.
// Size is given by (2^(lgsize)). I'm considering sizes of 6,7&8
// - Contains 4 RdOnly bits indicating the log page size, offset by
// eight. Page sizes are therefore given by (2^(lgpage+8)), and
69,7 → 69,7
// Supervisor *cannot* have page table entries, since there are no
// interrupts (page faults) allowed in supervisor context.
//
// To be valid,
// To be valid,
// Context Size (1..16), NFlags ( 4) < Page Size (8-23 bits)
// Page size (8-23 bits) > NFlags bits (4)
//
78,7 → 78,7
// 3. One status word, which contains the address that failed and some
// flags:
//
// Top Virtual address bits indicate which page ... caused a problem.
// Top Virtual address bits indicate which page ... caused a problem.
// These will be the top N bits of the word, where N is the size
// of the virtual address bits. (Bits are cleared upon any write.)
//
87,7 → 87,7
// - 4: Multiple page table matches
// - 2: Attempt to write a read-only page
// - 1: Page not found
//
//
// 3. Two words per active page table entry, accessed through two bus
// addresses. This word contains:
//
103,16 → 103,19
// 1-bit Read-only / ~written (user set/read/written)
// If set, this page will cause a fault on any
// attempt to write this memory.
// 1-bit This page may be executed
// 1-bit Cacheable
// This is not a hardware page, but a memory page.
// Therefore, the values within this page may be
// cached.
// 1-bit Accessed
// This an be used to implement a least-recently
// used measure. The hardware will set this value
// when the page is accessed. The user can also
// set or clear this at will.
// 1-bit Cacheable
// This is not a hardware page, but a memory page.
// Therefore, the values within this page may be
// cached.
// 1-bit This context
// This is a read-only bit, indicating that the
// context register of this address matches the
// context register in the control word.
//
// (Loaded flag Not necessary, just map the physical page to 0)
//
122,7 → 125,7
//
// 4. Can read/write this word in two parts:
//
// (20-bit Virtual )(8-bits lower context)(4-bit flags), and
// (20-bit Virtual )(8-bits lower context)(4-bit flags), and
// (20-bit Physical)(8-bits upper context)(4-bit flags)
//
// Actual bit lengths will vary as the MMU configuration changes,
137,10 → 140,10
// bits in the control register.
//
// +----+----+-----+----+----+----+----+--+--+--+--+
// | | Lower 8b| R| E| C| A|
// | 20-bit Virtual page ID | Context | O| X| C| C|
// |(top 20 bits of the addr)| ID | n| E| H| C|
// | | | W| F| E| S|
// | | Lower 8b| R| A| C| T|
// | 20-bit Virtual page ID | Context | O| C| C| H|
// |(top 20 bits of the addr)| ID | n| C| H| S|
// | | | W| S| E| P|
// +----+----+-----+----+----+----+----+--+--+--+--+
//
// +----+----+-----+----+----+----+----+--+--+--+--+
147,7 → 150,7
// | | Upper 8b| R| A| C| T|
// | 20-bit Physical pg ID | Context | O| C| C| H|
// |(top 20 bits of the | ID | n| C| H| S|
// | physical address) | | W| S| E| P|
// | physical address | | W| S| E| P|
// +----+----+-----+----+----+----+----+--+--+--+--+
//
// 5. PF Cache--handles words in both physical and virtual
160,7 → 163,7
// will have long been available, the "Valid" bit will be turned
// on and associated with the physical mapping.
// - On any data-write (pf doesn't write), MMU sends [Context,Va,Pa]
// TLB mapping to the pf-cache.
// TLB mapping to the pf-cache.
// - If the write matches any physical PF-cache addresses (???), the
// pfcache declares that address line invalid, and just plain
// clears the valid bit for that page.
186,7 → 189,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-2019, Gisselquist Technology, LLC
// Copyright (C) 2016-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
208,20 → 211,9
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define ROFLAG 3 // Read-only flag
`define EXEFLG 2 // No-execute flag (invalid for I-cache)
`define CHFLAG 1 // Cachable flag
`define AXFLAG 0 // Accessed flag
//
module zipmmu(i_clk, i_reset, i_wbs_cyc_stb, i_wbs_we, i_wbs_addr,
i_wbs_data, o_wbs_ack, o_wbs_stall, o_wbs_data,
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_exe,
i_wbm_addr, i_wbm_data, i_wbm_sel, i_gie,
o_cyc, o_stb, o_we, o_addr, o_data, o_sel,
module zipmmu(i_clk, i_reset, i_ctrl_cyc_stb, i_wbm_cyc, i_wbm_stb, i_wb_we,
i_wb_addr, i_wb_data,
o_cyc, o_stb, o_we, o_addr, o_data,
i_stall, i_ack, i_err, i_data,
o_rtn_stall, o_rtn_ack, o_rtn_err,
o_rtn_miss, o_rtn_data,
228,66 → 220,36
pf_return_stb, pf_return_we,
pf_return_p, pf_return_v,
pf_return_cachable);
parameter // The size of the address bus. Actual addressable
// size will likely be 2^(ADDRESS_WIDTH+2) octets
ADDRESS_WIDTH=28,
// Number of page table entries
`ifdef FORMAL
LGTBL=4'h2,
`else
LGTBL=4'h6,
`endif
// The requested log page size in 8-bit bytes
PLGPGSZB=20,
// Number of bits describing context
`ifdef FORMAL
PLGCTXT=2;
`else
PLGCTXT=16;
`endif
parameter [0:0] OPT_DELAY_RETURN = 1'b0;
parameter ADDRESS_WIDTH=28, LGTBL=6, PLGPGSZ=12, PLGCTXT=16, DW=32;
localparam // And for our derived parameters (don't set these ...)
// Width of the data bus is 32-bits. This may be hard
// to change.
DW = 32,
// AW is just shorthand for the name ADDRESS_WIDTH
AW = ADDRESS_WIDTH,
// Page sizes must allow for a minimum of one context
// bit per page, plus four flag bits, hence the minimum
// number of bits for an address within a page is 5
LGPGSZB=(PLGPGSZB < 5)? 5:PLGPGSZB, // in bytes
LGPGSZW=LGPGSZB-2, // in words
// The context value for a given page can be split
// across both virtual and physical words. It cannot
// have so many bits to it that it takes more bits
// then are available.
LGCTXT=((2*LGPGSZB-4)>PLGCTXT)?
PLGCTXT:(2*LGPGSZB-4),
LGPGSZ=(PLGPGSZ < 5)? 5:PLGPGSZ,
// The number of context bits is twice the number of
// bits left over from DW after removing the LGPGSZ
// and flags bits.
LGCTXT=(((DW-LGPGSZ-4)<<1)<PLGCTXT)?
((DW-LGPGSZ-4)<<1):PLGCTXT,
// LGLCTX is the number of context bits in the low word
LGLCTX=(LGCTXT > (LGPGSZB-4))?(LGPGSZB-4):LGCTXT,
LGLCTX=((LGPGSZ-4)<LGCTXT)?(LGPGSZ-4):LGCTXT,
// LGHCTX is the number of context bits in the high word
LGHCTX= (LGCTXT-LGLCTX>0)?(LGCTXT-LGLCTX):0,
VAW=(DW-LGPGSZB), // Virtual address width, in bytes
PAW=(AW-LGPGSZW), // Physical address width, in words
LGHCTX= (LGCTXT-LGLCTX),
VAW=(DW-LGPGSZ), // Virtual address width
PAW=(AW-LGPGSZ), // Physical address width
TBL_BITS = LGTBL, // Bits necessary to addr tbl
TBL_SIZE=(1<<TBL_BITS);// Number of table entries
input wire i_clk, i_reset;
input i_clk, i_reset;
//
input wire i_wbs_cyc_stb;
input wire i_wbs_we;
input wire [(LGTBL+1):0] i_wbs_addr;
input wire [(DW-1):0] i_wbs_data;
output reg o_wbs_ack;
output wire o_wbs_stall;
output reg [(DW-1):0] o_wbs_data;
input i_ctrl_cyc_stb;
//
input wire i_wbm_cyc, i_wbm_stb;
input i_wbm_cyc, i_wbm_stb;
//
input wire i_wbm_we, i_wbm_exe;
input wire [(DW-2-1):0] i_wbm_addr;
input wire [(DW-1):0] i_wbm_data;
input wire [(DW/8-1):0] i_wbm_sel;
input wire i_gie;
input i_wb_we;
input [(DW-1):0] i_wb_addr;
input [(DW-1):0] i_wb_data;
//
// Here's where we drive the slave side of the bus
output reg o_cyc;
294,18 → 256,17
output wire o_stb, o_we;
output reg [(AW-1):0] o_addr;
output reg [(DW-1):0] o_data;
output reg [(DW/8-1):0] o_sel;
// and get our return information from driving the slave ...
input wire i_stall, i_ack, i_err;
input wire [(DW-1):0] i_data;
input i_stall, i_ack, i_err;
input [(DW-1):0] i_data;
//
// Here's where we return information on either our slave/control bus
// or the memory bus we are controlled from. Note that we share these
// wires ...
output wire o_rtn_stall;
output wire o_rtn_ack;
output reg o_rtn_ack;
output wire o_rtn_err, o_rtn_miss;
output wire [(DW-1):0] o_rtn_data;
output [(DW-1):0] o_rtn_data;
// Finally, to allow the prefetch to snoop on the MMU conversion ...
output wire pf_return_stb, // snoop data is valid
pf_return_we; // snoop data is chnging
318,67 → 279,60
//
//
//
reg [3:0] tlb_flags [0:(TBL_SIZE-1)];
wire [3:0] s_tlb_flags;
reg [3:1] tlb_flags [0:(TBL_SIZE-1)];
reg [(LGCTXT-1):0] tlb_cdata [0:(TBL_SIZE-1)];
reg [(VAW-1):0] tlb_vdata [0:(TBL_SIZE-1)];
reg [(PAW-1):0] tlb_pdata [0:(TBL_SIZE-1)];
reg [(TBL_SIZE-1):0] tlb_valid, tlb_accessed;
reg [(DW-LGPGSZ-1):0] tlb_vdata [0:(TBL_SIZE-1)];
reg [(AW-LGPGSZ-1):0] tlb_pdata [0:(TBL_SIZE-1)];
 
wire adr_control, adr_vtable, adr_ptable;
wire wr_control, wr_vtable, wr_ptable;
wire adr_control, adr_status, adr_vtable, adr_ptable;
wire wr_control, wr_status, wr_vtable, wr_ptable;
wire [(LGTBL-1):0] wr_tlb_addr;
assign wr_tlb_addr= i_wbs_addr[(LGTBL):1]; // Leave bottom for V/P
assign adr_control= (i_wbs_cyc_stb)&&(~i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
assign adr_vtable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
assign adr_ptable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&( i_wbs_addr[0]);
assign wr_control = (adr_control)&&(i_wbs_we);
assign wr_vtable = (adr_vtable )&&(i_wbs_we);
assign wr_ptable = (adr_ptable )&&(i_wbs_we);
assign wr_tlb_addr= i_wb_addr[(LGTBL):1]; // Leave bottom for V/P
assign adr_control= (i_ctrl_cyc_stb)&&(~i_wb_addr[(LGTBL+1)])&&(~i_wb_addr[0]);
assign adr_status = (i_ctrl_cyc_stb)&&(~i_wb_addr[(LGTBL+1)])&&( i_wb_addr[0]);
assign adr_vtable = (i_ctrl_cyc_stb)&&( i_wb_addr[(LGTBL+1)])&&(~i_wb_addr[0]);
assign adr_ptable = (i_ctrl_cyc_stb)&&( i_wb_addr[(LGTBL+1)])&&( i_wb_addr[0]);
assign wr_control = (adr_control)&&(i_wb_we);
assign wr_status = (adr_status )&&(i_wb_we);
assign wr_vtable = (adr_vtable )&&(i_wb_we);
assign wr_ptable = (adr_ptable )&&(i_wb_we);
 
reg z_context;
wire kernel_context;
reg [(LGCTXT-1):0] r_context_word;
reg setup_ack, z_context, setup_this_page_flag;
reg [(DW-1):0] setup_data;
reg [(LGCTXT-1):0] r_context_word, setup_page;
//
wire [31:0] w_control_data, w_ptable_reg;
reg [31:0] w_vtable_reg;
wire [31:0] w_control_data,w_vtable_reg,w_ptable_reg;
wire [(LGCTXT-1):0] w_ctable_reg;
reg [31:0] status_word;
//
reg rf_miss, rf_ropage, rf_table_err;
wire [31:0] control_word;
wire [3:0] lgaddr_bits, lgtblsz_bits, lgpagesz_bits,
lgcontext_bits;
 
reg [(AW-(LGPGSZ)):0] r_mmu_err_vaddr;
wire [(DW-LGPGSZ):0] w_mmu_err_vaddr;
//
reg r_pending, r_we, r_exe, r_valid,
last_page_valid, last_ro, last_exe;
reg [(DW-3):0] r_addr;
reg r_pending, r_we, last_page_valid, last_ro, r_valid;
reg [(DW-1):0] r_addr;
reg [(DW-1):0] r_data;
wire [(VAW-1):0] vpage;
wire [AW-LGPGSZW-1:0] ppage;
reg [(DW/8-1):0] r_sel;
reg [(PAW-1):0] last_ppage;
reg [(VAW-1):0] last_vpage;
//
wire [(TBL_SIZE-1):0] r_tlb_match;
reg [(LGTBL-1):0] s_tlb_addr, last_tlb;
reg [(LGTBL-1):0] s_tlb_addr;
reg s_tlb_miss, s_tlb_hit, s_pending;
//
wire ro_flag, exe_flag, simple_miss, ro_miss, exe_miss, table_err, cachable;
reg pf_stb, pf_cachable;
reg miss_pending;
wire ro_flag, simple_miss, ro_miss, table_err, cachable;
reg p_tlb_miss,p_tlb_err, pf_stb, pf_cachable;
//
reg rtn_err;
 
 
wire this_page_valid, pending_page_valid;
assign this_page_valid = ((last_page_valid)
&&(i_wbm_addr[(DW-3):(DW-2-VAW)]==last_vpage)
&&((!last_ro)||(!i_wbm_we))
&&((!last_exe)||(!i_wbm_exe)));
assign pending_page_valid = ((s_pending)&&(s_tlb_hit)
&&((!r_we)||(!ro_flag))
&&((!r_exe)||(exe_flag)));
 
//////////////////////////////////////////
//
//
// Step one -- handle the control bus--i_wbs_cyc_stb
// Step one -- handle the control bus--i_ctrl_cyc_stb
//
//
//////////////////////////////////////////
386,40 → 340,23
begin
// Write to the Translation lookaside buffer
if (wr_vtable)
tlb_vdata[wr_tlb_addr]<=i_wbs_data[(DW-1):LGPGSZB];
tlb_vdata[wr_tlb_addr]<=i_wb_data[(DW-1):LGPGSZ];
if (wr_ptable)
tlb_pdata[wr_tlb_addr]<=i_wbs_data[(AW+1):LGPGSZB];
tlb_pdata[wr_tlb_addr]<=i_wb_data[(AW-1):LGPGSZ];
// Set the context register for the page
if ((wr_vtable)||(wr_ptable))
tlb_flags[wr_tlb_addr] <= i_wb_data[3:1];
// Otherwise, keep track of the accessed bit if we ever access this page
else if ((!z_context)&&(r_pending)&&(s_tlb_hit)&&((!r_we)||(!ro_flag)))
tlb_flags[s_tlb_addr][2] <= 1'b1;
if (wr_vtable)
tlb_flags[wr_tlb_addr] <= { i_wbs_data[3:1], 1'b0 };
if (wr_vtable)
tlb_cdata[wr_tlb_addr][(LGLCTX-1):0]
<= i_wbs_data[(LGLCTX+4-1):4];
tlb_cdata[wr_tlb_addr][((LGCTXT>=8)? 7:(LGCTXT-1)):0]
<= i_wb_data[((LGCTXT>=8)? 11:(4+LGCTXT-1)):4];
if ((wr_ptable)&&(LGCTXT > 8))
tlb_cdata[wr_tlb_addr][(LGCTXT-1):8]
<= i_wb_data[(4+LGCTXT-8-1):4];
setup_ack <= (i_ctrl_cyc_stb)&&(!i_reset);
end
 
initial tlb_accessed = 0;
always @(posedge i_clk)
if (i_reset)
tlb_accessed <= 0;
else begin
if (wr_vtable)
tlb_accessed[wr_tlb_addr] <= 1'b0;
// Otherwise, keep track of the accessed bit if we
// ever access this page
else if ((!kernel_context)&&(pending_page_valid))
tlb_accessed[s_tlb_addr] <= 1'b1;
else if ((!kernel_context)&&(this_page_valid))
tlb_accessed[last_tlb] <= 1'b1;
end
 
generate if (LGHCTX > 0)
begin : HCTX
always @(posedge i_clk)
if (wr_ptable)
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX]
<= i_wbs_data[(LGHCTX+4-1):4];
end endgenerate
 
// Writing to the control word
initial z_context = 1'b1;
initial r_context_word = 0;
426,62 → 363,48
always @(posedge i_clk)
if (wr_control)
begin
r_context_word <= i_wbs_data[(LGCTXT-1):0];
z_context <= (i_wbs_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
r_context_word <= i_wb_data[(LGCTXT-1):0];
z_context <= (i_wb_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
end
assign kernel_context = (z_context)||(!i_gie);
// Status words cannot be written to
 
always @(posedge i_clk)
if (i_reset)
tlb_valid <= 0;
else if (wr_ptable)
tlb_valid[wr_tlb_addr]<=1'b1; //(i_wbs_data[(AW+1):LGPGSZB]!=0);
 
/* v*rilator lint_off WIDTH */
assign w_control_data[31:28] = AW[3:0]-4'd1;
assign w_control_data[27:24] = LGTBL[3:0];
assign w_control_data[23:20] = LGPGSZB[3:0]-4'd10;
assign w_control_data[19:16] = LGCTXT[3:0]-1'b1;
/* v*rilator lint_on WIDTH */
/* verilator lint_off WIDTH */
assign w_control_data[31:28] = AW-17;
assign w_control_data[27:24] = LGTBL;
assign w_control_data[23:20] = LGPGSZ-8;
assign w_control_data[19:16] = LGCTXT-1;
/* verilator lint_on WIDTH */
assign w_control_data[15: 0] = {{(16-LGCTXT){1'b0}}, r_context_word};
//
always @(*)
begin
w_vtable_reg = 0;
w_vtable_reg[(DW-1):LGPGSZB] = tlb_vdata[wr_tlb_addr];
w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] };
w_vtable_reg[ 3:0] = { tlb_flags[wr_tlb_addr][3:1],
tlb_accessed[wr_tlb_addr] };
end
assign w_vtable_reg[(DW-1):LGPGSZ] = tlb_vdata[wr_tlb_addr];
assign w_vtable_reg[(LGPGSZ-1):(LGLCTX+4-1)] = 0;
assign w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] };
assign w_vtable_reg[ 3:0] = { tlb_flags[wr_tlb_addr], 1'b0 };
//
assign w_ptable_reg[(DW-1):LGPGSZB] = { {(DW-PAW-LGPGSZB){1'b0}},
assign w_ptable_reg[(DW-1):LGPGSZ] = { {(DW-AW){1'b0}},
tlb_pdata[wr_tlb_addr] };
assign w_ptable_reg[ 3:0] = 4'h0;
assign w_ptable_reg[LGPGSZ:(4+LGHCTX)] = 0;
assign w_ptable_reg[ 3:0] = { tlb_flags[wr_tlb_addr], 1'b0 };
assign w_ctable_reg = tlb_cdata[wr_tlb_addr];
//
generate
if (4+LGHCTX-1>4)
assign w_ptable_reg[(4+LGHCTX-1):4] = {
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX] };
if (LGPGSZB > LGLCTX+4)
assign w_vtable_reg[(LGPGSZB-1):(LGLCTX+4)] = 0;
if (LGPGSZB > LGHCTX+4)
assign w_ptable_reg[(LGPGSZB-1):(LGHCTX+4)] = 0;
endgenerate
 
//
// Now, reading from the bus
/*
wire [(LGCTXT-1):0] w_ctable_reg;
assign w_ctable_reg = tlb_cdata[wr_tlb_addr];
reg setup_this_page_flag;
reg [(LGCTXT-1):0] setup_page;
initial setup_this_page_flag = 1'b0;
always @(posedge i_clk)
setup_page <= w_ctable_reg;
always @(posedge i_clk)
setup_this_page_flag <= (!i_reset)&&(i_wbs_cyc_stb)&&(i_wbs_addr[LGTBL+1]);
*/
setup_this_page_flag <= (i_ctrl_cyc_stb)&&(i_wb_addr[LGTBL+1]);
always @(posedge i_clk)
case({i_wb_addr[LGTBL+1],i_wb_addr[0]})
2'b00: setup_data <= w_control_data;
2'b01: setup_data <= status_word;
2'b10: setup_data <= w_vtable_reg;
2'b11: setup_data <= w_ptable_reg;
endcase
 
 
 
492,6 → 415,7
//
//
//////////////////////////////////////////
assign w_mmu_err_vaddr = { {(DW-AW){1'b0}}, r_mmu_err_vaddr };
 
//
//
500,280 → 424,125
// work.
//
//
wire [(VAW-1):0] r_vpage;
wire [(PAW-1):0] r_ppage;
assign r_vpage = (r_addr[(DW-3):(DW-2-VAW)]);
assign r_ppage = (o_addr[(AW-1):LGPGSZW]);
 
initial s_pending = 1'b0;
initial r_pending = 1'b0;
initial r_valid = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
r_pending <= 1'b0;
r_valid <= 1'b0;
o_addr <= 0;
r_we <= 0;
r_exe <= 0;
r_addr <= 0;
r_data <= 0;
r_sel <= 0;
//
s_pending <= 1'b0;
end else
begin
if (!o_rtn_stall)
begin
r_pending <= (i_wbm_stb)&&(!kernel_context)
&&(!this_page_valid);
r_we <= i_wbm_we;
r_exe <= i_wbm_exe;
o_addr <= { { (kernel_context)?
i_wbm_addr[(AW-1):LGPGSZW] : last_ppage },
i_wbm_addr[(LGPGSZW-1):0] };
r_addr <= i_wbm_addr;
r_data <= i_wbm_data;
r_sel <= i_wbm_sel;
r_valid <= (i_wbm_stb)&&((kernel_context)||(this_page_valid));
r_pending <= i_wbm_stb;
r_we <= i_wb_we;
r_addr <= i_wb_addr;
r_data <= i_wb_data;
r_valid <= (i_wbm_stb)&&((z_context)||((last_page_valid)
&&(i_wb_addr[(DW-1):LGPGSZ] == last_vpage)
&&((!last_ro)||(!i_wb_we))));
s_pending <= 1'b0;
end else if (!r_valid) begin
r_valid <= (pending_page_valid);
o_addr <= { ppage , r_addr[(LGPGSZW-1):0] };
r_pending<= (r_pending)&&(!pending_page_valid);
s_pending <=(r_pending)&&(!pending_page_valid);
end else begin
r_pending <= 1'b0;
s_pending <= 1'b0;
r_valid <= (r_valid)||((last_page_valid)
&&(r_addr[(DW-1):LGPGSZ] == last_vpage)
&&((!last_ro)||(!r_we)));
r_pending<= (r_pending)&&(i_wbm_cyc);
s_pending <= r_pending;
end
 
if ((!i_wbm_cyc)||(o_rtn_err)||((o_cyc)&&(i_err)))
begin
s_pending <= 1'b0;
if (i_reset)
r_pending <= 1'b0;
r_valid <= 1'b0;
end
end
 
`ifdef FORMAL
reg f_past_valid;
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(r_pending))&&(r_pending)&&($past(o_rtn_stall))&&(i_wbm_cyc)&&(!o_stb))
assert(s_pending);
`endif
 
// Second clock: know which buffer entry this belong in.
// If we don't already know, then the pipeline must be stalled for a
// while ...
genvar k, s;
generate
for(k=0; k<TBL_SIZE; k = k + 1)
for(k=0; k<TBL_BITS; k = k + 1)
assign r_tlb_match[k] =
// The page must be valid
(tlb_valid[k])
// Virtual address must match
&&(tlb_vdata[k] == r_vpage)
((tlb_vdata[k] == r_addr[(DW-1):LGPGSZ])
// Context must match as well
&&(tlb_cdata[k][LGCTXT-1:1] == r_context_word[LGCTXT-1:1])
&&((!tlb_cdata[k][0])||(r_context_word[0]));
&&(tlb_cdata[k] == r_context_word));
endgenerate
 
initial s_tlb_miss = 1'b0;
initial s_tlb_hit = 1'b0;
generate
integer i;
always @(posedge i_clk)
begin // valid when s_ becomes valid
s_tlb_addr <= {(LGTBL){1'b0}};
for(i=0; i<TBL_SIZE; i=i+1)
if (r_tlb_match[i])
s_tlb_addr <= i[(LGTBL-1):0];
s_tlb_miss <= (r_pending)&&(r_tlb_match == 0);
for(k=0; k<TBL_SIZE; k=k+1)
for(s=0; s<LGTBL; s=s+1)
if (((k&(1<<s))!=0)&&(r_tlb_match[k]))
s_tlb_addr[s] <= 1'b1;
s_tlb_miss <= (r_pending)&&(r_tlb_match[(TBL_BITS-1):0] == 0);
s_tlb_hit <= 1'b0;
for(i=0; i<TBL_SIZE; i=i+1)
if (r_tlb_match == (1<<i))
s_tlb_hit <= (r_pending)&&(!r_valid)&&(i_wbm_cyc);
for(k=0; k<TBL_SIZE; k=k+1)
if (r_tlb_match == (1<<k))
s_tlb_hit <= (r_pending);
end endgenerate
 
 
// Third clock: Read from the address the virtual table offset,
// whether read-only, etc.
assign s_tlb_flags = tlb_flags[s_tlb_addr];
assign ro_flag = s_tlb_flags[`ROFLAG];
assign exe_flag = s_tlb_flags[`EXEFLG];
assign cachable = s_tlb_flags[`CHFLAG];
assign ro_flag = tlb_flags[s_tlb_addr][3];
assign simple_miss = (s_pending)&&(s_tlb_miss);
assign ro_miss = (s_pending)&&(s_tlb_hit)&&(r_we)&&(ro_flag);
assign exe_miss = (s_pending)&&(s_tlb_hit)&&(r_exe)&&(!exe_flag);
assign table_err = (s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit);
assign vpage = tlb_vdata[s_tlb_addr];
assign ppage = tlb_pdata[s_tlb_addr];
 
initial pf_cachable = 1'b0;
always @(posedge i_clk)
if (i_reset)
pf_cachable <= 1'b0;
else
pf_cachable <= cachable;
 
assign cachable = tlb_flags[s_tlb_addr][1];
// assign tlb_access_flag = tlb_flags[s_tlb_addr][2];
initial pf_stb = 1'b0;
initial last_ppage = 0;
initial last_vpage = 0;
initial p_tlb_err = 1'b0;
initial p_tlb_miss = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
pf_stb <= 1'b0;
last_ppage <= 0;
last_vpage <= 0;
last_tlb <= 0;
end else if ((!kernel_context)&&(r_pending)&&(!last_page_valid))
begin
last_tlb <= s_tlb_addr;
last_ppage <= ppage;
last_vpage <= vpage;
last_exe <= exe_flag;
last_ro <= ro_flag;
pf_stb <= 1'b1;
end else
pf_stb <= 1'b0;
p_tlb_miss <= (simple_miss)||(ro_miss);
p_tlb_err <= (s_pending)&&((!s_tlb_miss)&&(!s_tlb_hit));
 
initial status_word = 0;
always @(posedge i_clk)
if (i_reset)
status_word <= 0;
else if (wr_control)
status_word <= 0;
else if ((table_err)||(ro_miss)||(simple_miss)||(exe_miss))
status_word <= { r_vpage,
{(LGPGSZB-4){1'b0}},
(table_err), (exe_miss),
(ro_miss), (simple_miss) };
pf_cachable <= cachable;
if ((!z_context)&&(r_pending))
begin
last_ppage <= tlb_pdata[s_tlb_addr];
last_vpage <= tlb_vdata[s_tlb_addr];
last_ro <= ro_flag;
pf_stb <= 1'b1;
end else
pf_stb <= 1'b0;
if ((table_err)||(ro_miss)||(simple_miss))
status_word <= { r_addr[(DW-1):LGPGSZ],
{(LGPGSZ-3){1'b0}},
(table_err), (ro_miss), (simple_miss) };
if (wr_control)
last_page_valid <= (last_page_valid)
&&(r_context_word == i_wb_data[(LGCTXT-1):0]);
else if ((r_pending)&&(!z_context))
last_page_valid <= (s_tlb_hit)&&(!ro_miss);
 
initial last_page_valid = 1'b0;
always @(posedge i_clk)
if (i_reset)
last_page_valid <= 1'b0;
else if ((i_wbs_cyc_stb)&&(i_wbs_we))
last_page_valid <= 1'b0;
else if (!kernel_context)
begin
if (!o_rtn_stall)
// A new bus request
last_page_valid <= (last_page_valid)
&&(i_wbm_addr[(DW-3):(DW-2-VAW)] == last_vpage);
else if ((r_pending)&&(!last_page_valid))
last_page_valid <= (s_pending)&&(s_tlb_hit);
if (i_reset)
last_page_valid <= 1'b0;
end
 
parameter LGFIFO = 6;
reg [LGFIFO-1:0] bus_outstanding;
initial bus_outstanding = 0;
always @(posedge i_clk)
if (i_reset)
bus_outstanding <= 0;
else if (!o_cyc)
bus_outstanding <= 0;
else case({ (o_stb)&&(!i_stall), (i_ack)||(i_err) } )
2'b01: bus_outstanding <= bus_outstanding - 1'b1;
2'b10: bus_outstanding <= bus_outstanding + 1'b1;
default: begin end
endcase
 
reg bus_pending;
initial bus_pending = 0;
always @(posedge i_clk)
if (i_reset)
bus_pending <= 0;
else if (!o_cyc)
bus_pending <= 1'b0;
else case({ (o_stb)&&(!i_stall), ((i_ack)||(i_err)) })
2'b01: bus_pending <= (bus_outstanding > 1);
2'b10: bus_pending <= 1'b1;
default: begin end
endcase
 
initial rtn_err = 1'b0;
initial o_cyc = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
o_cyc <= 1'b0;
rtn_err <= 1'b0;
end else begin
o_cyc <= (i_wbm_cyc)&&(!o_rtn_err)&&((!i_err)||(!o_cyc)); /// &&((o_cyc)||(r_valid));
o_cyc <= (!i_reset)&&(i_wbm_cyc);
 
rtn_err <= (i_wbm_cyc)&&(i_err)&&(o_cyc);
o_rtn_ack <= (!i_reset)&&((setup_ack)||(i_wbm_cyc)&&(i_ack));
o_rtn_data <= (setup_ack) ? setup_data : i_data;
if (setup_this_page_flag)
o_rtn_data[0] <= ((setup_page == r_context_word)? 1'b1:1'b0);
rtn_err <= (!i_reset)&&(i_wbm_cyc)&&(i_err);
end
 
generate if (OPT_DELAY_RETURN)
begin
reg r_rtn_ack;
reg [31:0] r_rtn_data;
 
initial r_rtn_data = 0;
initial r_rtn_ack = 0;
always @(posedge i_clk)
if (i_reset)
begin
r_rtn_ack <= 0;
r_rtn_data <= 0;
end else begin
r_rtn_ack <= (i_wbm_cyc)&&(i_ack)&&(o_cyc);
r_rtn_data <= i_data;
end
 
assign o_rtn_ack = r_rtn_ack;
assign o_rtn_data = r_rtn_data;
end else begin
 
assign o_rtn_ack = (i_ack)&&(o_cyc);
assign o_rtn_data = i_data;
end endgenerate
 
assign o_stb = (r_valid);
assign o_we = (r_we);
assign o_rtn_stall = (i_wbm_cyc)&&(
(o_rtn_err)
||((r_pending)&&(!r_valid))
||((o_stb)&&(i_stall))
||(miss_pending));
assign o_rtn_stall = (i_wbm_cyc)&&(((r_pending)&&(!r_valid))||(i_stall));
assign o_rtn_miss = p_tlb_miss;
assign o_rtn_err = (rtn_err)||(p_tlb_err);
 
initial miss_pending = 0;
always @(posedge i_clk)
if (i_reset)
miss_pending <= 0;
else if (!i_wbm_cyc)
miss_pending <= 0;
else
miss_pending <= (i_wbm_cyc)&&(
(simple_miss)||(ro_miss)||(exe_miss)
||((s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit)));
 
assign o_rtn_miss = (miss_pending)&&(!bus_pending);
assign o_rtn_err = (rtn_err);
 
assign o_sel = r_sel;
assign o_addr[(AW-1):0] = {(z_context)?
r_addr[(AW-1):LGPGSZ] : last_ppage,
r_addr[(LGPGSZ-1):0]};
assign o_data = r_data;
 
//
assign o_wbs_stall = 1'b0;
initial o_wbs_ack = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_wbs_ack <= 1'b0;
else
o_wbs_ack <= (i_wbs_cyc_stb);
always @(posedge i_clk)
if (i_reset)
o_wbs_data <= 0;
else case({i_wbs_addr[LGTBL+1],i_wbs_addr[0]})
2'b00: o_wbs_data <= w_control_data;
2'b01: o_wbs_data <= status_word;
2'b10: o_wbs_data <= w_vtable_reg;
2'b11: o_wbs_data <= w_ptable_reg;
endcase
 
//
// Bus snooping returns ...
//
assign pf_return_stb = pf_stb;
782,378 → 551,4
assign pf_return_v = last_vpage;
assign pf_return_cachable = pf_cachable;
 
// Also requires being told when/if the page changed
// So, on a page change,
// pf_return_we = 1
// pf_stb = 1
// and pf_return_p has the physical address
 
// Make verilator happy
// verilator lint_off UNUSED
wire [(PAW-1):0] unused;
assign unused = r_ppage;
generate if (4+LGCTXT < LGPGSZB)
begin
wire [LGPGSZB-(4+LGCTXT)-1:0] unused_data;
assign unused_data = i_wbs_data[LGPGSZB-1:4+LGCTXT];
end endgenerate
 
wire unused_always;
assign unused_always = s_tlb_flags[0];
// verilator lint_on UNUSED
 
`ifdef FORMAL
initial f_past_valid = 0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
 
initial assume(i_reset);
always @(*)
if (!f_past_valid)
assume(i_reset);
 
always @(*)
if (i_reset)
assume(!i_wbs_cyc_stb);
always @(posedge i_clk)
if (f_past_valid)
assert(o_wbs_ack == $past(i_wbs_cyc_stb));
always @(*)
assert(o_wbs_stall == 1'b0);
 
always @(*)
assume((!i_wbm_cyc)||(!i_wbs_cyc_stb));
 
localparam F_LGDEPTH = 6;
reg [F_LGDEPTH-1:0] fv_nreqs, fv_nacks, fv_outstanding,
fp_nreqs, fp_nacks, fp_outstanding;
 
localparam F_MAX_STALL = 3,
F_MAX_WAIT = 2,
F_MAX_REQ = 9;
 
//
// The stall period needs to be long enough to allow all in-progress
// transactions to complete, as in the case of a page miss. Hence,
// the max stall amount depends upon the max wait time for the
// physical half of the interaction. It is artificially limited here
// in order to limit the amount of proof time required.
//
fwb_slave #(.F_MAX_STALL(F_MAX_STALL+(F_MAX_WAIT*F_MAX_REQ)+2),
.AW(DW-2),
.F_MAX_ACK_DELAY(F_MAX_STALL+F_MAX_WAIT+5),
.F_MAX_REQUESTS(F_MAX_REQ),
.F_LGDEPTH(F_LGDEPTH),
.F_OPT_MINCLOCK_DELAY(0))
busslave(i_clk, i_reset,
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_addr,
i_wbm_data, i_wbm_sel,
o_rtn_ack, o_rtn_stall, o_rtn_data,
o_rtn_err|o_rtn_miss,
fv_nreqs, fv_nacks, fv_outstanding);
 
fwb_master #(.F_MAX_STALL(F_MAX_STALL),
.AW(ADDRESS_WIDTH),
.F_MAX_ACK_DELAY(F_MAX_WAIT),
.F_MAX_REQUESTS(F_MAX_REQ),
.F_LGDEPTH(F_LGDEPTH),
.F_OPT_MINCLOCK_DELAY(0))
busmaster(i_clk, i_reset,
o_cyc, o_stb, o_we, o_addr,
o_data, o_sel,
i_ack, i_stall, i_data, i_err,
fp_nreqs, fp_nacks, fp_outstanding);
 
always @(*)
assert((!o_cyc)||(fp_outstanding == bus_outstanding));
 
always @(*)
assume(fv_nreqs < F_MAX_REQ);
always @(*)
if ((i_wbm_cyc)&&(o_cyc)&&(fv_outstanding == fp_outstanding))
assert(fv_nreqs == fp_nreqs);
always @(*)
if ((i_wbm_cyc)&&(o_cyc))
begin
assert(fp_nreqs <= fv_nreqs);
assert(fp_nacks >= fv_nacks);
end
 
reg [F_LGDEPTH-1:0] f_expected, f_ex_nreqs, f_ex_nacks;
always @(*)
if (!i_wbm_cyc)
begin
f_ex_nreqs <= 0;
f_ex_nacks <= 0;
f_expected <= 0;
end else if (OPT_DELAY_RETURN)
begin
if (r_pending)
begin
f_ex_nreqs <= fp_nreqs + 1'b1;
f_ex_nacks <= fp_nacks + o_rtn_ack;
f_expected <= fp_outstanding + 1'b1
+ o_rtn_ack;
end else begin
f_expected <= fp_outstanding + (o_stb)
+ (o_rtn_ack);
f_ex_nreqs <= fp_nreqs + o_stb;
f_ex_nacks <= fp_nacks + o_rtn_ack;
end
end else begin
if (r_pending)
begin
f_ex_nreqs <= fp_nreqs + 1'b1;
f_ex_nacks <= fp_nacks;
f_expected <= fp_outstanding + 1'b1;
end else begin
f_ex_nreqs <= fp_nreqs + o_stb;
f_ex_nacks <= fp_nacks;
f_expected <= fp_outstanding + (o_stb);
end
end
 
reg f_kill_input;
initial f_kill_input = 1'b0;
always @(posedge i_clk)
f_kill_input <= (i_wbm_cyc)&&(
(i_reset)
||(o_rtn_miss)
||(o_rtn_err));
always @(*)
if (f_kill_input)
assume(!i_wbm_cyc);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_rtn_miss))&&($past(i_wbm_cyc)))
begin
assume(!o_cyc);
assume(!i_wbm_cyc);
end
 
wire fv_is_one, fp_is_zero;
assign fv_is_one = (fv_outstanding == 1);
assign fp_is_zero = (fp_outstanding == 0);
always @(*)
if ((i_wbm_cyc)&&(o_cyc))
begin
if (o_rtn_miss)
begin
assert(fp_outstanding == 0);
assert(fv_outstanding == 1);
assert(fv_is_one);
assert(fp_is_zero);
end else begin
assert(fv_nreqs == f_ex_nreqs);
assert(fv_nacks == f_ex_nacks);
assert(fv_outstanding >= fp_outstanding);
assert(fv_outstanding == f_expected);
end
end
 
always @(*)
assert(z_context == (r_context_word == 0));
always @(*)
assert(kernel_context == ( ((r_context_word == 0)||(!i_gie)) ? 1'b1 : 1'b0));
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_wbs_cyc_stb)))
assume(!i_wbm_cyc);
always @(*)
if (o_wbs_ack)
assume(!i_wbm_cyc);
 
always @(*)
assert((!i_wbm_cyc)||(!o_wbs_ack));
always @(posedge i_clk)
if ((f_past_valid)&&(r_pending)&&($past(kernel_context))
&&($past(i_wbm_stb))&&(!$past(i_stall))&&(i_wbm_cyc)
&&(!o_rtn_stall))
assert(o_addr[(AW-1):0] == $past(i_wbm_addr[(AW-1):0]));
always @(*)
assert(bus_pending == (bus_outstanding > 0));
 
always @(*)
if ((s_pending)&&(!s_tlb_miss))
assert(r_tlb_match[s_tlb_addr]);
 
// Check out all of the criteria which should clear these flags
always @(posedge i_clk)
if ((f_past_valid)&&(($past(i_reset))
||(!$past(i_wbm_cyc))
||(!$past(o_rtn_stall))))
begin
assert(!simple_miss);
assert(!ro_miss);
assert(!exe_miss);
assert(!table_err);
if (!$past(i_wbm_we))
assert(!ro_miss);
 
if (!kernel_context)
begin
assert((!o_stb)||(!(simple_miss|ro_miss|table_err)));
// This doesn't belong on the clear list, but on the
// should be set list
// assert((!o_stb)||(!s_tlb_hit));
end
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
&&(!$past(o_rtn_stall)))
begin
if ((!$past(kernel_context))&&(o_stb))
assert((last_page_valid)||(s_tlb_hit));
end
 
reg [(LGTBL-1):0] f_last_page;
always @(posedge i_clk)
if ((f_past_valid)&&(!kernel_context)&&(r_pending)&&(!last_page_valid))
f_last_page <= s_tlb_addr;
 
wire [3:0] tlb_flag_last_page;
assign tlb_flag_last_page = tlb_flags[f_last_page];
always @(*)
if (last_page_valid)
begin
assert(tlb_valid[f_last_page]);
assert(last_tlb == f_last_page);
assert(last_ppage == tlb_pdata[f_last_page]);
assert(last_vpage == tlb_vdata[f_last_page]);
assert(last_ro == tlb_flag_last_page[`ROFLAG]);
assert(last_exe == tlb_flag_last_page[`EXEFLG]);
assert(r_context_word[LGCTXT-1:1] == tlb_cdata[f_last_page][LGCTXT-1:1]);
if (!r_context_word[0])
assert(!tlb_cdata[f_last_page][0]);
assert((!r_context_word[0])||(r_context_word[0]));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(last_page_valid))&&(!$past(kernel_context))
&&($past(o_stb))&&($past(i_wbm_cyc)))
assert(tlb_accessed[$past(last_tlb)]);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(pending_page_valid))&&(!$past(kernel_context))
&&($past(o_stb))&&($past(i_wbm_cyc)))
assert(tlb_accessed[$past(s_tlb_addr)]);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(kernel_context))&&(o_stb))
begin
assert(last_page_valid);
assert(r_ppage == last_ppage);
assert((!last_ro)||(!o_we));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(o_stb))&&(o_stb)&&(i_wbm_cyc))
assert((last_page_valid)||(kernel_context));
 
always @(*)
assert((!s_tlb_hit)||(!s_tlb_miss));
// always @(*)
// if ((fp_outstanding > 0)&&(o_cyc)&&(!o_stb)&&(!r_pending)&&(!kernel_context))
// assert(last_page_valid);
// always @(*) assume(kernel_context);
always @(*)
assume((!i_wbs_cyc_stb)||(!i_gie));
 
reg f_past_gie, f_past_wbm_cyc;
 
initial f_past_gie = 1'b0;
always @(posedge i_clk)
f_past_gie <= i_gie;
 
initial f_past_wbm_cyc = 1'b0;
always @(posedge i_clk)
f_past_wbm_cyc <= i_wbm_cyc;
always @(*)
if ((f_past_valid)&&(bus_pending))
assume(i_gie == f_past_gie);
always @(*)
if ((f_past_wbm_cyc)&&(i_wbm_cyc))
assume(i_gie == f_past_gie);
 
always @(posedge i_clk)
if ((f_past_valid)&&(i_wbm_cyc)&&($past(i_wbm_cyc)))
assume(i_gie == $past(i_gie));
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_reset)))
assume(!i_gie);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
&&($past(!kernel_context))
&&($past(r_pending))
&&(!$past(last_page_valid)))
begin
if (($past(s_tlb_hit))
&&(!$past(ro_miss))
&&(!$past(exe_miss)))
begin
assert(last_vpage == $past(r_vpage));
assert(last_page_valid);
assert(!miss_pending);
assert(tlb_accessed[s_tlb_addr]);
end else if (($past(s_tlb_hit))&&($past(ro_miss)))
begin
assert(miss_pending);
assert(last_page_valid);
assert(status_word[3:0] == 4'h2);
end else if (($past(s_tlb_hit))&&($past(exe_miss)))
begin
assert(miss_pending);
assert(last_page_valid);
assert(status_word[3:0] == 4'h4);
end else if (($past(s_tlb_hit))&&($past(simple_miss)))
begin
assert(miss_pending);
assert(last_page_valid);
assert(status_word[3:0] == 4'h1);
end else if (!$past(s_tlb_hit))
begin
assert(!last_page_valid);
end
end
 
always @(*)
assert((!ro_miss)||(!exe_miss)||(!simple_miss)||(!table_err));
 
reg [4:0] f_tlb_pipe;
 
initial f_tlb_pipe = 5'h0;
always @(posedge i_clk)
if (i_reset)
f_tlb_pipe <= 5'h0;
else if ((!r_pending)||(o_stb))
f_tlb_pipe <= 5'h0;
else if ((r_pending)&&(!r_valid)&&(!miss_pending))
f_tlb_pipe <= { f_tlb_pipe[3:0], 1'b1 };
 
always @(*)
assert(f_tlb_pipe != 5'h1f);
 
always @(*) // WE or EXE, never both
assume((!i_wbm_stb)||(!i_wbm_we)||(!i_wbm_exe));
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_wbm_stb))&&($past(o_rtn_stall)))
assume(i_wbm_exe == $past(i_wbm_exe));
 
always @(*)
assert((!r_pending)||(!o_stb));
always @(*)
assert((!s_pending)||(!o_stb));
always @(*)
assert((!s_pending)||(r_pending));
always @(posedge i_clk)
if ((f_past_valid)&&($past(i_wbm_cyc)))
assume(!i_wbs_cyc_stb);
 
always @(posedge i_clk)
if ((f_past_valid)&&(|status_word[3:0])&&(!$past(i_wbm_cyc)))
assume(!i_gie);
`endif
endmodule
/peripherals/ziptimer.v
45,7 → 45,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
69,17 → 69,15
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module ziptimer(i_clk, i_reset, i_ce,
module ziptimer(i_clk, i_rst, i_ce,
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data,
o_wb_ack, o_wb_stall, o_wb_data,
o_int);
parameter BW = 32, VW = (BW-1), RELOADABLE=1;
input wire i_clk, i_reset, i_ce;
input i_clk, i_rst, i_ce;
// Wishbone inputs
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
90,34 → 88,31
reg r_running;
 
wire wb_write;
assign wb_write = ((i_wb_stb)&&(i_wb_we));
assign wb_write = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we));
 
wire auto_reload;
wire [(VW-1):0] interval_count;
wire auto_reload;
wire [(VW-1):0] reload_value;
 
initial r_running = 1'b0;
always @(posedge i_clk)
if (i_reset)
if (i_rst)
r_running <= 1'b0;
else if (wb_write)
r_running <= (|i_wb_data[(VW-1):0]);
else if ((r_zero)&&(!auto_reload))
else if ((o_int)&&(~auto_reload))
r_running <= 1'b0;
 
generate
if (RELOADABLE != 0)
begin
reg r_auto_reload;
reg [(VW-1):0] r_interval_count;
reg r_auto_reload;
reg [(VW-1):0] r_reload_value;
 
initial r_auto_reload = 1'b0;
 
always @(posedge i_clk)
if (i_reset)
r_auto_reload <= 1'b0;
else if (wb_write)
r_auto_reload <= (i_wb_data[(BW-1)])
&&(|i_wb_data[(VW-1):0]);
if (wb_write)
r_auto_reload <= (i_wb_data[(BW-1)]);
 
assign auto_reload = r_auto_reload;
 
124,12 → 119,12
// If setting auto-reload mode, and the value to other
// than zero, set the auto-reload value
always @(posedge i_clk)
if (wb_write)
r_interval_count <= i_wb_data[(VW-1):0];
assign interval_count = r_interval_count;
if ((wb_write)&&(i_wb_data[(BW-1)])&&(|i_wb_data[(VW-1):0]))
r_reload_value <= i_wb_data[(VW-1):0];
assign reload_value = r_reload_value;
end else begin
assign auto_reload = 1'b0;
assign interval_count = 0;
assign reload_value = 0;
end endgenerate
 
 
136,44 → 131,27
reg [(VW-1):0] r_value;
initial r_value = 0;
always @(posedge i_clk)
if (i_reset)
r_value <= 0;
else if (wb_write)
if (wb_write)
r_value <= i_wb_data[(VW-1):0];
else if ((i_ce)&&(r_running))
begin
if (!r_zero)
r_value <= r_value - 1'b1;
else if (auto_reload)
r_value <= interval_count;
end
else if ((r_running)&&(i_ce)&&(~o_int))
r_value <= r_value + {(VW){1'b1}}; // r_value - 1;
else if ((r_running)&&(auto_reload)&&(o_int))
r_value <= reload_value;
 
reg r_zero = 1'b1;
always @(posedge i_clk)
if (i_reset)
r_zero <= 1'b1;
else if (wb_write)
r_zero <= (i_wb_data[(VW-1):0] == 0);
else if ((r_running)&&(i_ce))
begin
if (r_value == { {(VW-1){1'b0}}, 1'b1 })
r_zero <= 1'b1;
else if ((r_zero)&&(auto_reload))
r_zero <= 1'b0;
end
 
// Set the interrupt on our last tick, as we transition from one to
// zero.
initial o_int = 1'b0;
always @(posedge i_clk)
if ((i_reset)||(wb_write)||(!i_ce))
if (i_rst)
o_int <= 1'b0;
else // if (i_ce)
o_int <= (r_value == { {(VW-1){1'b0}}, 1'b1 });
else if (i_ce)
o_int <= (r_running)&&(r_value == { {(VW-1){1'b0}}, 1'b1 });
else
o_int <= 1'b0;
 
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
o_wb_ack <= (!i_reset)&&(i_wb_stb);
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb);
assign o_wb_stall = 1'b0;
 
generate
183,103 → 161,4
assign o_wb_data = { auto_reload, r_value };
endgenerate
 
// Make verilator happy
// verilator lint_off UNUSED
wire [32:0] unused;
assign unused = { i_wb_cyc, i_wb_data };
// 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;
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(r_value == 0);
assert(r_running == 0);
assert(auto_reload == 0);
assert(r_zero == 1'b1);
end
 
 
always @(*)
assert(r_zero == (r_value == 0));
 
always @(*)
if (r_value != 0)
assert(r_running);
 
always @(*)
if (auto_reload)
assert(r_running);
 
always @(*)
if (!RELOADABLE)
assert(auto_reload == 0);
 
always @(*)
if (auto_reload)
assert(interval_count != 0);
 
always @(posedge i_clk)
if ((f_past_valid)&&($past(r_value)==0)
&&(!$past(wb_write))&&(!$past(auto_reload)))
assert(r_value == 0);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(wb_write))
&&($past(r_value)==0)&&($past(auto_reload)))
begin
if ($past(i_ce))
assert(r_value == interval_count);
else
assert(r_value == $past(r_value));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&(!$past(wb_write))&&($past(r_value)!=0))
begin
if ($past(i_ce))
assert(r_value == $past(r_value)-1'b1);
else
assert(r_value == $past(r_value));
end
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(wb_write)))
assert(r_value == $past(i_wb_data[(VW-1):0]));
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(wb_write))
&&(RELOADABLE)&&(|$past(i_wb_data[(VW-1):0])))
assert(auto_reload == $past(i_wb_data[(BW-1)]));
 
always @(posedge i_clk)
if (!(f_past_valid)||($past(i_reset)))
assert(!o_int);
else if (($past(wb_write))||(!$past(i_ce)))
assert(!o_int);
else
assert(o_int == ((r_running)&&(r_value == 0)));
 
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
assert(!o_wb_ack);
else if ($past(i_wb_stb))
assert(o_wb_ack);
 
always @(*)
assert(!o_wb_stall);
always @(*)
assert(o_wb_data[BW-1] == auto_reload);
always @(*)
assert(o_wb_data[VW-1:0] == r_value);
`endif
endmodule
/zipbones.v
13,7 → 13,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
26,7 → 26,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// 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.
//
37,16 → 37,9
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`include "cpudefs.v"
//
`define RESET_BIT 6
`define STEP_BIT 8
`define HALT_BIT 10
`define CLEAR_CACHE_BIT 11
//
module zipbones(i_clk, i_reset,
module zipbones(i_clk, i_rst,
// Wishbone master interface from the CPU
o_wb_cyc, o_wb_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,
58,78 → 51,46
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data,
o_dbg_ack, o_dbg_stall, o_dbg_data
`ifdef DEBUG_SCOPE
, o_cpu_debug
, o_zip_debug
`endif
);
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30,
LGICACHE=8;
parameter [0:0] START_HALTED=0;
parameter EXTERNAL_INTERRUPTS=1,
`ifdef OPT_MULTIPLY
IMPLEMENT_MPY = `OPT_MULTIPLY;
`else
IMPLEMENT_MPY = 0;
`endif
parameter [0:0]
`ifdef OPT_DIVIDE
IMPLEMENT_DIVIDE=1,
`else
IMPLEMENT_DIVIDE=0,
`endif
`ifdef OPT_IMPLEMENT_FPU
IMPLEMENT_FPU=1,
`else
IMPLEMENT_FPU=0,
`endif
IMPLEMENT_LOCK=1;
localparam // Derived parameters
PHYSICAL_ADDRESS_WIDTH=ADDRESS_WIDTH,
PAW=ADDRESS_WIDTH,
`ifdef OPT_MMU
VIRTUAL_ADDRESS_WIDTH=30,
`else
VIRTUAL_ADDRESS_WIDTH=PAW,
`endif
LGTLBSZ = 6,
VAW=VIRTUAL_ADDRESS_WIDTH;
 
LGICACHE=8, START_HALTED=0;
localparam AW=ADDRESS_WIDTH;
input wire i_clk, i_reset;
input i_clk, i_rst;
// Wishbone master
output wire o_wb_cyc, o_wb_stb, o_wb_we;
output wire [(PAW-1):0] o_wb_addr;
output wire [(AW-1):0] o_wb_addr;
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
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;
// Incoming interrupts
input wire i_ext_int;
input i_ext_int;
// Outgoing interrupt
output wire o_ext_int;
// Wishbone slave
input wire i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input wire [31:0] i_dbg_data;
output wire o_dbg_ack;
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input [31:0] i_dbg_data;
output reg o_dbg_ack;
output wire o_dbg_stall;
output wire [31:0] o_dbg_data;
//
`ifdef DEBUG_SCOPE
output wire [31:0] o_cpu_debug;
output wire [31:0] o_zip_debug;
`endif
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall;
wire [31:0] dbg_idata, dbg_odata;
reg dbg_ack;
 
assign dbg_cyc = i_dbg_cyc;
assign dbg_stb = i_dbg_stb;
assign dbg_we = i_dbg_we;
assign dbg_addr = i_dbg_addr;
assign dbg_idata = i_dbg_data;
assign o_dbg_ack = dbg_ack;
assign o_dbg_stall = dbg_stall;
assign o_dbg_data = dbg_odata;
//
//
//
wire sys_cyc, sys_stb, sys_we;
wire [4:0] sys_addr;
wire [(AW-1):0] cpu_addr;
wire [31:0] sys_data;
wire sys_ack, sys_stall;
 
//
// The external debug interface
//
// We offer only a limited interface here, requiring a pre-register
146,48 → 107,51
reg cmd_reset, cmd_halt, cmd_step, cmd_clear_pf_cache;
reg [4:0] cmd_addr;
wire [3:0] cpu_dbg_cc;
assign dbg_cmd_write = (dbg_stb)&&(dbg_we)&&(!dbg_addr);
assign dbg_cmd_write = (i_dbg_cyc)&&(i_dbg_stb)&&(i_dbg_we)&&(~i_dbg_addr);
//
// Always start us off with an initial reset
//
initial cmd_reset = 1'b1;
always @(posedge i_clk)
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT]));
cmd_reset <= ((dbg_cmd_write)&&(i_dbg_data[6]));
//
initial cmd_halt = START_HALTED;
always @(posedge i_clk)
if (i_reset)
cmd_halt <= START_HALTED;
else if (cmd_reset)
cmd_halt <= START_HALTED;
else if (dbg_cmd_write)
cmd_halt <= ((dbg_idata[`HALT_BIT])&&(!dbg_idata[`STEP_BIT]));
else if ((cmd_step)||(cpu_break))
cmd_halt <= 1'b1;
if (i_rst)
cmd_halt <= (START_HALTED == 1)? 1'b1 : 1'b0;
else if (dbg_cmd_write)
cmd_halt <= ((i_dbg_data[10])||(i_dbg_data[8]));
else if ((cmd_step)||(cpu_break))
cmd_halt <= 1'b1;
 
initial cmd_clear_pf_cache = 1'b1;
initial cmd_clear_pf_cache = 1'b0;
always @(posedge i_clk)
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]);
if (i_rst)
cmd_clear_pf_cache <= 1'b0;
else if (dbg_cmd_write)
cmd_clear_pf_cache <= i_dbg_data[11];
else
cmd_clear_pf_cache <= 1'b0;
//
initial cmd_step = 1'b0;
always @(posedge i_clk)
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]);
cmd_step <= (dbg_cmd_write)&&(i_dbg_data[8]);
//
initial cmd_addr = 5'h0;
always @(posedge i_clk)
if (dbg_cmd_write)
cmd_addr <= dbg_idata[4:0];
cmd_addr <= i_dbg_data[4:0];
 
wire cpu_reset;
assign cpu_reset = (cmd_reset);
assign cpu_reset = (cmd_reset)||(i_rst);
 
wire cpu_halt, cpu_dbg_stall;
assign cpu_halt = (cmd_halt);
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step));
wire [31:0] cmd_data;
// Values:
// 0x0003f -> cmd_addr mask
// 0x00040 -> reset
// 0x00080 -> PIC interrrupt pending
// 0x00080 -> PIC interrrupts enabled
// 0x00100 -> cmd_step
// 0x00200 -> cmd_stall
// 0x00400 -> cmd_halt
197,17 → 161,19
// 0x10000 -> External interrupt line is high
assign cmd_data = { 7'h00, 8'h00, i_ext_int,
cpu_dbg_cc,
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0,
i_ext_int, cpu_reset, 1'b0, cmd_addr };
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0,
1'b0, cpu_reset, 1'b0, cmd_addr };
 
//
// The CPU itself
//
wire cpu_lcl_cyc, cpu_lcl_stb,
cpu_dbg_we,
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_dbg_we,
cpu_op_stall, cpu_pf_stall, cpu_i_count;
wire [31:0] cpu_data;
wire [31:0] cpu_dbg_data;
assign cpu_dbg_we = ((dbg_stb)&&(dbg_we)&&(dbg_addr));
assign cpu_dbg_we = ((i_dbg_cyc)&&(i_dbg_stb)
&&(i_dbg_we)&&(i_dbg_addr));
zipcpu #(.RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(ADDRESS_WIDTH),
.LGICACHE(LGICACHE),
214,7 → 180,7
.WITH_LOCAL_BUS(0))
thecpu(i_clk, cpu_reset, i_ext_int,
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we,
dbg_idata, cpu_dbg_stall, cpu_dbg_data,
i_dbg_data, cpu_dbg_stall, cpu_dbg_data,
cpu_dbg_cc, cpu_break,
o_wb_cyc, o_wb_stb,
cpu_lcl_cyc, cpu_lcl_stb,
223,23 → 189,17
(i_wb_err)||(cpu_lcl_cyc),
cpu_op_stall, cpu_pf_stall, cpu_i_count
`ifdef DEBUG_SCOPE
, o_cpu_debug
, o_zip_debug
`endif
);
 
// Return debug response values
assign dbg_odata = (!dbg_addr)?cmd_data :cpu_dbg_data;
initial dbg_ack = 1'b0;
assign o_dbg_data = (~i_dbg_addr)?cmd_data :cpu_dbg_data;
initial o_dbg_ack = 1'b0;
always @(posedge i_clk)
dbg_ack <= (dbg_stb)&&(!o_dbg_stall);
assign dbg_stall= (cpu_dbg_stall)&&(dbg_addr);
o_dbg_ack <= (i_dbg_cyc)&&((~i_dbg_addr)||(~o_dbg_stall));
assign o_dbg_stall=(i_dbg_cyc)&&(cpu_dbg_stall)&&(i_dbg_addr);
 
assign o_ext_int = (cmd_halt) && (!i_wb_stall);
assign o_ext_int = (cmd_halt) && (~i_wb_stall);
 
// Make Verilator happy
// verilator lint_off UNUSED
wire [4:0] unused;
assign unused = { dbg_cyc, cpu_lcl_stb, cpu_op_stall, cpu_pf_stall, cpu_i_count };
// verilator lint_on UNUSED
 
endmodule
/zipsystem.v
6,9 → 6,11
//
// Purpose: This portion of the ZIP CPU implements a number of soft
// peripherals to the CPU nearby its CORE. The functionality
// sits on the data bus, and does not include any true external hardware
// peripherals. The peripherals included here include:
// sits on the data bus, and does not include any true
// external hardware peripherals. The peripherals included here
// include:
//
//
// Local interrupt controller--for any/all of the interrupts generated
// here. This would include a pin for interrupts generated
// elsewhere, so this interrupt controller could be a master
19,6 → 21,21
// modern systems (Linux), they tend to send all interrupts to the
// same interrupt vector anyway. Hence, that's what we do here.
//
// Bus Error interrupts -- generates an interrupt any time the wishbone
// bus produces an error on a given access, for whatever purpose
// also records the address on the bus at the time of the error.
//
// Trap instructions
// Writing to this "register" will always create an interrupt.
// After the interrupt, this register may be read to see what
// value had been written to it.
//
// Bit reverse register ... ?
//
// (Potentially an eventual floating point co-processor ...)
//
// Real-time clock
//
// Interval timer(s) (Count down from fixed value, and either stop on
// zero, or issue an interrupt and restart automatically on zero)
// These can be implemented as watchdog timers if desired--the
29,41 → 46,25
// interrupt/time-out line is wired to the reset line instead of
// the interrupt line of the CPU.
//
// Direct Memory Access Controller: This controller allows you to command
// automatic memory moves. Such memory moves will take place
// without the CPU's involvement until they are done. See the
// DMA specification for more information. (Currently contained
// w/in the ZipCPU spec.)
// ROM Memory map
// Set a register to control this map, and a DMA will begin to
// fill this memory from a slower FLASH. Once filled, accesses
// will be from this memory instead of
//
// (Potentially an eventual floating point co-processor ...?)
//
// Busses: The ZipSystem implements a series of busses to make this take
// place. These busses are identified by their prefix:
// Doing some market comparison, let's look at what peripherals a TI
// MSP430 might offer: MSP's may have I2C ports, SPI, UART, DMA, ADC,
// Comparators, 16,32-bit timers, 16x16 or 32x32 timers, AES, BSL,
// brown-out-reset(s), real-time-clocks, temperature sensors, USB ports,
// Spi-Bi-Wire, UART Boot-strap Loader (BSL), programmable digital I/O,
// watchdog-timers,
//
// cpu This is the bus as the CPU sees it. Since the CPU controls
// two busses (a local and a global one), it uses _gbl_ to indicate
// the external bus (going through the MMU if necessary) and
// _lcl_ to indicate a peripheral bus seen here.
//
// mmu Sits between the CPU's wishbone interface and the external
// bus. Has no access to peripherals.
//
// sys A local bus implemented here within this space. This is how the
// CPU talks to the ZipSystem peripherals. However, this bus
// can also be accessed from the external debug bus.
//
// io_dbg
// io_wb
//
// dbg This is identical to the io_dbg bus, but separated by a clock
// dc The output of the DMA controller
//
// 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
87,20 → 88,23
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`include "cpudefs.v"
//
`define RESET_BIT 6
`define STEP_BIT 8
`define HALT_BIT 10
`define CLEAR_CACHE_BIT 11
//
// While I hate adding delays to any bus access, this next delay is required
// to make timing close in my Basys-3 design.
`define DELAY_DBG_BUS
// On my previous version, I needed to add a delay to access the external
// bus. Activate the define below and that delay will be put back into place.
// This particular version no longer needs the delay in order to run at
// 100 MHz. Timing indicates I may even run this at 250 MHz without the
// delay too, so we're doing better. To get rid of this, I placed the logic
// determining whether or not I was accessing the local system bus one clock
// earlier, or into the memops.v file. This also required my wishbone bus
// arbiter to maintain the bus selection as well, so that got updated ...
// you get the picture. But, the bottom line is that I no longer need this
// delay.
//
// `define DELAY_EXT_BUS
// `define DELAY_EXT_BUS // Required no longer!
//
//
// If space is tight, you might not wish to have your performance and
114,31 → 118,27
//
// Now, where am I placing all of my peripherals?
`define PERIPHBASE 32'hc0000000
`define INTCTRL 8'h0 //
`define WATCHDOG 8'h1 // Interrupt generates reset signal
`define BUSWATCHDOG 8'h2 // Sets IVEC[0]
`define CTRINT 8'h3 // Sets IVEC[5]
`define TIMER_A 8'h4 // Sets IVEC[4]
`define TIMER_B 8'h5 // Sets IVEC[3]
`define TIMER_C 8'h6 // Sets IVEC[2]
`define JIFFIES 8'h7 // Sets IVEC[1]
`define INTCTRL 5'h0 //
`define WATCHDOG 5'h1 // Interrupt generates reset signal
`define BUSWATCHDOG 5'h2 // Sets IVEC[0]
`define CTRINT 5'h3 // Sets IVEC[5]
`define TIMER_A 5'h4 // Sets IVEC[4]
`define TIMER_B 5'h5 // Sets IVEC[3]
`define TIMER_C 5'h6 // Sets IVEC[2]
`define JIFFIES 5'h7 // Sets IVEC[1]
 
 
`ifdef INCLUDE_ACCOUNTING_COUNTERS
`define MSTR_TASK_CTR 8'h08
`define MSTR_MSTL_CTR 8'h09
`define MSTR_PSTL_CTR 8'h0a
`define MSTR_INST_CTR 8'h0b
`define USER_TASK_CTR 8'h0c
`define USER_MSTL_CTR 8'h0d
`define USER_PSTL_CTR 8'h0e
`define USER_INST_CTR 8'h0f
`define MSTR_TASK_CTR 5'h08
`define MSTR_MSTL_CTR 5'h09
`define MSTR_PSTL_CTR 5'h0a
`define MSTR_INST_CTR 5'h0b
`define USER_TASK_CTR 5'h0c
`define USER_MSTL_CTR 5'h0d
`define USER_PSTL_CTR 5'h0e
`define USER_INST_CTR 5'h0f
`endif
 
`ifdef OPT_MMU
`define MMU_ADDR 8'h80
`endif
 
// Although I have a hole at 5'h2, the DMA controller requires four wishbone
// addresses, therefore we place it by itself and expand our address bus
// width here by another bit.
161,7 → 161,7
//
//
//
module zipsystem(i_clk, i_reset,
module zipsystem(i_clk, i_rst,
// Wishbone master interface from the CPU
o_wb_cyc, o_wb_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,
176,17 → 176,13
, o_cpu_debug
`endif
);
parameter RESET_ADDRESS=32'h1000_0000, ADDRESS_WIDTH=30,
LGICACHE=10,
LGDCACHE=12; // Set to zero for no data cache
parameter [0:0] START_HALTED=1;
parameter EXTERNAL_INTERRUPTS=1,
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30,
LGICACHE=10, START_HALTED=1, EXTERNAL_INTERRUPTS=1,
`ifdef OPT_MULTIPLY
IMPLEMENT_MPY = `OPT_MULTIPLY;
IMPLEMENT_MPY = `OPT_MULTIPLY,
`else
IMPLEMENT_MPY = 0;
IMPLEMENT_MPY = 0,
`endif
parameter [0:0]
`ifdef OPT_DIVIDE
IMPLEMENT_DIVIDE=1,
`else
199,33 → 195,23
`endif
IMPLEMENT_LOCK=1;
localparam // Derived parameters
PHYSICAL_ADDRESS_WIDTH=ADDRESS_WIDTH,
PAW=ADDRESS_WIDTH,
`ifdef OPT_MMU
VIRTUAL_ADDRESS_WIDTH=30,
`else
VIRTUAL_ADDRESS_WIDTH=PAW,
`endif
LGTLBSZ = 6,
VAW=VIRTUAL_ADDRESS_WIDTH;
 
localparam AW=ADDRESS_WIDTH;
input wire i_clk, i_reset;
AW=ADDRESS_WIDTH;
input i_clk, i_rst;
// Wishbone master
output wire o_wb_cyc, o_wb_stb, o_wb_we;
output wire [(PAW-1):0] o_wb_addr;
output wire [(AW-1):0] o_wb_addr;
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
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;
// Incoming interrupts
input wire [(EXTERNAL_INTERRUPTS-1):0] i_ext_int;
input [(EXTERNAL_INTERRUPTS-1):0] i_ext_int;
// Outgoing interrupt
output wire o_ext_int;
// Wishbone slave
input wire i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input wire [31:0] i_dbg_data;
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input [31:0] i_dbg_data;
output wire o_dbg_ack;
output wire o_dbg_stall;
output wire [31:0] o_dbg_data;
263,19 → 249,11
`endif
else
`ifdef INCLUDE_ACCOUNTING_COUNTERS
if (EXTERNAL_INTERRUPTS >= 15)
assign alt_int_vector = { i_ext_int[14:8],
mtc_int, moc_int, mpc_int, mic_int,
utc_int, uoc_int, upc_int, uic_int };
else
assign alt_int_vector = { {(7-(EXTERNAL_INTERRUPTS-9)){1'b0}},
i_ext_int[(EXTERNAL_INTERRUPTS-1):9],
mtc_int, moc_int, mpc_int, mic_int,
utc_int, uoc_int, upc_int, uic_int };
`else
if (EXTERNAL_INTERRUPTS >= 24)
assign alt_int_vector = { i_ext_int[(EXTERNAL_INTERRUPTS-1):9] };
else
assign alt_int_vector = { {(15-(EXTERNAL_INTERRUPTS-9)){1'b0}},
i_ext_int[(EXTERNAL_INTERRUPTS-1):9] };
`endif
286,15 → 264,11
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall;
wire [31:0] dbg_idata, dbg_odata;
reg dbg_ack;
`ifdef DELAY_DBG_BUS
wire dbg_err, no_dbg_err;
wire [3:0] dbg_sel;
wire no_dbg_err;
`ifdef DELAY_DBG_BUS
// Make verilator happy
// verilator lint_off UNUSED
// verilator lint_on UNUSED
wire dbg_err;
assign dbg_err = 1'b0;
busdelay #(1,32) wbdelay(i_clk, i_reset,
busdelay #(1,32) wbdelay(i_clk,
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data, 4'hf,
o_dbg_ack, o_dbg_stall, o_dbg_data, no_dbg_err,
dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_idata, dbg_sel,
308,8 → 282,6
assign o_dbg_ack = dbg_ack;
assign o_dbg_stall = dbg_stall;
assign o_dbg_data = dbg_odata;
assign dbg_sel = 4'b1111;
assign no_dbg_err = 1'b0;
`endif
 
//
316,26 → 288,12
//
//
wire sys_cyc, sys_stb, sys_we;
wire [7:0] sys_addr;
wire [(PAW-1):0] cpu_addr;
wire [4:0] sys_addr;
wire [(AW-1):0] cpu_addr;
wire [31:0] sys_data;
reg [31:0] sys_idata;
reg sys_ack;
wire sys_stall;
wire sys_ack, sys_stall;
 
wire sel_counter, sel_timer, sel_pic, sel_apic,
sel_watchdog, sel_bus_watchdog, sel_dmac, sel_mmus;
//
assign sel_pic = (sys_stb)&&(sys_addr == `INTCTRL);
assign sel_watchdog = (sys_stb)&&(sys_addr == `WATCHDOG);
assign sel_bus_watchdog= (sys_stb)&&(sys_addr == `BUSWATCHDOG);
assign sel_apic = (sys_stb)&&(sys_addr == `CTRINT);
assign sel_timer = (sys_stb)&&(sys_addr[7:2] == 6'h1);
assign sel_counter = (sys_stb)&&(sys_addr[7:3] == 5'h1);
assign sel_dmac = (sys_stb)&&(sys_addr[7:4] == 4'h1);
assign sel_mmus = (sys_stb)&&(sys_addr[7]);
 
//
// The external debug interface
//
// We offer only a limited interface here, requiring a pre-register
352,44 → 310,39
reg cmd_reset, cmd_halt, cmd_step, cmd_clear_pf_cache;
reg [5:0] cmd_addr;
wire [3:0] cpu_dbg_cc;
assign dbg_cmd_write = (dbg_stb)&&(dbg_we)&&(!dbg_addr);
assign dbg_cmd_write = (dbg_cyc)&&(dbg_stb)&&(dbg_we)&&(~dbg_addr);
//
// Always start us off with an initial reset
//
initial cmd_reset = 1'b1;
always @(posedge i_clk)
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT]))
||(wdt_reset);
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[6]));
//
initial cmd_halt = START_HALTED;
always @(posedge i_clk)
if (i_reset)
cmd_halt <= START_HALTED;
else if (cmd_reset)
cmd_halt <= START_HALTED;
else if (dbg_cmd_write)
cmd_halt <= ((dbg_idata[`HALT_BIT])&&(!dbg_idata[`STEP_BIT]));
else if ((cmd_step)||(cpu_break))
cmd_halt <= 1'b1;
if (i_rst)
cmd_halt <= (START_HALTED == 1)? 1'b1 : 1'b0;
else if (dbg_cmd_write)
cmd_halt <= ((dbg_idata[10])||(dbg_idata[8]));
else if ((cmd_step)||(cpu_break))
cmd_halt <= 1'b1;
 
initial cmd_clear_pf_cache = 1'b1;
always @(posedge i_clk)
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]);
cmd_clear_pf_cache = (~i_rst)&&(dbg_cmd_write)
&&((dbg_idata[11])||(dbg_idata[6]));
//
initial cmd_step = 1'b0;
always @(posedge i_clk)
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]);
cmd_step <= (dbg_cmd_write)&&(dbg_idata[8]);
//
initial cmd_addr = 6'h0;
always @(posedge i_clk)
if (dbg_cmd_write)
cmd_addr <= dbg_idata[5:0];
 
wire cpu_reset;
assign cpu_reset = (cmd_reset);
assign cpu_reset = (cmd_reset)||(wdt_reset)||(i_rst);
 
wire cpu_halt, cpu_dbg_stall;
assign cpu_halt = (cmd_halt);
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step));
wire [31:0] pic_data;
wire [31:0] cmd_data;
// Values:
409,11 → 362,11
assign cmd_data = { {(16-EXTERNAL_INTERRUPTS){1'b0}},
i_ext_int,
cpu_dbg_cc, // 4 bits
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0,
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0,
pic_data[15], cpu_reset, cmd_addr };
else
assign cmd_data = { i_ext_int[15:0], cpu_dbg_cc,
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0,
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0,
pic_data[15], cpu_reset, cmd_addr };
endgenerate
 
426,8 → 379,8
wire wdt_ack, wdt_stall, wdt_reset;
wire [31:0] wdt_data;
ziptimer #(32,31,0)
watchdog(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_watchdog), sys_we,
watchdog(i_clk, cpu_reset, ~cmd_halt,
sys_cyc, ((sys_stb)&&(sys_addr == `WATCHDOG)), sys_we,
sys_data,
wdt_ack, wdt_stall, wdt_data, wdt_reset);
 
438,20 → 391,21
// take less than the number written to this register.
//
reg wdbus_ack;
reg [(PAW-1):0] r_wdbus_data;
reg [(AW-1):0] r_wdbus_data;
wire [31:0] wdbus_data;
wire [14:0] wdbus_ignored_data;
wire reset_wdbus_timer, wdbus_int;
assign reset_wdbus_timer = (!o_wb_cyc)||(o_wb_stb)||(i_wb_ack);
assign reset_wdbus_timer = ((o_wb_cyc)&&((o_wb_stb)||(i_wb_ack)));
wbwatchdog #(14) watchbus(i_clk,(cpu_reset)||(reset_wdbus_timer),
14'h2000, wdbus_int);
o_wb_cyc, 14'h2000, wdbus_int);
initial r_wdbus_data = 0;
always @(posedge i_clk)
if ((wdbus_int)||(cpu_err))
r_wdbus_data <= o_wb_addr;
assign wdbus_data = { {(32-PAW){1'b0}}, r_wdbus_data };
if ((wdbus_int)||(cpu_ext_err))
r_wdbus_data = o_wb_addr;
assign wdbus_data = { {(32-AW){1'b0}}, r_wdbus_data };
initial wdbus_ack = 1'b0;
always @(posedge i_clk)
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sel_bus_watchdog));
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sys_addr == 5'h02));
 
// Counters -- for performance measurement and accounting
//
467,8 → 421,8
// Master task counter
wire mtc_ack, mtc_stall;
wire [31:0] mtc_data;
zipcounter mtask_ctr(i_clk, 1'b0, (!cpu_halt), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b000),
zipcounter mtask_ctr(i_clk, (~cpu_halt), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_TASK_CTR),
sys_we, sys_data,
mtc_ack, mtc_stall, mtc_data, mtc_int);
 
475,8 → 429,8
// Master Operand Stall counter
wire moc_ack, moc_stall;
wire [31:0] moc_data;
zipcounter mmstall_ctr(i_clk,1'b0, (cpu_op_stall), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b001),
zipcounter mmstall_ctr(i_clk,(cpu_op_stall), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_MSTL_CTR),
sys_we, sys_data,
moc_ack, moc_stall, moc_data, moc_int);
 
483,8 → 437,8
// Master PreFetch-Stall counter
wire mpc_ack, mpc_stall;
wire [31:0] mpc_data;
zipcounter mpstall_ctr(i_clk,1'b0, (cpu_pf_stall), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b010),
zipcounter mpstall_ctr(i_clk,(cpu_pf_stall), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_PSTL_CTR),
sys_we, sys_data,
mpc_ack, mpc_stall, mpc_data, mpc_int);
 
491,8 → 445,8
// Master Instruction counter
wire mic_ack, mic_stall;
wire [31:0] mic_data;
zipcounter mins_ctr(i_clk,1'b0, (cpu_i_count), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b011),
zipcounter mins_ctr(i_clk,(cpu_i_count), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_INST_CTR),
sys_we, sys_data,
mic_ack, mic_stall, mic_data, mic_int);
 
503,8 → 457,8
// User task counter
wire utc_ack, utc_stall;
wire [31:0] utc_data;
zipcounter utask_ctr(i_clk,1'b0, (!cpu_halt)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b100),
zipcounter utask_ctr(i_clk,(~cpu_halt)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_TASK_CTR),
sys_we, sys_data,
utc_ack, utc_stall, utc_data, utc_int);
 
511,8 → 465,8
// User Op-Stall counter
wire uoc_ack, uoc_stall;
wire [31:0] uoc_data;
zipcounter umstall_ctr(i_clk,1'b0, (cpu_op_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b101),
zipcounter umstall_ctr(i_clk,(cpu_op_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_MSTL_CTR),
sys_we, sys_data,
uoc_ack, uoc_stall, uoc_data, uoc_int);
 
519,8 → 473,8
// User PreFetch-Stall counter
wire upc_ack, upc_stall;
wire [31:0] upc_data;
zipcounter upstall_ctr(i_clk,1'b0, (cpu_pf_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b110),
zipcounter upstall_ctr(i_clk,(cpu_pf_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_PSTL_CTR),
sys_we, sys_data,
upc_ack, upc_stall, upc_data, upc_int);
 
527,8 → 481,8
// User instruction counter
wire uic_ack, uic_stall;
wire [31:0] uic_data;
zipcounter uins_ctr(i_clk,1'b0, (cpu_i_count)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b111),
zipcounter uins_ctr(i_clk,(cpu_i_count)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_INST_CTR),
sys_we, sys_data,
uic_ack, uic_stall, uic_data, uic_int);
 
564,7 → 518,7
assign uic_int = 1'b0;
 
always @(posedge i_clk)
actr_ack <= sel_counter;
actr_ack <= (sys_stb)&&(sys_addr[4:3] == 2'b01);
`endif // INCLUDE_ACCOUNTING_COUNTERS
 
//
575,14 → 529,14
wire dmac_ack, dmac_stall;
wire dc_cyc, dc_stb, dc_we, dc_ack, dc_stall;
wire [31:0] dc_data;
wire [(PAW-1):0] dc_addr;
wire [(AW-1):0] dc_addr;
wire cpu_gbl_cyc;
wire [31:0] dmac_int_vec;
assign dmac_int_vec = { 1'b0, alt_int_vector, 1'b0,
main_int_vector[14:1], 1'b0 };
assign dmac_stb = (sys_stb)&&(sel_dmac);
assign dmac_stb = (sys_stb)&&(sys_addr[4]);
`ifdef INCLUDE_DMA_CONTROLLER
wbdmac #(PAW) dma_controller(i_clk, cpu_reset,
wbdmac #(AW) dma_controller(i_clk, cpu_reset,
sys_cyc, dmac_stb, sys_we,
sys_addr[1:0], sys_data,
dmac_ack, dmac_stall, dmac_data,
595,7 → 549,6
dmac_int);
`else
reg r_dmac_ack;
initial r_dmac_ack = 1'b0;
always @(posedge i_clk)
r_dmac_ack <= (sys_cyc)&&(dmac_stb);
assign dmac_ack = r_dmac_ack;
605,7 → 558,7
assign dc_cyc = 1'b0;
assign dc_stb = 1'b0;
assign dc_we = 1'b0;
assign dc_addr = { (PAW) {1'b0} };
assign dc_addr = { (AW) {1'b0} };
assign dc_data = 32'h00;
 
assign dmac_int = 1'b0;
614,21 → 567,21
wire ctri_sel, ctri_stall;
reg ctri_ack;
wire [31:0] ctri_data;
assign ctri_sel = (sys_stb)&&(sel_apic);
initial ctri_ack = 1'b0;
assign ctri_sel = (sys_stb)&&(sys_addr == `CTRINT);
always @(posedge i_clk)
ctri_ack <= (ctri_sel)&&(!cpu_reset);
ctri_ack <= ctri_sel;
assign ctri_stall = 1'b0;
`ifdef INCLUDE_ACCOUNTING_COUNTERS
//
// Counter Interrupt controller
//
generate if (EXTERNAL_INTERRUPTS <= 9)
begin : ALT_PIC
generate
if (EXTERNAL_INTERRUPTS <= 9)
begin
icontrol #(8) ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data, alt_int_vector[7:0],
ctri_int);
end else begin : ALT_PIC
end else begin
icontrol #(8+(EXTERNAL_INTERRUPTS-9))
ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data,
638,12 → 591,13
 
`else // INCLUDE_ACCOUNTING_COUNTERS
 
generate if (EXTERNAL_INTERRUPTS <= 9)
begin : ALT_PIC
generate
if (EXTERNAL_INTERRUPTS <= 9)
begin
assign ctri_stall = 1'b0;
assign ctri_data = 32'h0000;
assign ctri_int = 1'b0;
end else begin : ALT_PIC
end else begin
icontrol #(EXTERNAL_INTERRUPTS-9)
ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data,
658,10 → 612,10
//
wire tma_ack, tma_stall;
wire [31:0] tma_data;
ziptimer timer_a(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b00),
sys_we, sys_data,
tma_ack, tma_stall, tma_data, tma_int);
ziptimer timer_a(i_clk, cpu_reset, ~cmd_halt,
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_A), sys_we,
sys_data,
tma_ack, tma_stall, tma_data, tma_int);
 
//
// Timer B
668,10 → 622,10
//
wire tmb_ack, tmb_stall;
wire [31:0] tmb_data;
ziptimer timer_b(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b01),
sys_we, sys_data,
tmb_ack, tmb_stall, tmb_data, tmb_int);
ziptimer timer_b(i_clk, cpu_reset, ~cmd_halt,
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_B), sys_we,
sys_data,
tmb_ack, tmb_stall, tmb_data, tmb_int);
 
//
// Timer C
678,10 → 632,10
//
wire tmc_ack, tmc_stall;
wire [31:0] tmc_data;
ziptimer timer_c(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0]==2'b10),
sys_we, sys_data,
tmc_ack, tmc_stall, tmc_data, tmc_int);
ziptimer timer_c(i_clk, cpu_reset, ~cmd_halt,
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_C), sys_we,
sys_data,
tmc_ack, tmc_stall, tmc_data, tmc_int);
 
//
// JIFFIES
688,8 → 642,8
//
wire jif_ack, jif_stall;
wire [31:0] jif_data;
zipjiffies jiffies(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b11), sys_we,
zipjiffies jiffies(i_clk, ~cmd_halt,
sys_cyc, (sys_stb)&&(sys_addr == `JIFFIES), sys_we,
sys_data,
jif_ack, jif_stall, jif_data, jif_int);
 
697,17 → 651,18
// The programmable interrupt controller peripheral
//
wire pic_interrupt;
generate if (EXTERNAL_INTERRUPTS < 9)
begin : MAIN_PIC
generate
if (EXTERNAL_INTERRUPTS < 9)
begin
icontrol #(6+EXTERNAL_INTERRUPTS) pic(i_clk, cpu_reset,
(sys_cyc)&&(sys_stb)&&(sys_we)
&&(sel_pic),
&&(sys_addr==`INTCTRL),
sys_data, pic_data,
main_int_vector[(6+EXTERNAL_INTERRUPTS-1):0], pic_interrupt);
end else begin : MAIN_PIC
end else begin
icontrol #(15) pic(i_clk, cpu_reset,
(sys_cyc)&&(sys_stb)&&(sys_we)
&&(sel_pic),
&&(sys_addr==`INTCTRL),
sys_data, pic_data,
main_int_vector[14:0], pic_interrupt);
end endgenerate
716,7 → 671,7
assign pic_stall = 1'b0;
reg pic_ack;
always @(posedge i_clk)
pic_ack <= (sys_stb)&&(sel_pic);
pic_ack <= (sys_stb)&&(sys_addr == `INTCTRL);
 
//
// The CPU itself
723,21 → 678,20
//
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_dbg_we;
wire [31:0] cpu_data, cpu_idata;
wire [3:0] cpu_sel, mmu_sel;
wire [31:0] cpu_data, wb_data;
wire [3:0] cpu_sel;
wire cpu_ack, cpu_stall, cpu_err;
wire [31:0] cpu_dbg_data;
assign cpu_dbg_we = ((dbg_cyc)&&(dbg_stb)&&(!cmd_addr[5])
assign cpu_dbg_we = ((dbg_cyc)&&(dbg_stb)&&(~cmd_addr[5])
&&(dbg_we)&&(dbg_addr));
zipcpu #( .RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(VIRTUAL_ADDRESS_WIDTH),
zipcpu #(
.RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(ADDRESS_WIDTH),
.LGICACHE(LGICACHE),
.OPT_LGDCACHE(LGDCACHE),
.IMPLEMENT_MPY(IMPLEMENT_MPY),
.IMPLEMENT_DIVIDE(IMPLEMENT_DIVIDE),
.IMPLEMENT_FPU(IMPLEMENT_FPU),
.IMPLEMENT_LOCK(IMPLEMENT_LOCK),
.WITH_LOCAL_BUS(1'b1)
.IMPLEMENT_LOCK(IMPLEMENT_LOCK)
)
thecpu(i_clk, cpu_reset, pic_interrupt,
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we,
746,8 → 700,8
cpu_gbl_cyc, cpu_gbl_stb,
cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_addr, cpu_data, cpu_sel,
// Return values from the Wishbone bus
cpu_ack, cpu_stall, cpu_idata, cpu_err,
cpu_ack, cpu_stall, wb_data,
cpu_err,
cpu_op_stall, cpu_pf_stall, cpu_i_count
`ifdef DEBUG_SCOPE
, o_cpu_debug
754,112 → 708,24
`endif
);
 
wire ext_ack, ext_stall;
wire mmu_cyc, mmu_stb, mmu_we, mmu_stall, mmu_ack, mmu_err;
wire mmus_ack, mmus_stall;
wire [PAW-1:0] mmu_addr;
wire [31:0] mmu_data, mmu_idata, mmus_data;
 
// Specific responses from the MMU
wire cpu_miss;
 
// The mmu_cpu_ lines are the return bus lines from the MMU. They
// are separate from the cpu_'s lines simply because either the sys_
// (local) bus or the mmu_cpu_ (global) bus might return a response to
// the CPU, and the responses haven't been merged back together again
// yet.
wire mmu_cpu_ack, mmu_cpu_stall;
wire [31:0] mmu_cpu_idata;
 
// The wires associated with cache snooping
wire pf_return_stb, pf_return_we, pf_return_cachable;
wire [19:0] pf_return_v, pf_return_p;
 
`ifdef OPT_MMU
// Ok ... here's the MMU
zipmmu #( .LGTBL(LGTLBSZ),
.ADDRESS_WIDTH(PHYSICAL_ADDRESS_WIDTH)
)
themmu(i_clk, cpu_reset,
// Slave interface
(sys_stb)&&(sel_mmus),
sys_we, sys_addr[7:0], sys_data,
mmus_ack, mmus_stall, mmus_data,
// CPU global bus master lines
cpu_gbl_cyc, cpu_gbl_stb, cpu_we, cpu_addr,
cpu_data, cpu_sel,
// MMU bus master outgoing lines
mmu_cyc, mmu_stb, mmu_we, mmu_addr, mmu_data, mmu_sel,
// .... and the return from the slave(s)
mmu_stall, mmu_ack, mmu_err, mmu_idata,
// CPU gobal bus master return lines
mmu_cpu_stall, mmu_cpu_ack, cpu_err, cpu_miss, mmu_cpu_idata,
pf_return_stb, pf_return_we, pf_return_p, pf_return_v,
pf_return_cachable);
 
`else
assign mmu_cyc = cpu_gbl_cyc;
assign mmu_stb = cpu_gbl_stb;
assign mmu_we = cpu_we;
assign mmu_addr = cpu_addr;
assign mmu_data = cpu_data;
assign mmu_sel = cpu_sel;
assign cpu_miss = 1'b0;
assign cpu_err = (mmu_err)&&(cpu_gbl_cyc);
assign mmu_cpu_idata = mmu_idata;
assign mmu_cpu_stall = mmu_stall;
assign mmu_cpu_ack = mmu_ack;
reg r_mmus_ack;
initial r_mmus_ack = 1'b0;
always @(posedge i_clk)
r_mmus_ack <= (sys_stb)&&(sys_addr[7]);
assign mmus_ack = r_mmus_ack;
assign mmus_stall = 1'b0;
assign mmus_data = 32'h0;
 
assign pf_return_stb = 0;
assign pf_return_v = 0;
assign pf_return_p = 0;
assign pf_return_we = 0;
assign pf_return_cachable = 0;
`endif
// Responses from the MMU still need to be merged/muxed back together
// with the responses from the local bus
assign cpu_ack = ((cpu_lcl_cyc)&&(sys_ack))
||((cpu_gbl_cyc)&&(mmu_cpu_ack));
assign cpu_stall = ((cpu_lcl_cyc)&&(sys_stall))
||((cpu_gbl_cyc)&&(mmu_cpu_stall));
assign cpu_idata = (cpu_gbl_cyc)?mmu_cpu_idata : sys_idata;
 
// The following lines (will be/) are used to allow the prefetch to
// snoop on any external interaction. Until this capability is
// integrated into the CPU, they are unused. Here we tell Verilator
// not to be surprised that these lines are unused:
 
// verilator lint_off UNUSED
wire [(3+1+20+20-1):0] mmu_unused;
assign mmu_unused = { pf_return_stb, pf_return_we,
pf_return_p, pf_return_v, pf_return_cachable,
cpu_miss };
// verilator lint_on UNUSED
 
// Now, arbitrate the bus ... first for the local peripherals
// For the debugger to have access to the local system bus, the
// following must be true:
// (dbg_cyc) The debugger must request the bus
// (!cpu_lcl_cyc) The CPU cannot be using it (CPU gets priority)
// (~cpu_lcl_cyc) The CPU cannot be using it (CPU gets priority)
// (dbg_addr) The debugger must be requesting its data
// register, not just the control register
// and one of two other things. Either
// ((cpu_halt)&&(!cpu_dbg_stall)) the CPU is completely halted,
// ((cpu_halt)&&(~cpu_dbg_stall)) the CPU is completely halted,
// or
// (!cmd_addr[5]) we are trying to read a CPU register
// (~cmd_addr[5]) we are trying to read a CPU register
// while in motion. Let the user beware that,
// by not waiting for the CPU to fully halt,
// his results may not be what he expects.
//
wire sys_dbg_cyc = ((dbg_cyc)&&(!cpu_lcl_cyc)&&(dbg_addr))
&&(cmd_addr[5]);
wire sys_dbg_cyc = ((dbg_cyc)&&(~cpu_lcl_cyc)&&(dbg_addr))
&&(((cpu_halt)&&(~cpu_dbg_stall))
||(~cmd_addr[5]));
assign sys_cyc = (cpu_lcl_cyc)||(sys_dbg_cyc);
assign sys_stb = (cpu_lcl_cyc)
? (cpu_lcl_stb)
866,23 → 732,16
: ((dbg_stb)&&(dbg_addr)&&(cmd_addr[5]));
 
assign sys_we = (cpu_lcl_cyc) ? cpu_we : dbg_we;
assign sys_addr= (cpu_lcl_cyc) ? cpu_addr[7:0] : { 3'h0, cmd_addr[4:0]};
assign sys_addr= (cpu_lcl_cyc) ? cpu_addr[4:0] : cmd_addr[4:0];
assign sys_data= (cpu_lcl_cyc) ? cpu_data : dbg_idata;
 
// Return debug response values
// A return from one of three busses:
// CMD giving command instructions to the CPU (step, halt, etc)
// CPU-DBG-DATA internal register responses from within the CPU
// sys Responses from the front-side bus here in the ZipSystem
assign dbg_odata = (!dbg_addr) ? cmd_data
:((!cmd_addr[5])?cpu_dbg_data : sys_idata);
assign dbg_odata = (~dbg_addr)?cmd_data
:((~cmd_addr[5])?cpu_dbg_data : wb_data);
initial dbg_ack = 1'b0;
always @(posedge i_clk)
dbg_ack <= (dbg_stb)&&(!dbg_stall);
assign dbg_stall=(dbg_cyc)&&(
((!sys_dbg_cyc)&&(cpu_dbg_stall))
||(sys_stall)
)&&(dbg_addr);
dbg_ack <= (dbg_cyc)&&(dbg_stb)&&(~dbg_stall);
assign dbg_stall=(dbg_cyc)&&((~sys_dbg_cyc)||(sys_stall))&&(dbg_addr);
 
// Now for the external wishbone bus
// Need to arbitrate between the flash cache and the CPU
890,32 → 749,22
// cache gets access to the bus--the CPU will be stuck until the
// flash cache is finished with the bus.
wire ext_cyc, ext_stb, ext_we, ext_err;
wire [(PAW-1):0] ext_addr;
wire cpu_ext_ack, cpu_ext_stall, ext_ack, ext_stall,
cpu_ext_err;
wire [(AW-1):0] ext_addr;
wire [31:0] ext_odata;
wire [3:0] ext_sel;
wbpriarbiter #(32,PAW) dmacvcpu(i_clk,
mmu_cyc, mmu_stb, mmu_we, mmu_addr, mmu_data, mmu_sel,
mmu_ack, mmu_stall, mmu_err,
wbpriarbiter #(32,AW) dmacvcpu(i_clk,
cpu_gbl_cyc, cpu_gbl_stb, cpu_we, cpu_addr, cpu_data, cpu_sel,
cpu_ext_ack, cpu_ext_stall, cpu_ext_err,
dc_cyc, dc_stb, dc_we, dc_addr, dc_data, 4'hf,
dc_ack, dc_stall, dc_err,
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, ext_sel,
ext_ack, ext_stall, ext_err);
assign mmu_idata = ext_idata;
/*
assign ext_cyc = mmu_cyc;
assign ext_stb = mmu_stb;
assign ext_we = mmu_we;
assign ext_odata= mmu_data;
assign ext_addr = mmu_addr;
assign ext_sel = mmu_sel;
assign mmu_ack = ext_ack;
assign mmu_stall= ext_stall;
assign mmu_err = ext_err;
*/
 
`ifdef DELAY_EXT_BUS
busdelay #(.AW(PAW),.DW(32),.DELAY_STALL(0)) extbus(i_clk, i_reset,
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, ext_sel,
busdelay #(AW,32) extbus(i_clk,
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata,
ext_ack, ext_stall, ext_idata, ext_err,
o_wb_cyc, o_wb_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)||(wdbus_int));
939,48 → 788,19
:(tmb_ack ? tmb_data
:(tmc_ack ? tmc_data
:jif_data));
always @(posedge i_clk)
casez({ mmus_ack, tmr_ack, wdt_ack, actr_ack,
dmac_ack, pic_ack, ctri_ack, wdbus_ack })
8'b1???????: sys_idata <= mmus_data;
8'b01??????: sys_idata <= tmr_data;
8'b001?????: sys_idata <= wdt_data;
8'b0001????: sys_idata <= actr_data;
8'b00001???: sys_idata <= dmac_data;
8'b000001??: sys_idata <= pic_data;
8'b0000001?: sys_idata <= ctri_data;
8'b00000001: sys_idata <= wdbus_data;
default: sys_idata <= mmus_data;
endcase
assign wb_data = (tmr_ack|wdt_ack)?((tmr_ack)?tmr_data:wdt_data)
:((actr_ack|dmac_ack)?((actr_ack)?actr_data:dmac_data)
:((pic_ack|ctri_ack)?((pic_ack)?pic_data:ctri_data)
:((wdbus_ack)?wdbus_data:(ext_idata))));
 
always @(posedge i_clk)
if ((i_reset)||(!sys_cyc))
sys_ack <= 1'b0;
else
sys_ack <= (|{ mmu_ack, tmr_ack, wdt_ack, actr_ack,
dmac_ack, pic_ack, ctri_ack, wdbus_ack,
mmus_ack });
assign sys_stall = (tma_stall | tmb_stall | tmc_stall | jif_stall
| wdt_stall | ctri_stall | actr_stall
| pic_stall | dmac_stall | mmus_stall); // Always 1'b0!
| pic_stall | dmac_stall); // Always 1'b0!
assign cpu_stall = (sys_stall)|(cpu_ext_stall);
assign sys_ack = (tmr_ack|wdt_ack|ctri_ack|actr_ack|pic_ack|dmac_ack|wdbus_ack);
assign cpu_ack = (sys_ack)||(cpu_ext_ack);
assign cpu_err = (cpu_ext_err)&&(cpu_gbl_cyc);
 
assign o_ext_int = (cmd_halt) && (!cpu_stall);
assign o_ext_int = (cmd_halt) && (~cpu_stall);
 
// Make verilator happy
// verilator lint_off UNUSED
wire [5:0] unused;
assign unused = { no_dbg_err, dbg_sel, sel_mmus };
`ifndef INCLUDE_ACCOUNTING_COUNTERS
wire [11:0] unused_ctrs;
assign unused_ctrs = {
moc_int, mpc_int, mic_int, mtc_int,
uoc_int, upc_int, uic_int, utc_int,
cpu_gie, cpu_op_stall, cpu_pf_stall, cpu_i_count };
`endif
`ifndef INCLUDE_DMA_CONTROLLER
wire [34:0] unused_dmac;
assign unused_dmac = { dc_err, dc_ack, dc_stall, dmac_int_vec };
`endif
// verilator lint_on UNUSED
endmodule

powered by: WebSVN 2.1.0

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