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

Subversion Repositories zipcpu

[/] [zipcpu/] [trunk/] [rtl/] [core/] [pfcache.v] - Rev 209

Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
// Filename:	pfcache.v
//
// 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).
//
//	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
//
// 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
// <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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype	none
//
module	pfcache(i_clk, i_reset, i_new_pc, i_clear_cache,
			// i_early_branch, i_from_addr,
			i_stall_n, i_pc, o_insn, o_pc, o_valid,
		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
	localparam	CW=LGCACHELEN;	// Short hand for LGCACHELEN
	localparam	LS=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	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	[(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;
	//
	// 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
	// to zero just to set it to something.
	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			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):CW]	tagvalipc, tagvallst;
	wire	[(AW-1):CW]	tagval;
	wire	[(AW-1):LS]	lasttag;
	reg			illegal_valid;
	reg	[(AW-1):LS]	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;
	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.
		//
		// 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 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.
		r_pc <= i_pc;
		r_lastpc <= lastpc;
	end
 
	// 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
	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.
	initial	tagvallst = 0;
	always @(posedge i_clk)
		tagvallst <= cache_tags[lastpc[(CW+1):LS+2]];
 
	// 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
	// isn't valid, but the decoder is stalled, get the next address
	// anyway.
	initial	lastpc = 0;
	always @(posedge i_clk)
	if (w_advance)
		lastpc <= i_pc;
 
	assign	lasttag = lastpc[(AW+1):LS+2];
 
	/////////////////////////////////////////////////
	//
	// 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]]));
 
	initial	delay = 2'h3;
	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)
			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
 
	wire	w_invalidate_result;
	assign	w_invalidate_result = (i_reset)||(i_clear_cache);
 
	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;
	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);
 
		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.
	//
	/////////////////////////////////////////////////
	//
	//
	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));
 
	//
	// 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.
	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_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;
	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)
			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;
 
	end else if ((needload)&&(!i_new_pc))
	begin
		o_wb_cyc  <= 1'b1;
		o_wb_stb  <= 1'b1;
	end
 
	// 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];
 
 
	// 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}};
	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}} };
 
	// 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.
	always @(posedge i_clk)
	if (o_wb_cyc)
		cache[wraddr] <= 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;
	initial	svmask = 1'b0;
	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)
		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);
		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)];
		end
 
		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)
		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])));
		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;
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(i_reset)))
	begin
		if (isrc)
			assert(lastpc == r_pc);
		else
			assert(lastpc + 4== r_pc);
	end
 
	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
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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