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

Subversion Repositories zipcpu

[/] [zipcpu/] [trunk/] [rtl/] [peripherals/] [zipmmu.v] - Rev 209

Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
// Filename: 	zipmmu.v
//
// Project:	Zip CPU backend for the GNU Compiler Collection
//
// Purpose:	To provide a "bump-in-the-line" wishbone memory management
//		unit, that is configured from one wishbone bus and modifies a
//	separate wishbone bus.  Both busses will not be active at the same time.
//
//	The idea is that the CPU can use one portion of its peripheral
//	system memory space to configure the MMU, and another portion of its
//	memory space to access the MMU.  Even more, configuring the MMU is to
//	be done when the CPU is in supervisor mode.  This means that all
//	high-memory, system-peripheral accesses will be enabled *only* when
//	the CPU is in supervisor mode.
//
//	There is a very specific reason for this design choice: by designing
//	the MMU in this fashion, the MMU may then be inluded (or not) at the
//	discretion of the individual assembling the ZipSystem (or equivalent)
//	module.
//
// Design Goals:
//
//	Since we're trying to design this for disadvantaged, limited CPUs,
//	we should be able to offer such CPUs only as much MMU as they want.
//	Therefore, it should be possible to scale the MMU up and/or down in
//	LUT space.
//
// Memory space:
//	1. On access via the memory bus, the MMU should provide for a speed
//		going through it such that any access is delayed by only one
//		clock cycle.  Further, multiple accesses to the same page
//		should not take any longer than the one cycle delay.  Accesses
//		to other pages should take a minimum number of clocks.
//		Accesses from one page to the next, such as from one page to
//		the next subsequent one, should cost no delays.
//
//	2. One independent control word to set the current context
//
//	  - When context = 0, virtual page = physical page, page table is an
//		unused pass through.
//	  - When context != 0, MMU translation is active anytime the GIE is
//		set.  Pages must match context, as well as virtual address.
//
//	  - Contains 4 RdOnly bits indicating the log address size for the
//		machine, offset by 17.  Thus, the build will have an address
//		bus of width (lgpage+17), or a memory space of (2^(lgpage+17)).
//		Under this formula, the number of valid address bits can range
//		from 17 to 32.
//	  -  Contains 4 RdOnly bits indicating log_2 TLB table size.
//	  	Size is given by (2^(lgsize)).  I'm considering sizes of 6,7&8
//	  -  Contains 4 RdOnly bits indicating the log page size, offset by
//		eight.  Page sizes are therefore given by (2^(lgpage+8)), and
//		the smallest page size is 256 words.
//	  - Contains 4 RdOnly bits indicating the log context size, offset by 1.
//		The number of bits in the context word therefore run from 1 to
//		(lgcontext+1)-1, supporting between (2^1)-1=3 and
//		(2^16)-1 = 65535 contexts.  (The zero context is not being
//		counted here, as it is special.)
//
//	+------+------+------+------+--------------------+
//	|      |      |      |      |                    |
//	|  4b  |  4b  |  4b  |  4b  |       16-bit       |
//	| LGADR| LGTBL|LGPGSZ|LGCTXT|    Context word    |
//	|      |      |      |      |                    |
//	+------+------+------+------+--------------------+
//
//	Supervisor *cannot* have page table entries, since there are no
//	interrupts (page faults) allowed in supervisor context.
//
//	To be valid,
//	  Context Size (1..16), NFlags (    4) < Page Size (8-23 bits)
//	  Page size (8-23 bits)                > NFlags bits (4)
//
//	Small page sizes, then, mean fewer contexts are possible
//
//	3. One status word, which contains the address that failed and some
//		flags:
//
//	Top Virtual address bits indicate which page ... caused  a problem.
//		These will be the top N bits of the word, where N is the size
//		of the virtual address bits.  (Bits are cleared upon any write.)
//
//	Flags: (Up to 12 bits, all zeros means no fault.  Bits are cleared upon
//		write)
//		- 4: Multiple page table matches
//		- 2: Attempt to write a read-only page
//		- 1: Page not found
//
//	3. Two words per active page table entry, accessed through two bus
//		addresses.  This word contains:
//
//	  16-bits	Page context
//	  20-bits	Virtual address
//	  20-bits	Physical address
//				A physical address of all ones means that the
//				page does not exist, and any attempt to access
//				the virtual address associated with this page
//				should fault.
//
//	Flags:
//	   1-bit	Read-only / ~written (user set/read/written)
//				If set, this page will cause a fault on any
//				attempt to write this memory.
//	   1-bit	This page may be executed
//	   1-bit	Cacheable
//				This is not a hardware page, but a memory page.
//				Therefore, the values within this page may be
//				cached.
//	   1-bit	Accessed
//				This an be used to implement a least-recently
//				used measure.  The hardware will set this value
//				when the page is accessed.  The user can also
//				set or clear this at will.
//
//		(Loaded flag	Not necessary, just map the physical page to 0)
//
//	We could equivalently do a 16-bit V&P addresses, for a 28-bit total
//	address space, if we didn't want to support the entire 32-bit space.
//
//
//	4. Can read/write this word in two parts:
//
//		(20-bit Virtual )(8-bits lower context)(4-bit flags), and
//		(20-bit Physical)(8-bits upper context)(4-bit flags)
//
//		Actual bit lengths will vary as the MMU configuration changes,
//		however the flags will always be the low order four bits,
//		and the virtual/physical address flags will always consume
//		32 bits minus the page table size.  The context bits will
//		always be split into upper and lower context bits.  If there
//		are more context bits than can fit in the space, then the
//		upper bits of the context field will be filled with zeros.
//
//		On any write, the context bits will be set from the context
//		bits in the control register.
//
//	+----+----+-----+----+----+----+----+--+--+--+--+
//	|                         | Lower 8b| R| E| C| A|
//	|  20-bit Virtual page ID | Context | O| X| C| C|
//	|(top 20 bits of the addr)|   ID    | n| E| H| C|
//	|                         |         | W| F| E| S|
//	+----+----+-----+----+----+----+----+--+--+--+--+
//
//	+----+----+-----+----+----+----+----+--+--+--+--+
//	|                         | Upper 8b| R| A| C| T|
//	|  20-bit Physical pg ID  | Context | O| C| C| H|
//	|(top 20 bits of the      |   ID    | n| C| H| S|
//	|    physical address)    |         | W| S| E| P|
//	+----+----+-----+----+----+----+----+--+--+--+--+
//
//	5. PF Cache--handles words in both physical and virtual
//	- On any pf-read, the MMU returns the current pagetable/TBL mapping
//		This consists of [Context,Va,Pa].
//	- The PF cache stores this with the address tag.  (If the PF is reading,
//		the VP should match, only the physical page ID needs to be
//		sored ...)
//	- At the end of any cache line read, the page table/TBL mapping address
//		will have long been available, the "Valid" bit will be turned
//		on and associated with the physical mapping.
//	- On any data-write (pf doesn't write), MMU sends [Context,Va,Pa]
//		TLB mapping to the pf-cache.
//	- If the write matches any physical PF-cache addresses (???), the
//		pfcache declares that address line invalid, and just plain
//		clears the valid bit for that page.
//
//	Since the cache lines sizes are smaller than the page table sizes,
//	failure to match the address means ... what?
//
//
//	6. Normal operation and timing:
//	  - One clock lost if still on the same page as last time, or in the
//		supervisor (physical pages only) context ...
//	  - Two clocks (1-more delay) if opening a new page.
//		(1-clock to look up the entry--comparing against all entries,
//		1-clock to read it, next clock the access goes forward.)
//	  - No more than two stalls for any access, pipelineable.  Thus, once
//		you've stalled by both clocks, you'll not stall again during
//		any pipeline operation.
//
//
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-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
//
`define	ROFLAG	3	// Read-only flag
`define	EXEFLG	2	// No-execute flag (invalid for I-cache)
`define	CHFLAG	1	// Cachable flag
`define	AXFLAG	0	// Accessed flag
//
module zipmmu(i_clk, i_reset, i_wbs_cyc_stb, i_wbs_we, i_wbs_addr,
				i_wbs_data, o_wbs_ack, o_wbs_stall, o_wbs_data,
		i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_exe,
			i_wbm_addr, i_wbm_data, i_wbm_sel, i_gie,
		o_cyc, o_stb, o_we, o_addr, o_data, o_sel,
			i_stall, i_ack, i_err, i_data,
			o_rtn_stall, o_rtn_ack, o_rtn_err,
				o_rtn_miss, o_rtn_data,
		pf_return_stb, pf_return_we,
				pf_return_p, pf_return_v,
				pf_return_cachable);
	parameter	// The size of the address bus.  Actual addressable
			// size will likely be 2^(ADDRESS_WIDTH+2) octets
			ADDRESS_WIDTH=28,
			// Number of page table entries
