UberDDR3/delete_later/rtl/cpu/dcache.v

2726 lines
67 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: dcache.v
// {{{
// Project: 10Gb Ethernet switch
//
// Purpose: To provide a simple data cache for the ZipCPU. The cache is
// designed to be a drop in replacement for the pipememm memory
// unit currently existing within the ZipCPU. The goal of this unit is
// to achieve single cycle read access to any memory in the last cache line
// used, or two cycle access to any memory currently in the cache.
//
// The cache separates between four types of accesses, one write and three
// read access types. The read accesses are split between those that are
// not cacheable, those that are in the cache, and those that are not.
//
// 1. Write accesses always create writes to the bus. For these reasons,
// these may always be considered cache misses.
//
// Writes to memory locations within the cache must also update
// cache memory immediately, to keep the cache in synch.
//
// It is our goal to be able to maintain single cycle write
// accesses for memory bursts.
//
// 2. Read access to non-cacheable memory locations will also immediately
// go to the bus, just as all write accesses go to the bus.
//
// 3. Read accesses to cacheable memory locations will immediately read
// from the appropriate cache line. However, since thee valid
// line will take a second clock to read, it may take up to two
// clocks to know if the memory was in cache. For this reason,
// we bypass the test for the last validly accessed cache line.
//
// We shall design these read accesses so that reads to the cache
// may take place concurrently with other writes to the bus.
//
// Errors in cache reads will void the entire cache line. For this reason,
// cache lines must always be of a smaller in size than any associated
// 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) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
// }}}
// http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
//
//
`ifdef FORMAL
`define ASSERT assert
`ifdef DCACHE
`define ASSUME assume
`else
`define ASSUME assert
`endif
`endif
// }}}
module dcache #(
// {{{
parameter LGCACHELEN = 8,
BUS_WIDTH=32,
ADDRESS_WIDTH=32-$clog2(BUS_WIDTH/8),
LGNLINES=(LGCACHELEN-3), // Log of the number of separate cache lines
NAUX=5, // # of aux d-wires to keep aligned w/memops
parameter DATA_WIDTH=32, // CPU's register width
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 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
`ifdef FORMAL
parameter F_LGDEPTH=1 + (((!OPT_PIPE)||(LS > OPT_FIFO_DEPTH))
? LS : OPT_FIFO_DEPTH),
`endif
parameter [0:0] OPT_LOWPOWER = 1'b0,
// localparam DW = 32, // Bus data width
localparam DP = OPT_FIFO_DEPTH,
localparam WBLSB = $clog2(BUS_WIDTH/8),
// localparam DLSB = $clog2(DATA_WIDTH/8),
//
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, i_clear,
// Interface from the CPU
// {{{
input wire i_pipe_stb, i_lock,
input wire [2:0] i_op,
input wire [DATA_WIDTH-1:0] i_addr,
input wire [DATA_WIDTH-1:0] i_data,
input wire [(NAUX-1):0] i_oreg, // Aux data, such as reg to write to
// Outputs, going back to the CPU
output reg o_busy, o_rdbusy,
output reg o_pipe_stalled,
output reg o_valid, o_err,
output reg [(NAUX-1):0] o_wreg,
output reg [DATA_WIDTH-1: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 [BUS_WIDTH-1:0] o_wb_data,
output wire [BUS_WIDTH/8-1:0] o_wb_sel,
// Wishbone bus slave response inputs
input wire i_wb_stall, i_wb_ack, i_wb_err,
input wire [BUS_WIDTH-1:0] i_wb_data
// }}}
// }}}
);
// Declarations
// {{{
localparam FIF_WIDTH = (NAUX-1)+2+WBLSB;
integer ik;
`ifdef FORMAL
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding;
wire f_pc, f_gie, f_read_cycle;
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 [BUS_WIDTH-1:0] c_mem [0:((1<<CS)-1)];
reg set_vflag;
reg [1:0] state;
reg [(CS-1):0] wr_addr;
reg [BUS_WIDTH-1:0] cached_iword, cached_rword;
reg lock_gbl, lock_lcl;
// 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 [BUS_WIDTH-1:0] c_wdata;
reg [BUS_WIDTH/8-1:0] c_wsel;
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;
`ifdef FORMAL
reg [F_LGDEPTH-1:0] f_fill;
reg [AW:0] f_return_address;
reg [AW:0] f_pending_addr;
reg f_pc_pending;
wire [4:0] f_last_reg, f_addr_reg;
// Verilator lint_off UNDRIVEN
(* anyseq *) reg [4:0] f_areg;
// Verilator lint_on UNDRIVEN
`endif
wire cache_miss_inow, w_cachable;
wire raw_cachable_address;
reg r_cachable, r_svalid, r_dvalid, r_rd, r_cache_miss,
r_rd_pending;
reg [AW-1:0] r_addr;
wire [(LGNLINES-1):0] r_cline;
wire [(CS-1):0] r_caddr;
wire [(AW-LS-1):0] r_ctag;
reg wr_cstb, r_iv, in_cache;
reg [(AW-LS-1):0] r_itag;
reg [FIF_WIDTH:0] req_data;
reg gie;
reg [BUS_WIDTH-1:0] pre_data, pre_shifted;
// }}}
// Convenience assignments
// {{{
assign i_cline = i_addr[WBLSB +LS +: (CS-LS)];
assign i_caddr = i_addr[WBLSB +: CS];
assign cache_miss_inow = (!last_tag_valid)
||(last_tag != i_addr[WBLSB+LS +: (AW-LS)])
||(!c_v[i_cline]);
assign w_cachable = ((!OPT_LOCAL_BUS)
||(i_addr[DATA_WIDTH-1:DATA_WIDTH-8]!=8'hff))
&&((!i_lock)||(!OPT_LOCK))&&(raw_cachable_address);
assign r_cline = r_addr[(CS-1):LS];
assign r_caddr = r_addr[(CS-1):0];
assign r_ctag = r_addr[(AW-1):LS];
// }}}
// Cachability checking
// {{{
iscachable chkaddress(i_addr[0 +: AW+WBLSB], raw_cachable_address);
// }}}
// r_* values
// {{{
// The one-clock delayed read values from the cache.
//
initial r_rd = 1'b0;
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)
begin
// The single clock path
// 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);
//
// The two clock in-cache path
//
// Some preliminaries that needed to be calculated on the first
// clock
if ((!o_pipe_stalled)&&(!r_rd_pending))
r_addr <= i_addr[WBLSB +: AW];
if ((!o_pipe_stalled)&&(!r_rd_pending))
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));
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_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;
last_tag <= r_ctag;
end else if ((state == DC_READC)
&&(last_tag[CS-LS-1:0]==r_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.
// A cache miss is only true _if_
// 1. A read was requested
// 2. It is for a cachable address, AND
// 3. It isn't in the cache on the first read
// or the second read
// 4. The read hasn't yet started to get this address
r_cache_miss <= ((!cyc)||(o_wb_we))&&(r_cachable)
// One clock path -- miss
&&(!r_svalid)
// Two clock path -- misses as well
&&(r_rd)&&(!r_svalid)
&&((r_itag != r_ctag)||(!r_iv));
if (i_clear)
last_tag_valid <= 0;
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
end
always @(posedge i_clk)
r_itag <= c_vtags[(!o_pipe_stalled && !r_rd_pending) ? i_cline : r_cline];
// }}}
// o_wb_sel, r_sel
// {{{
generate if (DATA_WIDTH == BUS_WIDTH)
begin : COPY_SEL
// {{{
reg [BUS_WIDTH/8-1:0] r_sel;
initial r_sel = 4'hf;
always @(posedge i_clk)
if (i_reset)
r_sel <= 4'hf;
else if (!o_pipe_stalled && (!OPT_LOWPOWER || i_pipe_stb))
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
end else if (OPT_LOWPOWER && !i_wb_stall)
r_sel <= 4'h0;
assign o_wb_sel = (state == DC_READC) ? 4'hf : r_sel;
// }}}
end else begin : GEN_SEL
// {{{
reg [DATA_WIDTH/8-1:0] pre_sel;
reg [BUS_WIDTH/8-1:0] full_sel, r_wb_sel;
always @(*)
casez(i_op[2:1])
2'b0?: pre_sel = {(DATA_WIDTH/8){1'b1}};
2'b10: pre_sel = { 2'b11, {(DATA_WIDTH/8-2){1'b0}} };
2'b11: pre_sel = { 1'b1, {(DATA_WIDTH/8-1){1'b0}} };
endcase
always @(*)
if (OPT_LOCAL_BUS && (&i_addr[31:24]))
full_sel = { {(BUS_WIDTH/8-4){1'b0}}, pre_sel }
>> (i_addr[1:0]);
else
full_sel = { pre_sel, {(BUS_WIDTH/8-4){1'b0}} }
>> (i_addr[WBLSB-1:0]);
initial r_wb_sel = -1;
always @(posedge i_clk)
if (i_reset)
r_wb_sel <= -1;
else if (i_pipe_stb && (i_op[0] || !w_cachable))
r_wb_sel <= full_sel;
else if (!i_wb_stall)
r_wb_sel <= -1;
assign o_wb_sel = r_wb_sel;
// }}}
end endgenerate
// }}}
// o_wb_data
// {{{
generate if (DATA_WIDTH == BUS_WIDTH)
begin : GEN_SAME_BUSWIDTH
// {{{
initial o_wb_data = 0;
always @(posedge i_clk)
if (i_reset)
o_wb_data <= 0;
else if ((!o_busy || !i_wb_stall) && (!OPT_LOWPOWER || i_pipe_stb))
begin
if (DATA_WIDTH == 32)
begin
if (OPT_LOWPOWER)
begin : ZERO_UNUSED_DATA_BITS
casez({ i_op[2:1], i_addr[1:0] })
4'b0???: o_wb_data <= i_data;
4'b100?: o_wb_data <= { i_data[15:0], 16'h0 };
4'b101?: o_wb_data <= { 16'h0, i_data[15:0] };
4'b1100: o_wb_data <= { i_data[7:0], 24'h0 };
4'b1101: o_wb_data <= { 8'h0, i_data[7:0], 16'h0 };
4'b1110: o_wb_data <= { 16'h0, i_data[7:0], 8'h0 };
4'b1111: o_wb_data <= { 24'h0, i_data[7:0] };
endcase
end else begin : DUPLICATE_UNUSED_DATA_BITS
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
end else begin
// Verilator coverage_off
casez(i_op[2:1])
2'b0?: o_wb_data <= i_data << (8*i_addr[$clog2(DATA_WIDTH)-1:0]);
2'b10: o_wb_data <= { 16'h0, i_data[15:0] } << (8*i_addr[$clog2(DATA_WIDTH)-1:0]);
2'b11: o_wb_data <= { 24'h0, i_data[7:0] } << (8*i_addr[$clog2(DATA_WIDTH)-1:0]);
endcase
// Verilator coverage_on
end
end else if (OPT_LOWPOWER && !i_wb_stall)
o_wb_data <= 0;
// }}}
end else begin : GEN_WIDE_BUS
// {{{
reg [DATA_WIDTH-1:0] pre_shift;
reg [BUS_WIDTH-1:0] wide_preshift, shifted_data;
always @(*)
begin
casez(i_op[2:1])
2'b0?: pre_shift = i_data;
2'b10: pre_shift = { i_data[15:0],
{(DATA_WIDTH-16){1'b0}} };
2'b11: pre_shift = { i_data[ 7:0],
{(DATA_WIDTH- 8){1'b0}} };
endcase
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
begin
wide_preshift = { {(BUS_WIDTH-DATA_WIDTH){1'b0}}, pre_shift };
shifted_data = wide_preshift >> (8*i_addr[2-1:0]);
end else begin
wide_preshift = { pre_shift,
{(BUS_WIDTH-DATA_WIDTH){1'b0}} };
shifted_data = wide_preshift >> (8*i_addr[WBLSB-1:0]);
end
end
initial o_wb_data = 0;
always @(posedge i_clk)
if (OPT_LOWPOWER && i_reset)
o_wb_data <= 0;
else if ((!o_busy || !i_wb_stall)
&& (!OPT_LOWPOWER || (i_pipe_stb && i_op[0])))
begin
if (!OPT_LOWPOWER)
begin
casez(i_op[2:1])
2'b0?: o_wb_data <= {(BUS_WIDTH/DATA_WIDTH){i_data}};
2'b10: o_wb_data <= {(BUS_WIDTH/16){i_data[15:0]}};
2'b11: o_wb_data <= {(BUS_WIDTH/ 8){i_data[ 7:0]}};
endcase
end else begin
o_wb_data <= shifted_data;
end
end else if (OPT_LOWPOWER && !i_wb_stall)
o_wb_data <= 0;
// }}}
end endgenerate
// }}}
// Register return FIFO
// {{{
generate if (OPT_PIPE)
begin : OPT_PIPE_FIFO
// {{{
reg [FIF_WIDTH-1: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[WBLSB-1:0] };
always @(posedge i_clk)
if (i_pipe_stb)
gie <= i_oreg[NAUX-1];
`ifdef NO_BKRAM
reg [FIF_WIDTH-1: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[WBLSB-1:0] };
always @(*)
begin
req_data[NAUX+4-1] = gie;
// if ((r_svalid)||(state == DC_READ))
if (single_write)
req_data[FIF_WIDTH-1:0] = r_last_data;
else
req_data[FIF_WIDTH-1:0] = r_req_data;
end
always @(*)
`ASSERT(req_data == fifo_data[rdaddr[DP-1:0]]);
`else
always @(*)
req_data[FIF_WIDTH-1:0] = fifo_data[rdaddr[DP-1:0]];
always @(*)
req_data[FIF_WIDTH] = 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 [FIF_WIDTH:0] f_last_data;
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)
begin
`ASSERT(f_fill == 1);
end else if (r_dvalid)
begin
`ASSERT(f_fill == 1);
end else
`ASSERT(f_fill == 0);
end else if (r_rd_pending)
begin
`ASSERT(f_fill == 1);
end 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)
begin
`ASSUME(!i_pipe_stb);
end
always @(posedge i_clk)
if (state == DC_WRITE)
begin
`ASSERT(!f_pc_pending);
end
always @(*)
begin
f_last_wraddr = 0;
f_last_wraddr[DP:0] = wraddr - 1'b1;
end
assign f_last_data = fifo_data[f_last_wraddr];
always @(posedge i_clk)
if (r_rd_pending)
begin
`ASSERT(f_pc_pending == (f_last_data[1+WBLSB+2 +: 3] == 3'h7));
`ASSERT({ gie, f_last_data[2+WBLSB +: 4] } == f_last_reg);
end
`define INSPECT_FIFO
reg [((1<<(DP+1))-1):0] f_valid_fifo_entry;
genvar gk;
for(gk=0; gk<(1<<(DP+1)); gk=gk+1)
begin
always @(*)
begin
f_valid_fifo_entry[gk] = 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)&&(gk < wraddr)
&&(gk >= rdaddr))
f_valid_fifo_entry[gk] = 1'b1;
else if ((rdaddr > wraddr)&&(gk >= rdaddr))
f_valid_fifo_entry[gk] = 1'b1;
else if ((rdaddr > wraddr)&&(gk < wraddr))
f_valid_fifo_entry[gk] = 1'b1;
end
`ifdef INSPECT_FIFO
wire [FIF_WIDTH-1:0] fifo_data_k;
assign fifo_data_k = fifo_data[gk[DP-1:0]];
always @(*)
if (f_valid_fifo_entry[gk])
begin
if (!f_pc_pending)
begin
`ASSERT((o_wb_we)||(fifo_data_k[1+2+WBLSB +: 3] != 3'h7));
end else if (gk != f_last_wraddr)
`ASSERT(fifo_data_k[1+2+WBLSB +: 3] != 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(req_data[1+2+WBLSB +: 3] != 3'h7);
`endif // INSPECT_FIFO
//
//
//
always @(*)
begin
f_pending_addr[AW-1:0] = f_fifo_addr[rdaddr];
f_pending_addr[AW] = r_wb_cyc_lcl;
end
//
//
//
always @(posedge i_clk)
if (i_pipe_stb)
begin
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
f_fifo_addr[wraddr[DP-1:0]] <= { 1'b1, i_addr[2 +: AW] };
else
f_fifo_addr[wraddr[DP-1:0]] <= { 1'b0, i_addr[WBLSB +: AW] };
end
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
reg [DP:0] f_twin_next;
// Verilator lint_off UNDRIVEN
(* anyconst *) reg [DP:0] f_twin_base;
(* anyconst *) reg [AW+FIF_WIDTH-1:0] f_twin_first,
f_twin_second;
// Verilator lint_on UNDRIVEN
reg f_twin_none, f_twin_single, f_twin_double, f_twin_last;
reg f_twin_valid_one, f_twin_valid_two;
always @(*)
f_twin_next = f_twin_base+1;
always @(*)
begin
f_twin_valid_one = ((f_valid_fifo_entry[f_twin_base])
&&(f_twin_first == { f_fifo_addr[f_twin_base[DP-1:0]],
fifo_data[f_twin_base[DP-1:0]] }));
f_twin_valid_two = ((f_valid_fifo_entry[f_twin_next])
&&(f_twin_second == { f_fifo_addr[f_twin_next[DP-1:0]],
fifo_data[f_twin_next[DP-1:0]] }));
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)))
begin
`ASSERT(f_twin_none);
end else if ($past(f_twin_none))
begin
`ASSERT(f_twin_none || f_twin_single || f_twin_last);
end else if ($past(f_twin_single))
begin
`ASSERT(f_twin_none || f_twin_single || f_twin_double || f_twin_last);
end else if ($past(f_twin_double))
begin
`ASSERT(f_twin_double || f_twin_last);
end else if ($past(f_twin_last))
`ASSERT(f_twin_none || f_twin_single || f_twin_last);
// f_addr_reg test
// {{{
always @(*)
if (o_rdbusy)
begin
if (f_twin_valid_one && f_twin_base != f_last_wraddr)
`ASSERT({ gie, f_twin_first[2+WBLSB +: 4] } != f_addr_reg);
if (f_twin_valid_two && f_twin_next != f_last_wraddr)
`ASSERT({ gie, f_twin_second[2+WBLSB +: 4] } != f_addr_reg);
if ((rdaddr != f_last_wraddr)&&(rdaddr != f_twin_base)
&&(rdaddr != f_twin_next))
assume({ gie, req_data[2+WBLSB +: 4] } != f_addr_reg);
end
// }}}
`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)
begin
`ASSERT(f_fill == 1);
end else if (f_fill > 0)
begin
`ASSERT(cyc);
end
always @(posedge i_clk)
if (state != 0)
begin
`ASSERT(f_fill > 0);
end else if (!r_svalid && !r_dvalid && !r_rd_pending)
`ASSERT(f_fill == 0);
`endif // FORMAL
always @(posedge i_clk)
o_wreg <= req_data[2+WBLSB +: NAUX];
// }}}
end else begin : NO_FIFO
// {{{
reg [AW-1:0] fr_last_addr;
always @(posedge i_clk)
if (i_pipe_stb)
req_data <= { i_oreg, i_op[2:1], i_addr[WBLSB-1:0] };
always @(*)
o_wreg = req_data[2+WBLSB +: NAUX];
always @(*)
gie = o_wreg[NAUX-1];
`ifdef FORMAL
// f_pc_pending
// {{{
always @(*)
begin
f_pc_pending = 0;
if ((r_rd_pending || state == DC_READS)||(o_valid))
f_pc_pending = (o_wreg[3:1] == 3'h7);
end
// }}}
// f_pending_addr
// {{{
initial f_pending_addr = 0;
always @(posedge i_clk)
if (i_reset)
f_pending_addr <= 0;
else if (i_pipe_stb)
begin
if ((OPT_LOCAL_BUS)&&(&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
f_pending_addr <= { 1'b1, i_addr[2 +: AW] };
else
f_pending_addr <= { 1'b0, i_addr[WBLSB +: AW] };
end
// }}}
// f_return_address
// {{{
always @(posedge i_clk)
if (stb)
fr_last_addr <= o_wb_addr;
always @(*)
begin
f_return_address[AW] = o_wb_cyc_lcl;
f_return_address[AW-1:LS] = o_wb_addr[AW-1:LS];
if (OPT_LOWPOWER && !stb)
f_return_address[AW-1:LS] = fr_last_addr[AW-1:LS];
end
always @(*)
if (state == DC_READS)
begin
f_return_address[LS-1:0] = o_wb_addr[LS-1:0];
if (OPT_LOWPOWER && !stb)
f_return_address[LS-1:0] = fr_last_addr[LS-1:0];
end else begin
f_return_address[LS-1:0]
= (o_wb_addr[LS-1:0] - f_outstanding[LS-1:0]);
if (OPT_LOWPOWER && !stb)
f_return_address[LS-1:0] = (fr_last_addr[LS-1:0] - f_outstanding[LS-1:0]);
end
// }}}
// f_last_reg
// {{{
always @(*)
if (o_rdbusy)
assert(o_wreg == f_last_reg);
// }}}
// verilator lint_off UNUSED
initial f_fill = 0;
wire unused_no_fifo_formal;
assign unused_no_fifo_formal = &{ 1'b0, f_return_address,
f_addr_reg, f_fill };
`endif
wire unused_no_fifo;
assign unused_no_fifo = &{ 1'b0, gie };
// verilator lint_on UNUSED
// }}}
end endgenerate
// }}}
// BIG STATE machine: CYC, STB, c_v, state, etc
// {{{
initial o_wb_addr = 0;
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 <= {(BUS_WIDTH/8){1'b1}};
r_wb_cyc_gbl <= 1'b0;
r_wb_cyc_lcl <= 1'b0;
o_wb_stb_gbl <= 0;
o_wb_stb_lcl <= 0;
o_wb_addr <= 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;
set_vflag <= 1'b0;
if (!cyc && set_vflag)
c_v[c_waddr[(CS-1):LS]] <= 1'b1;
wr_cstb <= 1'b0;
// end_of_line
// {{{
// Verilator coverage_off
if (LS <= 0)
end_of_line <= 1'b1;
// Verilator coverage_on
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
// }}}
// last_line_stb
// {{{
if (!cyc || !stb || (OPT_LOWPOWER && state != DC_READC))
last_line_stb <= (LS <= 0);
// Verilator coverage_off
else if (!i_wb_stall && (LS <= 1))
last_line_stb <= 1'b1;
// Verilator coverage_on
else if (!i_wb_stall)
last_line_stb <= (o_wb_addr[(LS-1):1]=={(LS-1){1'b1}});
else
last_line_stb <= (o_wb_addr[(LS-1):0]=={(LS){1'b1}});
// }}}
//
//
case(state)
DC_IDLE: begin
// {{{
o_wb_we <= 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;
in_cache <= (i_op[0])&&(w_cachable);
if ((i_pipe_stb)&&(i_op[0]))
begin // Write operation
// {{{
state <= DC_WRITE;
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
o_wb_addr <= i_addr[2 +: AW];
else
o_wb_addr <= i_addr[WBLSB +: AW];
o_wb_we <= 1'b1;
cyc <= 1'b1;
stb <= 1'b1;
if (OPT_LOCAL_BUS)
begin
r_wb_cyc_gbl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]!=8'hff);
r_wb_cyc_lcl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]==8'hff);
o_wb_stb_gbl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]!=8'hff);
o_wb_stb_lcl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]==8'hff);
end else begin
r_wb_cyc_gbl <= 1'b1;
o_wb_stb_gbl <= 1'b1;
end
// }}}
end else if (r_cache_miss)
begin // Cache miss
state <= DC_READC;
o_wb_addr <= { r_ctag, {(LS){1'b0}} };
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;
end else if ((i_pipe_stb)&&(!w_cachable))
begin // Read non-cachable memory area
state <= DC_READS;
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
o_wb_addr <= i_addr[2 +: AW];
else
o_wb_addr <= i_addr[WBLSB +: AW];
cyc <= 1'b1;
stb <= 1'b1;
if (OPT_LOCAL_BUS)
begin
r_wb_cyc_gbl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]!=8'hff);
r_wb_cyc_lcl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]==8'hff);
o_wb_stb_gbl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]!=8'hff);
o_wb_stb_lcl <= (i_addr[DATA_WIDTH-1:DATA_WIDTH-8]==8'hff);
end else begin
r_wb_cyc_gbl <= 1'b1;
o_wb_stb_gbl <= 1'b1;
end
end // else we stay idle
end
// }}}
DC_READC: begin
// {{{
// We enter here once we have committed to reading
// data into a cache line.
if (stb && !i_wb_stall)
begin
stb <= (!last_line_stb);
o_wb_stb_gbl <= (!last_line_stb);
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1;
if (OPT_LOWPOWER && last_line_stb)
o_wb_addr <= 0;
end
if (i_wb_ack)
c_v[r_cline] <= 1'b0;
c_wr <= (i_wb_ack);
c_wdata <= i_wb_data;
c_waddr <= c_waddr+(i_wb_ack ? 1:0);
c_wsel <= {(BUS_WIDTH/8){1'b1}};
set_vflag <= !i_wb_err;
// if (i_wb_ack)
// c_vtags[r_addr[(CS-1):LS]]
// <= r_addr[(AW-1):LS];
if ((i_wb_ack && end_of_line)|| i_wb_err)
begin
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;
//
if (OPT_LOWPOWER)
o_wb_addr <= 0;
end end
// }}}
DC_READS: begin
// {{{
// We enter here once we have committed to reading
// data that cannot go into a cache line
if ((!i_wb_stall)&&(!i_pipe_stb))
begin
stb <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
if (OPT_LOWPOWER)
o_wb_addr <= 0;
end
if ((!i_wb_stall)&&(i_pipe_stb))
begin
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
o_wb_addr <= i_addr[2 +: AW];
else
o_wb_addr <= i_addr[WBLSB +: AW];
end
c_wr <= 1'b0;
if (((i_wb_ack)&&(last_ack))||(i_wb_err))
begin
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;
if (OPT_LOWPOWER)
o_wb_addr <= 0;
end end
// }}}
DC_WRITE: begin
// {{{
c_wr <= o_wb_stb_gbl && (c_v[o_wb_addr[CS-1:LS]])
// &&(c_vtags[o_wb_addr[CS-1:LS]]==o_wb_addr[AW-1:LS]);
&&(r_itag==o_wb_addr[AW-1:LS]);
c_wdata <= o_wb_data;
c_waddr <= r_addr[CS-1:0];
c_wsel <= o_wb_sel;
if ((!i_wb_stall)&&(!i_pipe_stb))
begin
stb <= 1'b0;
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
if (OPT_LOWPOWER)
o_wb_addr <= 0;
end
wr_cstb <= (stb)&&(!i_wb_stall)&&(in_cache);
if (i_pipe_stb && !i_wb_stall)
begin
if (OPT_LOCAL_BUS && (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
o_wb_addr <= i_addr[2 +: AW];
else
o_wb_addr <= i_addr[WBLSB +: AW];
end
if (((i_wb_ack)&&(last_ack)
&&((!OPT_PIPE)||(!i_pipe_stb)))
||(i_wb_err))
begin
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;
if (OPT_LOWPOWER)
o_wb_addr <= 0;
end end
// }}}
endcase
if (i_clear)
c_v <= 0;
end
always @(posedge i_clk)
if (state == DC_READC && i_wb_ack)
c_vtags[r_addr[(CS-1):LS]] <= r_addr[(AW-1):LS];
// }}}
// wr_addr
// {{{
always @(posedge i_clk)
if (!cyc)
begin
wr_addr <= r_addr[(CS-1):0];
if ((!i_pipe_stb || !i_op[0])&&(r_cache_miss))
wr_addr[LS-1:0] <= 0;
end else if (i_wb_ack)
wr_addr <= wr_addr + 1'b1;
else
wr_addr <= wr_addr;
// }}}
// npending
// {{{
// npending is the number of outstanding (non-cached) read or write
// requests. We only keep track of npending if we are running in a
// piped fashion, i.e. if OPT_PIPE, and so need to keep track of
// possibly multiple outstanding transactions
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
`ifdef FORMAL
always @(*)
`ASSERT(npending <= { 1'b1, {(DP){1'b0}} });
`endif
// }}}
// last_ack
// {{{
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
// }}}
//
// Writes to the cache
// {{{
// These have been made as simple as possible. Note that the c_wr
// 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
for(ik=0; ik<BUS_WIDTH/8; ik=ik+1)
if (c_wsel[ik])
c_mem[c_waddr][ik *8 +: 8] <= c_wdata[ik * 8 +: 8];
end
// }}}
//
// Reads from the cache
// {{{
// Some architectures require that all reads be registered. We
// accomplish that here. Whether or not the result of this read is
// going to be our output will need to be determined with combinatorial
// logic on the output.
//
generate if (OPT_DUAL_READ_PORT)
begin : GEN_DUAL_READ_PORT
always @(posedge i_clk)
cached_iword <= c_mem[i_caddr];
always @(posedge i_clk)
cached_rword <= c_mem[r_caddr];
end else begin : GEN_SHARED_READ_PORT
always @(posedge i_clk)
cached_rword <= c_mem[(o_busy) ? r_caddr : i_caddr];
always @(*)
cached_iword = cached_rword;
end endgenerate
// }}}
// o_data, pre_data
// {{{
// 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 @(*)
if (r_svalid)
pre_data = cached_iword;
else if (state == DC_READS)
pre_data = i_wb_data;
else
pre_data = cached_rword;
always @(*)
if (o_wb_cyc_lcl)
pre_shifted = { i_wb_data[31:0], {(BUS_WIDTH-32){1'b0}} } << (8*req_data[2-1:0]);
else
pre_shifted = pre_data << (8*req_data[WBLSB-1:0]);
// o_data
initial o_data = 0;
always @(posedge i_clk)
if (OPT_LOWPOWER && (i_reset
||(!r_svalid && (!i_wb_ack || state != DC_READS) && !r_dvalid)))
o_data <= 0;
else casez(req_data[WBLSB +: 2])
2'b10: o_data <= { 16'h0, pre_shifted[BUS_WIDTH-1:BUS_WIDTH-16] };
2'b11: o_data <= { 24'h0, pre_shifted[BUS_WIDTH-1:BUS_WIDTH- 8] };
default o_data <= pre_shifted[BUS_WIDTH-1:BUS_WIDTH-32];
endcase
// }}}
// o_valid
// {{{
initial o_valid = 1'b0;
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);
// }}}
// o_err
// {{{
initial o_err = 1'b0;
always @(posedge i_clk)
if (i_reset)
o_err <= 1'b0;
else
o_err <= (cyc)&&(i_wb_err);
// }}}
// o_busy
// {{{
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;
// }}}
// o_rdbusy
// {{{
initial o_rdbusy = 0;
always @(posedge i_clk)
if ((i_reset)||((cyc)&&(i_wb_err)))
o_rdbusy <= 1'b0;
else if (i_pipe_stb && !i_op[0])
o_rdbusy <= 1'b1;
else if ((state == DC_READS)&&(i_wb_ack))
o_rdbusy <= 1'b0;
else if ((r_rd_pending)&&(!r_dvalid))
o_rdbusy <= 1'b1;
else if (cyc && !o_wb_we)
o_rdbusy <= 1'b1;
else // if ((r_dvalid)||(r_svalid))
o_rdbusy <= 1'b0;
// }}}
//
// 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 || !OPT_LOCK)
begin
lock_gbl <= 1'b0;
lock_lcl<= 1'b0;
end else begin
// lock_gbl <= (r_wb_cyc_gbl)||(lock_gbl);
// lock_lcl <= (r_wb_cyc_lcl)||(lock_lcl);
if (i_pipe_stb)
begin
lock_gbl <= (!OPT_LOCAL_BUS
|| i_addr[DATA_WIDTH-1:DATA_WIDTH-8] != 8'hff);
lock_lcl <=(i_addr[DATA_WIDTH-1:DATA_WIDTH-8] == 8'hff);
end
if (r_wb_cyc_gbl && i_wb_err)
lock_gbl <= 1'b0;
if (r_wb_cyc_lcl && i_wb_err)
lock_lcl <= 1'b0;
if (!i_lock)
{ lock_gbl, lock_lcl } <= 2'b00;
if (!OPT_LOCAL_BUS)
lock_lcl <= 1'b0;
end
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
// Make Verilator happy
// {{{
// Verilator coverage_off
// Verilator lint_off UNUSED
wire unused;
assign unused = &{ 1'b0, pre_shifted };
generate if (AW+WBLSB < DATA_WIDTH)
begin : UNUSED_BITS
wire unused_aw;
assign unused = &{ 1'b0, i_addr[DATA_WIDTH-1:AW+WBLSB] };
end endgenerate
// Verilator lint_on UNUSED
// Verilator coverage_on
// }}}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties for verification
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
////////////////////////////////////////////////////////////////////////
//
// Reset properties
// {{{
////////////////////////////////////////////////////////////////////////
//
//
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
// }}}
////////////////////////////////////////////////////////////////////////
//
// Assumptions about our inputs (the CPU interface)
// {{{
////////////////////////////////////////////////////////////////////////
//
//
reg f_rdbusy, f_done;
reg [F_LGDEPTH-1:0] f_cpu_outstanding;
wire faxi_write;
fmem #(
// {{{
.OPT_LOCK(OPT_LOCK),
.F_LGDEPTH(F_LGDEPTH),
.OPT_MAXDEPTH(1<<(F_LGDEPTH-1)),
.OPT_AXI_LOCK(1'b0)
// }}}
) f_cpu(
// {{{
.i_clk(i_clk),
.i_sys_reset(i_reset),
.i_cpu_reset(i_reset),
// The CPU interface
.i_stb(i_pipe_stb),
.i_pipe_stalled(o_pipe_stalled),
.i_clear_cache(i_clear),
.i_lock(i_lock),
.i_op(i_op),
.i_addr(i_addr),
.i_data(i_data),
.i_oreg(i_oreg),
.i_areg(f_areg),
.i_busy(o_busy),
.i_rdbusy(o_rdbusy),
.i_valid(o_valid),
.i_done(f_done),
.i_err(o_err),
.i_wreg(o_wreg),
.i_result(o_data),
.f_outstanding(f_cpu_outstanding),
.f_pc(f_pc),
.f_gie(f_gie),
.f_read_cycle(f_read_cycle),
.f_axi_write_cycle(faxi_write),
.f_last_reg(f_last_reg),
.f_addr_reg(f_addr_reg)
// }}}
);
always @(*)
assert(faxi_write == 0);
always @(*)
if (OPT_PIPE || (!o_err && !r_svalid && !r_dvalid))
assert(f_pc_pending == f_pc);
// f_rdbusy
// {{{
always @(*)
begin
f_rdbusy = 0;
if (state == DC_READC)
f_rdbusy = 1'b1;
if (state == DC_READS)
f_rdbusy = 1'b1;
if (r_svalid || r_dvalid)
f_rdbusy = 1'b1;
if ((r_rd_pending)||(r_dvalid)||(r_svalid))
f_rdbusy = 1'b1;
assert(f_rdbusy == o_rdbusy);
end
// }}}
// f_done
// {{{
initial f_done = 0;
always @(posedge i_clk)
if (i_reset)
f_done <= 1'b0;
else begin
f_done <= 1'b0;
if ((cyc)&&(i_wb_err))
f_done <= 1'b1;
if ((state == DC_READS || state == DC_WRITE)&&(i_wb_ack))
f_done <= 1'b1;
if ((r_dvalid)||(r_svalid))
f_done <= 1'b1;
end
// }}}
// f_gie
// {{{
always @(*)
if (o_busy)
assert(gie == f_gie);
// }}}
// f_read_cycle
// {{{
always @(*)
if (state == DC_READS || state == DC_READC || r_svalid || r_dvalid)
begin
assert(f_read_cycle);
end else if (state == DC_WRITE)
assert(!f_read_cycle);
// }}}
// The CPU is not allowed to write to the CC register while a
// memory operation is pending, lest any resulting bus error
// get returned to the wrong mode--i.e. user bus error halting
// the supervisor. What this means, though, is that the CPU
// will *never* attempt to clear the data cache while the cache
// is busy.
// always @(*)
// if (o_busy || i_pipe_stb) // f_outstanding)
// `ASSUME(!i_clear);
always @(*)
if (cyc && !o_wb_we)
begin
if (state == DC_READC)
begin
assert(f_cpu_outstanding == 1);
end else
assert(f_cpu_outstanding == f_outstanding
+ (r_svalid ? 1:0) + (r_dvalid ? 1:0)
+ (o_valid ? 1:0) + (stb ? 1:0));
end else if (cyc) // Writing
begin
assume(f_cpu_outstanding <= (1<<OPT_FIFO_DEPTH));
if (!o_err)
assert(f_cpu_outstanding == f_outstanding + (stb ? 1:0)
+ (f_done ? 1:0));
end
always @(*)
if (!cyc && (!OPT_PIPE || !o_err))
assert(f_cpu_outstanding ==
((r_svalid || r_dvalid || r_rd_pending) ? 1:0)
+ ((f_done || o_valid || o_err) ? 1:0));
/*
always @(*)
if (o_pipe_stalled)
`ASSUME(!i_pipe_stb);
always @(*)
if (!f_past_valid)
`ASSUME(!i_pipe_stb);
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));
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)
begin
assert(state == DC_WRITE);
end else
assert(state == DC_READS);
end
if (cyc)
assert(o_wb_we == (state == DC_WRITE));
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
fwb_master #(
// {{{
.AW(AW), .DW(BUS_WIDTH),
.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),
// Verilator lint_off WIDTH
.F_MAX_REQUESTS((OPT_PIPE) ? 0 : (1<<LS)),
// Verilator lint_on WIDTH
`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
// }}}
);
// }}}
////////////////////////////////////////////////////////////////////////
//
// Contract checking -- Arbitrary address (contract) properties
// {{{
////////////////////////////////////////////////////////////////////////
`ifdef DCACHE
// Arbitrary access is specific to local dcache implementation
//
//
(* 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 [BUS_WIDTH-1:0] f_const_data;
wire [BUS_WIDTH-1:0] f_cmem_here;
reg f_pending_rd;
wire f_cval_in_cache;
wire [AW-1:0] wb_start;
reg [AW-1:0] f_cache_waddr;
wire f_this_cache_waddr;
wire f_this_return;
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 = (OPT_LOWPOWER ? r_addr[AW-1:LS]
: 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 f_this_cache_waddr = (!f_const_addr[AW])
&&(f_cache_waddr == f_const_addr[AW-1:0]);
assign f_this_return = (f_return_address == f_const_addr);
assign wb_start = (f_stb) ? (o_wb_addr - f_nreqs)
: { r_addr[AW-1:LS], {(LS){1'b0}} };
// Assume f_const_addr[AW] consistent with the local bus declaration
// {{{
generate if ((AW > BUS_WIDTH - 8)&&(OPT_LOCAL_BUS))
begin : UPPER_CONST_ADDR_BITS
always @(*)
if (f_const_addr[AW])
begin
assume(&f_const_addr[AW-1:DATA_WIDTH-8-2]);
end else
assume(!(&f_const_addr[AW-1:DATA_WIDTH-8-WBLSB]));
end endgenerate
// }}}
// f_const_data -- Adjust our special data word upon request
// {{{
reg [BUS_WIDTH-1:0] f_shifted_data;
reg [BUS_WIDTH/8-1:0] f_shifted_sel;
always @(*)
begin
casez(i_op[2:1])
2'b0?: begin
f_shifted_data = { i_data, {(BUS_WIDTH-DATA_WIDTH){1'b0}} } >> (8*i_addr[WBLSB-1:0]);
f_shifted_sel = { 4'b1111, {(BUS_WIDTH/8-4){1'b0}} } >> i_addr[WBLSB-1:0];
end
2'b10: begin
f_shifted_data = { i_data[15:0], {(BUS_WIDTH-16){1'b0}} } >> (8*i_addr[WBLSB-1:0]);
f_shifted_sel = { 2'b11, {(BUS_WIDTH/8-2){1'b0}} } >> i_addr[WBLSB-1:0];
end
2'b11: begin
f_shifted_data = { i_data[ 7:0], {(BUS_WIDTH-8){1'b0}} } >> (8*i_addr[WBLSB-1:0]);
f_shifted_sel = { 1'b1, {(BUS_WIDTH/8-1){1'b0}} } >> i_addr[WBLSB-1:0];
end
endcase
end
always @(posedge i_clk)
// Upon a request for our special address ...
if (i_pipe_stb && (!cyc || !i_wb_err)
// That matches the local or global bus address....
&& f_const_addr[AW] == ((OPT_LOCAL_BUS)
&&(&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
// and it is a write request
&& i_op[0])
begin
// Then update the chosen data word at that address
if (f_const_addr[AW]
&& (&i_addr[DATA_WIDTH-1:DATA_WIDTH-8])
&& (i_addr[2 +: AW] == f_const_addr[AW-1:0]))
begin // Local bus word
// {{{
casez({ i_op[2:1], i_addr[1:0] })
4'b0???: f_const_data[31: 0] <= 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 else if (!f_const_addr[AW]
&& (!OPT_LOCAL_BUS
|| !(&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]))
&& (i_addr[WBLSB +: AW] == f_const_addr[AW-1:0]))
begin // Global bus word
for(ik=0; ik<BUS_WIDTH/8; ik=ik+1)
if (f_shifted_sel[ik])
f_const_data[8*ik +: 8] <= f_shifted_data[8*ik +: 8];
end
end
// }}}
// Insure the cache and bus both have an accurate/valid copy of the data
// {{{
always @(posedge i_clk)
if ((f_past_valid)&&(!i_reset)&&(!f_const_buserr))
begin
// Is this a write to our special address?
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
// Only the updated data value should be written to the
// bus
// {{{
if (f_const_addr[AW])
begin
for(ik=0; ik<DATA_WIDTH/8; ik=ik+1)
if (f_stb && o_wb_sel[ik])
`ASSERT(o_wb_data[ik*8 +: 8]==f_const_data[ik*8 +: 8]);
end else begin
for(ik=0; ik<BUS_WIDTH/8; ik=ik+1)
if (f_stb && o_wb_sel[ik])
`ASSERT(o_wb_data[ik*8 +: 8]==f_const_data[ik*8 +: 8]);
end
// }}}
// Check the data written into the cache for validity
// {{{
if (!f_const_addr[AW] && c_v[f_const_tag_addr]
&& (f_ctag_here == o_wb_addr[AW-1:LS]))
begin
for(ik=0; ik<BUS_WIDTH/8; ik=ik+1)
if (!o_wb_sel[ik] && !c_wsel[ik])
`ASSERT(f_cmem_here[8*ik +: 8]==f_const_data[8*ik +: 8]);
// }}}
end
// Otherwise, if this is a valid address, *and* it is in the
// cache ...
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
// {{{
for(ik=0; ik<BUS_WIDTH/8; ik=ik+1)
if (c_wsel[ik] && $past(o_wb_cyc_gbl))
begin
`ASSERT(c_wdata[8*ik +: 8]
== f_const_data[8*ik +: 8]);
end else
`ASSERT(f_cmem_here[8*ik +: 8]
== f_const_data[8*ik +: 8]);
// }}}
end else
// Else, we assert the correct value is already
// in the cache
`ASSERT(f_cmem_here == f_const_data);
// }}}
end
end
// }}}
// When reading a cache line, assert the correct value has been read
// if we've passed that aprt of our read
// {{{
always @(posedge i_clk)
if ((f_past_valid)&&(state == DC_READC))
begin
`ASSERT(f_return_address[AW-1:LS] == r_ctag);
`ASSERT(f_wb_tag == r_ctag);
if ((r_ctag == 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]))
begin
`ASSERT(c_wdata == f_const_data);
end else
`ASSERT(f_cmem_here == f_const_data);
end
if (!i_reset && f_nacks > 0)
`ASSERT(!c_v[r_cline]);
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
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
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))
// `ASSUME(i_op[0] == o_wb_we);
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 (f_stb)
assert(r_addr[AW-1:LS] == o_wb_addr[AW-1:LS]);
if (($past(i_wb_ack))&&(!$past(f_stb)))
begin
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] });
end else if (f_nacks > 0)
begin
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] });
`ASSERT(c_waddr[CS-1:LS] == r_addr[CS-1:LS]);
end else begin
`ASSERT(c_waddr[CS-1:LS] == r_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
reg [BUS_WIDTH-1:0] f_shift_const_data;
always @(posedge i_clk)
begin
if (f_const_addr[AW])
f_shift_const_data = { f_const_data[31:0], {(BUS_WIDTH-DATA_WIDTH){1'b0}} } << (8*req_data[2-1:0]);
else
f_shift_const_data = f_const_data << (8*req_data[WBLSB-1:0]);
end
always @(posedge i_clk)
if ((f_past_valid)&&(o_valid)&&($past(f_pending_addr) == f_const_addr))
begin
if (f_const_buserr)
begin
`ASSERT(o_err);
end else if (f_pending_rd)
begin
casez($past(req_data[WBLSB +: 2]))
4'b0?: `ASSERT(o_data ==f_shift_const_data[BUS_WIDTH-1:BUS_WIDTH-32]);
4'b10: `ASSERT(o_data =={16'h00,f_shift_const_data[BUS_WIDTH-1:BUS_WIDTH-16]});
4'b11: `ASSERT(o_data =={24'h00,f_shift_const_data[BUS_WIDTH-1:BUS_WIDTH- 8]});
endcase
end
end
// #1. Assume this return matches our chosen data
// {{{
always @(*)
if ((f_cyc)&&(
((state == DC_READC)
&&(f_return_address[AW-1:LS] == f_const_addr[AW-1:LS]))
||(f_this_return)))
begin
if (f_const_buserr)
begin
assume(!i_wb_ack);
end 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);
// Chosen address is invalid: bus error properties
// {{{
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
// }}}
/*
// ?? why was I assuming this again?
always @(*)
if (f_cval_in_cache)
begin
assume((!i_wb_err)
||(!i_pipe_stb)
||(f_const_addr[AW-1:0] != i_addr[WBLSB +: AW]));
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;
wire f_wb_cachable;
// 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
// }}}
iscachable #(
// {{{
.ADDRESS_WIDTH(AW+WBLSB)
// }}}
) f_chkwb_addr({ r_addr, {(WBLSB){1'b0}} }, 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)
begin
`ASSERT(o_busy);
end else if (r_svalid)
begin
`ASSERT(o_busy);
end else if (o_valid)
begin
`ASSERT(!o_busy);
end else if (o_err)
begin
`ASSERT(!o_busy);
end
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)
begin
`ASSERT(!o_valid);
end 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))
begin
`ASSERT(f_rdpending <= 2);
end else
`ASSERT(f_rdpending == 1);
end else if ((OPT_PIPE)&&(o_valid)&&($past(r_dvalid|r_svalid)))
begin
`ASSERT(f_rdpending <= 2);
end 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(!lock_gbl);
`ASSERT(!lock_lcl);
`ASSERT(r_rd_pending);
`ASSERT(r_cachable);
if (($past(cyc))&&(!$past(o_wb_stb_gbl)))
begin
`ASSERT(!o_wb_stb_gbl);
end
if ((OPT_PIPE)&&(o_valid))
begin
`ASSERT(f_rdpending == 2);
end else
`ASSERT(f_rdpending == 1);
end
always @(*)
if (state == DC_READS)
begin
`ASSERT(!o_wb_we);
if (OPT_PIPE)
begin
if (o_valid)
begin
`ASSERT(({ 1'b0, f_rdpending } == npending + 1)
||({ 1'b0, f_rdpending } == npending));
end else
`ASSERT({ 1'b0, 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 == { 1'b0, f_outstanding});
2'b1?: `ASSERT(npending == { 1'b0, f_outstanding} + 1);
2'b01: `ASSERT(npending == { 1'b0, f_outstanding} + 1);
endcase
if (state == DC_WRITE)
`ASSERT(!o_valid);
end else
`ASSERT(f_outstanding <= 1);
end
always @(*)
if (OPT_PIPE)
begin
`ASSERT(f_rdpending <= 2);
end else
`ASSERT(f_rdpending <= 1);
always @(posedge i_clk)
if ((!OPT_PIPE)&&(o_valid))
begin
`ASSERT(f_rdpending == 1);
end 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)))
begin
`ASSUME(!i_lock);
end 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)
begin
`ASSERT(o_wb_we);
end 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
if (OPT_LOWPOWER)
begin
`ASSERT(o_wb_addr == 0);
end else begin
`ASSERT(o_wb_addr[LS-1:0] == 0);
`ASSERT(o_wb_addr[AW-1:CS] == r_addr[AW-1:CS]);
end
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
+ { {(CS-F_LGDEPTH){1'b0}}, f_nacks };
always @(posedge i_clk)
if (state == DC_READC)
begin
if (LS == 0)
begin
`ASSERT(end_of_line);
end else if (f_nacks < (1<<LS)-1)
begin
`ASSERT(!end_of_line);
end else if (f_nacks == (1<<LS)-1)
begin
`ASSERT(end_of_line);
end
`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);
always @(*)
if (c_wr && !(&c_wsel))
`ASSERT($countones(c_wsel) == 1
|| $countones(c_wsel) == 2
|| $countones(c_wsel) == 4);
always @(*)
if (!OPT_PIPE)
begin
`ASSERT(o_pipe_stalled == o_busy);
end 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)))
begin
`ASSERT(cyc);
end else if ($past(f_outstanding > 1))
begin
`ASSERT(cyc);
end else if (($past(f_outstanding == 1))
&&((!$past(i_wb_ack))
||(($past(f_stb))
&&(!$past(i_wb_stall)))))
begin
`ASSERT(cyc);
end 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)))
begin
`ASSERT((npending == f_outstanding+1)
||(npending == f_outstanding+2));
end else
`ASSERT(npending == { 1'b0, 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[r_cline]);
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+WBLSB))
fccheck_last_tag({last_tag, {(LS+WBLSB){1'b0}} },
f_cachable_last_tag);
iscachable #(
.ADDRESS_WIDTH(AW+WBLSB)
) fccheck_r_cachable({ r_addr, {(WBLSB){1'b0}} }, 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 && (stb || !OPT_LOWPOWER))
`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
// }}}
////////////////////////////////////////////////////////////////////////
//
// Low power checks
// {{{
////////////////////////////////////////////////////////////////////////
//
//
generate if (OPT_LOWPOWER)
begin : CHECK_LOWPOWER
always @(posedge i_clk)
if (!o_wb_stb_gbl && !o_wb_stb_lcl)
begin
assert($stable(o_wb_addr) || (o_wb_addr == 0));
assert($stable(o_wb_data) || (o_wb_data == 0));
assert($stable(o_wb_sel) || (o_wb_sel == 0)
|| (&o_wb_sel));
end
always @(posedge i_clk)
if (!o_valid && !o_err)
begin
assert($stable(o_data) || (o_data == 0));
end
end endgenerate
// }}}
////////////////////////////////////////////////////////////////////////
//
// 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);
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))
begin
assume(!(&i_addr[(DATA_WIDTH-1):(DATA_WIDTH-8)]));
end else if ((i_lock)&&(o_wb_cyc_lcl)&&(i_pipe_stb))
assume(&i_addr[(DATA_WIDTH-1):(DATA_WIDTH-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[DATA_WIDTH-1:DATA_WIDTH-8])));
end
// If the bus is active, but we allow a second item in anyway 'cause
// we are piped, then assume that we don't cross from local to global
// buses
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)
begin
assume(&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]);
end else
assume(!(&i_addr[DATA_WIDTH-1:DATA_WIDTH-8]));
end
// Assume aligned accesses
always @(*)
if (i_pipe_stb)
casez(i_op[2:1])
2'b0?: assume(i_addr[1:0] == 2'b00);
2'b10: assume(i_addr[ 0] == 1'b0);
2'b11: begin end
endcase
// always @(posedge i_clk)
// if ((f_past_valid)&&(!$past(cyc))&&(!cyc))
// assume((!i_wb_err)&&(!i_wb_ack));
// }}}
`endif
// }}}
endmodule