`ifdef	FORMAL
			LGTBL=4'h2,
`else
			LGTBL=4'h6,
`endif
			// The requested log page size in 8-bit bytes
			PLGPGSZB=20,
			// Number of bits describing context
`ifdef	FORMAL
			PLGCTXT=2;
`else
			PLGCTXT=16;
`endif
	parameter [0:0] OPT_DELAY_RETURN = 1'b0;
	localparam	// And for our derived parameters (don't set these ...)
			// Width of the data bus is 32-bits.  This may be hard
			// to change.
			DW = 32,
			// AW is just shorthand for the name ADDRESS_WIDTH
			AW = ADDRESS_WIDTH,
			// Page sizes must allow for a minimum of one context
			// bit per page, plus four flag bits, hence the minimum
			// number of bits for an address within a page is 5
			LGPGSZB=(PLGPGSZB < 5)? 5:PLGPGSZB,	// in bytes
			LGPGSZW=LGPGSZB-2,			// in words
			// The context value for a given page can be split
			// across both virtual and physical words.  It cannot
			// have so many bits to it that it takes more bits
			// then are available.
			LGCTXT=((2*LGPGSZB-4)>PLGCTXT)?
				PLGCTXT:(2*LGPGSZB-4),
			// LGLCTX is the number of context bits in the low word
			LGLCTX=(LGCTXT > (LGPGSZB-4))?(LGPGSZB-4):LGCTXT,
			// LGHCTX is the number of context bits in the high word
			LGHCTX= (LGCTXT-LGLCTX>0)?(LGCTXT-LGLCTX):0,
			VAW=(DW-LGPGSZB), //  Virtual address width, in bytes
			PAW=(AW-LGPGSZW), // Physical address width, in words
			TBL_BITS = LGTBL,	// Bits necessary to addr tbl
			TBL_SIZE=(1<<TBL_BITS);// Number of table entries
	input	wire		i_clk, i_reset;
	//
	input	wire		i_wbs_cyc_stb;
	input	wire		i_wbs_we;
	input	wire	[(LGTBL+1):0]	i_wbs_addr;
	input	wire	[(DW-1):0]	i_wbs_data;
	output	reg			o_wbs_ack;
	output	wire			o_wbs_stall;
	output	reg	[(DW-1):0]	o_wbs_data;
	//
	input	wire		i_wbm_cyc, i_wbm_stb;
	//
	input	wire			i_wbm_we, i_wbm_exe;
	input	wire [(DW-2-1):0]	i_wbm_addr;
	input	wire [(DW-1):0]		i_wbm_data;
	input	wire [(DW/8-1):0]	i_wbm_sel;
	input	wire			i_gie;
	//
	// Here's where we drive the slave side of the bus
	output	reg			o_cyc;
	output	wire			o_stb, o_we;
	output	reg	[(AW-1):0]	o_addr;
	output	reg	[(DW-1):0]	o_data;
	output	reg	[(DW/8-1):0]	o_sel;
	// and get our return information from driving the slave ...
	input	wire			i_stall, i_ack, i_err;
	input	wire	[(DW-1):0]	i_data;
	//
	// Here's where we return information on either our slave/control bus
	// or the memory bus we are controlled from.  Note that we share these
	// wires ...
	output	wire		o_rtn_stall;
	output	wire		o_rtn_ack;
	output	wire		o_rtn_err, o_rtn_miss;
	output	wire [(DW-1):0]	o_rtn_data;
	// Finally, to allow the prefetch to snoop on the MMU conversion ...
	output	wire			pf_return_stb, // snoop data is valid
					pf_return_we; // snoop data is chnging
	output	wire	[(PAW-1):0]	pf_return_p;
	output	wire	[(VAW-1):0]	pf_return_v;
	output	wire			pf_return_cachable;
	//
	//
 
//
//
//
	reg	[3:0]			tlb_flags	[0:(TBL_SIZE-1)];
	wire	[3:0]			s_tlb_flags;
	reg	[(LGCTXT-1):0]		tlb_cdata	[0:(TBL_SIZE-1)];
	reg	[(VAW-1):0]		tlb_vdata	[0:(TBL_SIZE-1)];
	reg	[(PAW-1):0]		tlb_pdata	[0:(TBL_SIZE-1)];
	reg	[(TBL_SIZE-1):0]	tlb_valid, tlb_accessed;
 
	wire	adr_control, adr_vtable, adr_ptable;
	wire	wr_control, wr_vtable, wr_ptable;
	wire	[(LGTBL-1):0]	wr_tlb_addr;
	assign	wr_tlb_addr= i_wbs_addr[(LGTBL):1]; // Leave bottom for V/P
	assign	adr_control= (i_wbs_cyc_stb)&&(~i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
	assign	adr_vtable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]);
	assign	adr_ptable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&( i_wbs_addr[0]);
	assign	wr_control = (adr_control)&&(i_wbs_we);
	assign	wr_vtable  = (adr_vtable )&&(i_wbs_we);
	assign	wr_ptable  = (adr_ptable )&&(i_wbs_we);
 
	reg			z_context;
	wire			kernel_context;
	reg	[(LGCTXT-1):0]	r_context_word;
	//
	wire	[31:0]		w_control_data, w_ptable_reg;
	reg	[31:0]		w_vtable_reg;
	reg	[31:0]	status_word;
	//
	//
	reg			r_pending, r_we, r_exe, r_valid,
				last_page_valid, last_ro, last_exe;
	reg	[(DW-3):0]	r_addr;
	reg	[(DW-1):0]	r_data;
	wire	[(VAW-1):0]	vpage;
	wire	[AW-LGPGSZW-1:0]	ppage;
	reg	[(DW/8-1):0]	r_sel;
	reg	[(PAW-1):0]	last_ppage;
	reg	[(VAW-1):0]	last_vpage;
	//
	wire	[(TBL_SIZE-1):0]	r_tlb_match;
	reg	[(LGTBL-1):0]		s_tlb_addr, last_tlb;
	reg				s_tlb_miss, s_tlb_hit, s_pending;
	//
	wire	ro_flag, exe_flag, simple_miss, ro_miss, exe_miss, table_err, cachable;
	reg	pf_stb, pf_cachable;
	reg	miss_pending;
	//
	reg	rtn_err;
 
 
	wire	this_page_valid, pending_page_valid;
	assign	this_page_valid = ((last_page_valid)
				&&(i_wbm_addr[(DW-3):(DW-2-VAW)]==last_vpage)
				&&((!last_ro)||(!i_wbm_we))
				&&((!last_exe)||(!i_wbm_exe)));
	assign	pending_page_valid = ((s_pending)&&(s_tlb_hit)
				&&((!r_we)||(!ro_flag))
				&&((!r_exe)||(exe_flag)));
 
	//////////////////////////////////////////
	//
	//
	// Step one -- handle the control bus--i_wbs_cyc_stb
	//
	//
	//////////////////////////////////////////
	always @(posedge i_clk)
	begin
		// Write to the Translation lookaside buffer
		if (wr_vtable)
			tlb_vdata[wr_tlb_addr]<=i_wbs_data[(DW-1):LGPGSZB];
		if (wr_ptable)
			tlb_pdata[wr_tlb_addr]<=i_wbs_data[(AW+1):LGPGSZB];
		// Set the context register for the page
		if (wr_vtable)
			tlb_flags[wr_tlb_addr] <= { i_wbs_data[3:1], 1'b0 };
		if (wr_vtable)
			tlb_cdata[wr_tlb_addr][(LGLCTX-1):0]
				<= i_wbs_data[(LGLCTX+4-1):4];
	end
 
	initial	tlb_accessed = 0;
	always @(posedge i_clk)
	if (i_reset)
		tlb_accessed <= 0;
	else begin
		if (wr_vtable)
			tlb_accessed[wr_tlb_addr] <= 1'b0;
		// Otherwise, keep track of the accessed bit if we
		// ever access this page
		else if ((!kernel_context)&&(pending_page_valid))
			tlb_accessed[s_tlb_addr] <= 1'b1;
		else if ((!kernel_context)&&(this_page_valid))
			tlb_accessed[last_tlb] <= 1'b1;
	end
 
	generate if (LGHCTX > 0)
	begin : HCTX
		always @(posedge i_clk)
		if (wr_ptable)
			tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX]
				<= i_wbs_data[(LGHCTX+4-1):4];
	end endgenerate
 
	// Writing to the control word
	initial z_context = 1'b1;
	initial r_context_word = 0;
	always @(posedge i_clk)
	if (wr_control)
		begin
		r_context_word <= i_wbs_data[(LGCTXT-1):0];
		z_context      <= (i_wbs_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
		end
	assign	kernel_context = (z_context)||(!i_gie);
	// Status words cannot be written to
 
	always @(posedge i_clk)
	if (i_reset)
		tlb_valid <= 0;
	else if (wr_ptable)
		tlb_valid[wr_tlb_addr]<=1'b1; //(i_wbs_data[(AW+1):LGPGSZB]!=0);
 
	/* v*rilator lint_off WIDTH */
	assign	w_control_data[31:28] = AW[3:0]-4'd1;
	assign	w_control_data[27:24] = LGTBL[3:0];
	assign	w_control_data[23:20] = LGPGSZB[3:0]-4'd10;
	assign	w_control_data[19:16] = LGCTXT[3:0]-1'b1;
	/* v*rilator lint_on WIDTH */
	assign	w_control_data[15: 0] = {{(16-LGCTXT){1'b0}}, r_context_word};
	//
	always @(*)
	begin
		w_vtable_reg = 0;
		w_vtable_reg[(DW-1):LGPGSZB] = tlb_vdata[wr_tlb_addr];
		w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] };
		w_vtable_reg[ 3:0]  = { tlb_flags[wr_tlb_addr][3:1],
					tlb_accessed[wr_tlb_addr] };
	end
	//
	assign	w_ptable_reg[(DW-1):LGPGSZB] = { {(DW-PAW-LGPGSZB){1'b0}},
					tlb_pdata[wr_tlb_addr] };
	assign	w_ptable_reg[ 3:0]  = 4'h0;
	//
	generate
		if (4+LGHCTX-1>4)
			assign	w_ptable_reg[(4+LGHCTX-1):4] =  {
				tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX] };
		if (LGPGSZB > LGLCTX+4)
			assign	w_vtable_reg[(LGPGSZB-1):(LGLCTX+4)] = 0;
		if (LGPGSZB > LGHCTX+4)
			assign	w_ptable_reg[(LGPGSZB-1):(LGHCTX+4)] = 0;
	endgenerate
 
	//
	// Now, reading from the bus
	/*
	wire	[(LGCTXT-1):0]	w_ctable_reg;
	assign	w_ctable_reg = tlb_cdata[wr_tlb_addr];
	reg			setup_this_page_flag;
	reg	[(LGCTXT-1):0]	setup_page;
	initial	setup_this_page_flag = 1'b0;
	always @(posedge i_clk)
		setup_page <= w_ctable_reg;
	always @(posedge i_clk)
		setup_this_page_flag <= (!i_reset)&&(i_wbs_cyc_stb)&&(i_wbs_addr[LGTBL+1]);
	*/
 
 
 
	//////////////////////////////////////////
	//
	//
	// Step two -- handle the page lookup on the master bus
	//
	//
	//////////////////////////////////////////
 
	//
	//
	// First clock, and the r_ register, copies the bus data from the bus.
	// While this increases the bus latency, it also gives us a moment to
	// work.
	//
	//
	wire	[(VAW-1):0]	r_vpage;
	wire	[(PAW-1):0]	r_ppage;
	assign	r_vpage = (r_addr[(DW-3):(DW-2-VAW)]);
	assign	r_ppage = (o_addr[(AW-1):LGPGSZW]);
 
	initial	s_pending = 1'b0;
	initial	r_pending = 1'b0;
	initial	r_valid   = 1'b0;
	always @(posedge i_clk)
	if (i_reset)
	begin
		r_pending <= 1'b0;
		r_valid   <= 1'b0;
		o_addr    <= 0;
		r_we      <= 0;
		r_exe     <= 0;
		r_addr    <= 0;
		r_data    <= 0;
		r_sel     <= 0;
		//
		s_pending <= 1'b0;
	end else
	begin
		if (!o_rtn_stall)
		begin
			r_pending <= (i_wbm_stb)&&(!kernel_context)
						&&(!this_page_valid);
			r_we      <= i_wbm_we;
			r_exe     <= i_wbm_exe;
			o_addr    <= { { (kernel_context)?
				i_wbm_addr[(AW-1):LGPGSZW] : last_ppage },
				i_wbm_addr[(LGPGSZW-1):0] };
			r_addr    <= i_wbm_addr;
			r_data    <= i_wbm_data;
			r_sel     <= i_wbm_sel;
			r_valid   <= (i_wbm_stb)&&((kernel_context)||(this_page_valid));
			s_pending <= 1'b0;
		end else if (!r_valid) begin
			r_valid <= (pending_page_valid);
			o_addr <= { ppage , r_addr[(LGPGSZW-1):0] };
			r_pending<= (r_pending)&&(!pending_page_valid);
			s_pending <=(r_pending)&&(!pending_page_valid);
		end else begin
			r_pending <= 1'b0;
			s_pending <= 1'b0;
		end
 
		if ((!i_wbm_cyc)||(o_rtn_err)||((o_cyc)&&(i_err)))
		begin
			s_pending <= 1'b0;
			r_pending <= 1'b0;
			r_valid   <= 1'b0;
		end
	end
 
`ifdef	FORMAL
	reg	f_past_valid;
 
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(r_pending))&&(r_pending)&&($past(o_rtn_stall))&&(i_wbm_cyc)&&(!o_stb))
		assert(s_pending);
`endif
 
	// Second clock: know which buffer entry this belong in.
	// If we don't already know, then the pipeline must be stalled for a
	// while ...
	genvar k, s;
	generate
	for(k=0; k<TBL_SIZE; k = k + 1)
		assign r_tlb_match[k] =
			// The page  must be valid
			(tlb_valid[k])
			// Virtual address must match
			&&(tlb_vdata[k] == r_vpage)
			// Context must match as well
			&&(tlb_cdata[k][LGCTXT-1:1] == r_context_word[LGCTXT-1:1])
			&&((!tlb_cdata[k][0])||(r_context_word[0]));
	endgenerate
 
	initial	s_tlb_miss = 1'b0;
	initial	s_tlb_hit  = 1'b0;
	generate
		integer i;
	always @(posedge i_clk)
	begin // valid when s_ becomes valid
		s_tlb_addr <= {(LGTBL){1'b0}};
		for(i=0; i<TBL_SIZE; i=i+1)
			if (r_tlb_match[i])
				s_tlb_addr <= i[(LGTBL-1):0];
		s_tlb_miss <= (r_pending)&&(r_tlb_match == 0);
		s_tlb_hit <= 1'b0;
		for(i=0; i<TBL_SIZE; i=i+1)
			if (r_tlb_match == (1<<i))
				s_tlb_hit <= (r_pending)&&(!r_valid)&&(i_wbm_cyc);
	end endgenerate
 
	// Third clock: Read from the address the virtual table offset,
	// whether read-only, etc.
	assign	s_tlb_flags = tlb_flags[s_tlb_addr];
	assign	ro_flag     = s_tlb_flags[`ROFLAG];
	assign	exe_flag    = s_tlb_flags[`EXEFLG];
	assign	cachable    = s_tlb_flags[`CHFLAG];
	assign	simple_miss = (s_pending)&&(s_tlb_miss);
	assign	ro_miss     = (s_pending)&&(s_tlb_hit)&&(r_we)&&(ro_flag);
	assign	exe_miss    = (s_pending)&&(s_tlb_hit)&&(r_exe)&&(!exe_flag);
	assign	table_err   = (s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit);
	assign	vpage       = tlb_vdata[s_tlb_addr];
	assign	ppage	    = tlb_pdata[s_tlb_addr];
 
	initial	pf_cachable = 1'b0;
	always @(posedge i_clk)
	if (i_reset)
		pf_cachable <= 1'b0;
	else
		pf_cachable <= cachable;
 
	initial	pf_stb = 1'b0;
	initial	last_ppage = 0;
	initial	last_vpage = 0;
	always @(posedge i_clk)
	if (i_reset)
	begin
		pf_stb <= 1'b0;
		last_ppage <= 0;
		last_vpage <= 0;
		last_tlb   <= 0;
	end else if ((!kernel_context)&&(r_pending)&&(!last_page_valid))
	begin
		last_tlb   <= s_tlb_addr;
		last_ppage <= ppage;
		last_vpage <= vpage;
		last_exe   <= exe_flag;
		last_ro    <= ro_flag;
		pf_stb <= 1'b1;
	end else
		pf_stb <= 1'b0;
 
	initial	status_word = 0;
	always @(posedge i_clk)
	if (i_reset)
		status_word <= 0;
	else if (wr_control)
		status_word <= 0;
	else if ((table_err)||(ro_miss)||(simple_miss)||(exe_miss))
		status_word <= { r_vpage,
				{(LGPGSZB-4){1'b0}},
				(table_err), (exe_miss),
				(ro_miss), (simple_miss) };
 
	initial	last_page_valid = 1'b0;
	always @(posedge i_clk)
	if (i_reset)
		last_page_valid <= 1'b0;
	else if ((i_wbs_cyc_stb)&&(i_wbs_we))
		last_page_valid <= 1'b0;
	else if (!kernel_context)
	begin
		if (!o_rtn_stall)
			// A new bus request
			last_page_valid <= (last_page_valid)
				&&(i_wbm_addr[(DW-3):(DW-2-VAW)] == last_vpage);
		else if ((r_pending)&&(!last_page_valid))
			last_page_valid <= (s_pending)&&(s_tlb_hit);
	end
 
	parameter	LGFIFO = 6;
	reg	[LGFIFO-1:0]	bus_outstanding;
	initial	bus_outstanding = 0;
	always @(posedge i_clk)
	if (i_reset)
		bus_outstanding <= 0;
	else if (!o_cyc)
		bus_outstanding <= 0;
	else case({ (o_stb)&&(!i_stall), (i_ack)||(i_err) } )
	2'b01: bus_outstanding <= bus_outstanding - 1'b1;
	2'b10: bus_outstanding <= bus_outstanding + 1'b1;
	default: begin end
	endcase
 
	reg	bus_pending;
	initial	bus_pending = 0;
	always @(posedge i_clk)
	if (i_reset)
		bus_pending <= 0;
	else if (!o_cyc)
		bus_pending <= 1'b0;
	else case({ (o_stb)&&(!i_stall), ((i_ack)||(i_err)) })
	2'b01: bus_pending <= (bus_outstanding > 1);
	2'b10: bus_pending <= 1'b1;
	default: begin end
	endcase
 
	initial	rtn_err = 1'b0;
	initial	o_cyc   = 1'b0;
	always @(posedge i_clk)
	if (i_reset)
	begin
		o_cyc <= 1'b0;
		rtn_err   <= 1'b0;
	end else begin
		o_cyc <=  (i_wbm_cyc)&&(!o_rtn_err)&&((!i_err)||(!o_cyc)); /// &&((o_cyc)||(r_valid));
 
		rtn_err  <= (i_wbm_cyc)&&(i_err)&&(o_cyc);
	end
 
	generate if (OPT_DELAY_RETURN)
	begin
		reg		r_rtn_ack;
		reg	[31:0]	r_rtn_data;
 
		initial	r_rtn_data = 0;
		initial	r_rtn_ack  = 0;
		always @(posedge i_clk)
		if (i_reset)
		begin
			r_rtn_ack  <= 0;
			r_rtn_data <= 0;
		end else begin
			r_rtn_ack  <= (i_wbm_cyc)&&(i_ack)&&(o_cyc);
			r_rtn_data <= i_data;
		end
 
		assign	o_rtn_ack  = r_rtn_ack;
		assign	o_rtn_data = r_rtn_data;
	end else begin
 
		assign	o_rtn_ack  = (i_ack)&&(o_cyc);
		assign	o_rtn_data = i_data;
	end endgenerate
 
	assign	o_stb = (r_valid);
	assign	o_we  =  (r_we);
	assign	o_rtn_stall = (i_wbm_cyc)&&(
			(o_rtn_err)
			||((r_pending)&&(!r_valid))
			||((o_stb)&&(i_stall))
			||(miss_pending));
 
	initial	miss_pending = 0;
	always @(posedge i_clk)
	if (i_reset)
		miss_pending <= 0;
	else if (!i_wbm_cyc)
		miss_pending <= 0;
	else
		miss_pending <= (i_wbm_cyc)&&(
				(simple_miss)||(ro_miss)||(exe_miss)
				||((s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit)));
 
	assign	o_rtn_miss  = (miss_pending)&&(!bus_pending);
	assign	o_rtn_err   = (rtn_err);
 
	assign	o_sel  = r_sel;
	assign	o_data = r_data;
 
	//
	assign	o_wbs_stall = 1'b0;
	initial	o_wbs_ack = 1'b0;
	always @(posedge i_clk)
	if (i_reset)
		o_wbs_ack <= 1'b0;
	else
		o_wbs_ack <= (i_wbs_cyc_stb);
	always @(posedge i_clk)
	if (i_reset)
		o_wbs_data <= 0;
	else case({i_wbs_addr[LGTBL+1],i_wbs_addr[0]})
	2'b00: o_wbs_data <= w_control_data;
	2'b01: o_wbs_data <= status_word;
	2'b10: o_wbs_data <= w_vtable_reg;
	2'b11: o_wbs_data <= w_ptable_reg;
	endcase
 
	//
	// Bus snooping returns ...
	//
	assign	pf_return_stb = pf_stb;
	assign	pf_return_we = r_we;
	assign	pf_return_p  = last_ppage;
	assign	pf_return_v  = last_vpage;
	assign	pf_return_cachable = pf_cachable;
 
	// Also requires being told when/if the page changed
	// So, on a page change,
	// pf_return_we = 1
	// pf_stb = 1
	// and pf_return_p has the physical address
 
	// Make verilator happy
	// verilator lint_off UNUSED
	wire [(PAW-1):0]	unused;
	assign	unused = r_ppage;
	generate if (4+LGCTXT < LGPGSZB)
	begin
		wire	[LGPGSZB-(4+LGCTXT)-1:0]	unused_data;
		assign	unused_data = i_wbs_data[LGPGSZB-1:4+LGCTXT];
	end endgenerate
 
	wire	unused_always;
	assign	unused_always = s_tlb_flags[0];
	// verilator lint_on  UNUSED
 
`ifdef	FORMAL
	initial	f_past_valid = 0;
	always @(posedge i_clk)
		f_past_valid <= 1'b1;
 
	initial	assume(i_reset);
	always @(*)
		if (!f_past_valid)
			assume(i_reset);
 
	always @(*)
		if (i_reset)
			assume(!i_wbs_cyc_stb);
	always @(posedge i_clk)
	if (f_past_valid)
		assert(o_wbs_ack == $past(i_wbs_cyc_stb));
	always @(*)
		assert(o_wbs_stall == 1'b0);
 
	always @(*)
		assume((!i_wbm_cyc)||(!i_wbs_cyc_stb));
 
	localparam	F_LGDEPTH = 6;
	reg	[F_LGDEPTH-1:0]	fv_nreqs, fv_nacks, fv_outstanding,
			fp_nreqs, fp_nacks, fp_outstanding;
 
	localparam	F_MAX_STALL = 3,
			F_MAX_WAIT  = 2,
			F_MAX_REQ   = 9;
 
	//
	// The stall period needs to be long enough to allow all in-progress
	// transactions to complete, as in the case of a page miss.  Hence,
	// the max stall amount depends upon the max wait time for the
	// physical half of the interaction.  It is artificially limited here
	// in order to limit the amount of proof time required.
	//
	fwb_slave #(.F_MAX_STALL(F_MAX_STALL+(F_MAX_WAIT*F_MAX_REQ)+2),
			.AW(DW-2),
			.F_MAX_ACK_DELAY(F_MAX_STALL+F_MAX_WAIT+5),
			.F_MAX_REQUESTS(F_MAX_REQ),
			.F_LGDEPTH(F_LGDEPTH),
			.F_OPT_MINCLOCK_DELAY(0))
		busslave(i_clk, i_reset,
				i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_addr,
					i_wbm_data, i_wbm_sel,
				o_rtn_ack, o_rtn_stall, o_rtn_data,
					o_rtn_err|o_rtn_miss,
				fv_nreqs, fv_nacks, fv_outstanding);
 
	fwb_master #(.F_MAX_STALL(F_MAX_STALL),
			.AW(ADDRESS_WIDTH),
			.F_MAX_ACK_DELAY(F_MAX_WAIT),
			.F_MAX_REQUESTS(F_MAX_REQ),
			.F_LGDEPTH(F_LGDEPTH),
			.F_OPT_MINCLOCK_DELAY(0))
		busmaster(i_clk, i_reset,
				o_cyc, o_stb, o_we, o_addr,
					o_data, o_sel,
				i_ack, i_stall, i_data, i_err,
				fp_nreqs, fp_nacks, fp_outstanding);
 
	always @(*)
		assert((!o_cyc)||(fp_outstanding == bus_outstanding));
 
	always @(*)
		assume(fv_nreqs < F_MAX_REQ);
	always @(*)
	if ((i_wbm_cyc)&&(o_cyc)&&(fv_outstanding == fp_outstanding))
		assert(fv_nreqs == fp_nreqs);
	always @(*)
	if ((i_wbm_cyc)&&(o_cyc))
	begin
		assert(fp_nreqs <= fv_nreqs);
		assert(fp_nacks >= fv_nacks);
	end
 
	reg	[F_LGDEPTH-1:0]	f_expected, f_ex_nreqs, f_ex_nacks;
	always @(*)
	if (!i_wbm_cyc)
	begin
		f_ex_nreqs <= 0;
		f_ex_nacks <= 0;
		f_expected <= 0;
	end else if (OPT_DELAY_RETURN)
	begin
		if (r_pending)
		begin
			f_ex_nreqs <= fp_nreqs + 1'b1;
			f_ex_nacks <= fp_nacks + o_rtn_ack;
			f_expected <= fp_outstanding + 1'b1
						+ o_rtn_ack;
		end else begin
			f_expected <= fp_outstanding + (o_stb)
				+ (o_rtn_ack);
			f_ex_nreqs <= fp_nreqs + o_stb;
			f_ex_nacks <= fp_nacks + o_rtn_ack;
		end
	end else begin
		if (r_pending)
		begin
			f_ex_nreqs <= fp_nreqs + 1'b1;
			f_ex_nacks <= fp_nacks;
			f_expected <= fp_outstanding + 1'b1;
		end else begin
			f_ex_nreqs <= fp_nreqs + o_stb;
			f_ex_nacks <= fp_nacks;
			f_expected <= fp_outstanding + (o_stb);
		end
	end
 
	reg	f_kill_input;
	initial	f_kill_input = 1'b0;
	always @(posedge i_clk)
		f_kill_input <= (i_wbm_cyc)&&(
			(i_reset)
			||(o_rtn_miss)
			||(o_rtn_err));
	always @(*)
		if (f_kill_input)
			assume(!i_wbm_cyc);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(o_rtn_miss))&&($past(i_wbm_cyc)))
	begin
		assume(!o_cyc);
		assume(!i_wbm_cyc);
	end
 
	wire	fv_is_one, fp_is_zero;
	assign	fv_is_one  = (fv_outstanding == 1);
	assign	fp_is_zero = (fp_outstanding == 0);
	always @(*)
	if ((i_wbm_cyc)&&(o_cyc))
	begin
		if (o_rtn_miss)
		begin
			assert(fp_outstanding == 0);
			assert(fv_outstanding == 1);
			assert(fv_is_one);
			assert(fp_is_zero);
		end else begin
			assert(fv_nreqs == f_ex_nreqs);
			assert(fv_nacks == f_ex_nacks);
			assert(fv_outstanding >= fp_outstanding);
			assert(fv_outstanding == f_expected);
		end
	end
 
	always @(*)
		assert(z_context == (r_context_word == 0));
	always @(*)
		assert(kernel_context == ( ((r_context_word == 0)||(!i_gie)) ? 1'b1 : 1'b0));
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(i_wbs_cyc_stb)))
		assume(!i_wbm_cyc);
	always @(*)
	if (o_wbs_ack)
		assume(!i_wbm_cyc);
 
	always @(*)
		assert((!i_wbm_cyc)||(!o_wbs_ack));
	always @(posedge i_clk)
	if ((f_past_valid)&&(r_pending)&&($past(kernel_context))
			&&($past(i_wbm_stb))&&(!$past(i_stall))&&(i_wbm_cyc)
			&&(!o_rtn_stall))
		assert(o_addr[(AW-1):0] == $past(i_wbm_addr[(AW-1):0]));
	always @(*)
		assert(bus_pending == (bus_outstanding > 0));
 
	always @(*)
	if ((s_pending)&&(!s_tlb_miss))
		assert(r_tlb_match[s_tlb_addr]);
 
	// Check out all of the criteria which should clear these flags
	always @(posedge i_clk)
	if ((f_past_valid)&&(($past(i_reset))
			||(!$past(i_wbm_cyc))
			||(!$past(o_rtn_stall))))
	begin
		assert(!simple_miss);
		assert(!ro_miss);
		assert(!exe_miss);
		assert(!table_err);
		if (!$past(i_wbm_we))
			assert(!ro_miss);
 
		if (!kernel_context)
		begin
			assert((!o_stb)||(!(simple_miss|ro_miss|table_err)));
			// This doesn't belong on the clear list, but on the
			// should be set list
			// assert((!o_stb)||(!s_tlb_hit));
		end
	end
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
			&&(!$past(o_rtn_stall)))
	begin
		if ((!$past(kernel_context))&&(o_stb))
			assert((last_page_valid)||(s_tlb_hit));
	end
 
	reg	[(LGTBL-1):0]		f_last_page;
	always @(posedge i_clk)
	if ((f_past_valid)&&(!kernel_context)&&(r_pending)&&(!last_page_valid))
		f_last_page <= s_tlb_addr;
 
	wire	[3:0]	tlb_flag_last_page;
	assign	tlb_flag_last_page = tlb_flags[f_last_page];
	always @(*)
	if (last_page_valid)
	begin
		assert(tlb_valid[f_last_page]);
		assert(last_tlb   == f_last_page);
		assert(last_ppage == tlb_pdata[f_last_page]);
		assert(last_vpage == tlb_vdata[f_last_page]);
		assert(last_ro    == tlb_flag_last_page[`ROFLAG]);
		assert(last_exe   == tlb_flag_last_page[`EXEFLG]);
		assert(r_context_word[LGCTXT-1:1] == tlb_cdata[f_last_page][LGCTXT-1:1]);
		if (!r_context_word[0])
			assert(!tlb_cdata[f_last_page][0]);
		assert((!r_context_word[0])||(r_context_word[0]));
	end
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(i_reset))
			&&($past(last_page_valid))&&(!$past(kernel_context))
			&&($past(o_stb))&&($past(i_wbm_cyc)))
		assert(tlb_accessed[$past(last_tlb)]);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(i_reset))
			&&($past(pending_page_valid))&&(!$past(kernel_context))
			&&($past(o_stb))&&($past(i_wbm_cyc)))
		assert(tlb_accessed[$past(s_tlb_addr)]);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(kernel_context))&&(o_stb))
	begin
		assert(last_page_valid);
		assert(r_ppage == last_ppage);
		assert((!last_ro)||(!o_we));
	end
 
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(o_stb))&&(o_stb)&&(i_wbm_cyc))
		assert((last_page_valid)||(kernel_context));
 
	always @(*)
		assert((!s_tlb_hit)||(!s_tlb_miss));
	// always @(*)
	// if ((fp_outstanding > 0)&&(o_cyc)&&(!o_stb)&&(!r_pending)&&(!kernel_context))
		// assert(last_page_valid);
	// always @(*) assume(kernel_context);
	always @(*)
		assume((!i_wbs_cyc_stb)||(!i_gie));
 
	reg	f_past_gie, f_past_wbm_cyc;
 
	initial	f_past_gie = 1'b0;
	always @(posedge i_clk)
		f_past_gie <= i_gie;
 
	initial	f_past_wbm_cyc = 1'b0;
	always @(posedge i_clk)
		f_past_wbm_cyc <= i_wbm_cyc;
	always @(*)
	if ((f_past_valid)&&(bus_pending))
		assume(i_gie == f_past_gie);
	always @(*)
	if ((f_past_wbm_cyc)&&(i_wbm_cyc))
		assume(i_gie == f_past_gie);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(i_wbm_cyc)&&($past(i_wbm_cyc)))
		assume(i_gie == $past(i_gie));
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(i_reset)))
		assume(!i_gie);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc))
			&&($past(!kernel_context))
			&&($past(r_pending))
			&&(!$past(last_page_valid)))
	begin
		if (($past(s_tlb_hit))
				&&(!$past(ro_miss))
				&&(!$past(exe_miss)))
		begin
			assert(last_vpage == $past(r_vpage));
			assert(last_page_valid);
			assert(!miss_pending);
			assert(tlb_accessed[s_tlb_addr]);
		end else if (($past(s_tlb_hit))&&($past(ro_miss)))
		begin
			assert(miss_pending);
			assert(last_page_valid);
			assert(status_word[3:0] == 4'h2);
		end else if (($past(s_tlb_hit))&&($past(exe_miss)))
		begin
			assert(miss_pending);
			assert(last_page_valid);
			assert(status_word[3:0] == 4'h4);
		end else if (($past(s_tlb_hit))&&($past(simple_miss)))
		begin
			assert(miss_pending);
			assert(last_page_valid);
			assert(status_word[3:0] == 4'h1);
		end else if (!$past(s_tlb_hit))
		begin
			assert(!last_page_valid);
		end
	end
 
	always @(*)
		assert((!ro_miss)||(!exe_miss)||(!simple_miss)||(!table_err));
 
	reg	[4:0]	f_tlb_pipe;
 
	initial	f_tlb_pipe = 5'h0;
	always @(posedge i_clk)
	if (i_reset)
		f_tlb_pipe <= 5'h0;
	else if ((!r_pending)||(o_stb))
		f_tlb_pipe <= 5'h0;
	else if ((r_pending)&&(!r_valid)&&(!miss_pending))
		f_tlb_pipe <= { f_tlb_pipe[3:0], 1'b1 };
 
	always @(*)
		assert(f_tlb_pipe != 5'h1f);
 
	always @(*) // WE or EXE, never both
	assume((!i_wbm_stb)||(!i_wbm_we)||(!i_wbm_exe));
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(i_wbm_stb))&&($past(o_rtn_stall)))
		assume(i_wbm_exe == $past(i_wbm_exe));
 
	always @(*)
		assert((!r_pending)||(!o_stb));
	always @(*)
		assert((!s_pending)||(!o_stb));
	always @(*)
		assert((!s_pending)||(r_pending));
	always @(posedge i_clk)
	if ((f_past_valid)&&($past(i_wbm_cyc)))
		assume(!i_wbs_cyc_stb);
 
	always @(posedge i_clk)
	if ((f_past_valid)&&(|status_word[3:0])&&(!$past(i_wbm_cyc)))
		assume(!i_gie);
`endif
endmodule
 

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.