cpu_mon.v 5.13 KB
// cpu_mon.v v1 Frank Berndt
// cpu sysad bus monitor;
// :set tabstop=4

`timescale 1ps/1ps

module cpu_mon (
	sysclk,
	divmode, coldrst_l, warmrst_l, pll_lock,
	eok_l,
	sysad_out, syscmd_out, pvalid_l,
	sysad_in, syscmd_in, evalid_l,
	int_l, nmi_l,
	secure,
	stim_load,
	stim_set
);
	input sysclk;
	input [2:0] divmode;
	input coldrst_l;
	input warmrst_l;
	input [1:0] pll_lock;
	input eok_l;
	input [31:0] sysad_out;		// from cpu;
	input [4:0] syscmd_out;
	input pvalid_l;
	input [31:0] sysad_in;		// to cpu;
	input [4:0] syscmd_in;
	input evalid_l;
	input [4:0] int_l;
	input nmi_l;
	input secure;
	input stim_load;
	input stim_set;

	// monitor cpu activity;

	reg cpu_mon;		// monitor enable flag;
	reg ready;			// cpu is ready;

	initial
	begin
		cpu_mon = $test$plusargs("cpu_mon");
		$display("%M: enabled %b", cpu_mon);
		ready = 0;
	end

	// monitor pll divmode;

	always @(divmode)
	begin
		$display("%t: %M: divmode %b", $time, divmode);
	end

	// monitor cpu resets;

	always @(coldrst_l or warmrst_l)
	begin
		$display("%t: %M: cpu coldrst %b warmrst %b", $time, coldrst_l, warmrst_l);
		ready = coldrst_l & warmrst_l;
	end

	// monitor secure mode;

	always @(secure)
	begin
		$display("%t: %M: secure mode %b", $time, secure);
	end

	// monitor system plls lock;

	reg pll_lockx;

	always @(pll_lock)
	begin
		$display("%t: %M: pll lock %b", $time, pll_lock);
		if(coldrst_l) begin
			pll_lockx = ^pll_lock;
			if(pll_lockx === 1'bx)
				$display("ERROR: %t: %M: pll_lock %b", $time, pll_lock);
		end
	end


	// monitor sysad bus activity;
	// always report fatal errors;

	reg r_eok_l;			// registered eok_l;
	reg [31:0] r_sysad_out;	// registered sysad_out;
	reg [4:0] r_syscmd_out;	// registered syscmd_out;
	reg r_pvalid_l;			// registered pvalid_l;
	reg [31:0] r_sysad_in;	// registered sysad_in;
	reg [4:0] r_syscmd_in;	// registered syscmd_in;
	reg r_evalid_l;			// registered evalid_l;
	reg issue;				// issue cycle;
	reg sout_x;				// address issue phase has Xs;
	reg cout_x;				// write data phase has Xs;
	integer size;			// request size in bytes;
	integer wdi;			// write data word index;
	integer rdi;			// response data word index;
	integer nwords;			// # of expected data words;
	reg slave;				// bus interface in slave;

	initial 
	begin
		slave = 0;
		nwords = 0;
	end

	// must register all cpu signals to avoid races;

	always @(posedge sysclk)
	begin
		r_eok_l <= eok_l;
		r_sysad_out <= sysad_out;
		r_syscmd_out <= syscmd_out;
		r_pvalid_l <= pvalid_l;
		r_sysad_in <= sysad_in;
		r_syscmd_in <= syscmd_in;
		r_evalid_l <= evalid_l;
	end

	// monitor request issue and write protocol;

	always @(negedge sysclk)
	begin
		issue = ~r_eok_l;
		if(ready) begin
			if((r_eok_l === 1'bx) | (r_eok_l === 1'bz))
				$display("ERROR: %t: %M: eok_l %b", $time, r_eok_l);
			if(r_pvalid_l === 1'b0) begin
				sout_x = ^r_sysad_out;
				cout_x = ^r_syscmd_out;
				if(cout_x === 1'bx)
					$display("ERROR: %t: %M: syscmd_out 0x%h", $time, r_syscmd_out);
				if(r_syscmd_out[4] === 1'b0) begin
					if(sout_x === 1'bx)
						$display("ERROR: %t: %M: sysad_out 0x%h", $time, r_sysad_out);
					size = r_syscmd_out[2]? (8 << r_syscmd_out[1:0]) : (r_syscmd_out[1:0] + 1);
					nwords = r_syscmd_out[2]? (2 << r_syscmd_out[1:0]) : 1;
					if(size > 32)
						$display("ERROR: %t: %M: syscmd_out size %0d", $time, size);
					if(issue) begin
						if(cpu_mon) begin
							$display("%t: %M: addr 0x%h, %s %0d", $time,
								r_sysad_out, r_syscmd_out[3]? "write" : "read ", size);
						end
						slave = ~r_syscmd_out[3];
					end
					wdi = 0;
					rdi = 0;
				end else if(r_syscmd_out[4] === 1'b1) begin
					if(cpu_mon)
						$display("%t: %M: data[%0d] 0x%h", $time, wdi, r_sysad_out);
					if(wdi >= nwords)
						$display("ERROR: %t: %M: excessive write data", $time);
					wdi = wdi + 1;
				end
			end else if(r_pvalid_l !== 1'b1)
				$display("ERROR: %t: %M: pvalid_l %b", $time, r_pvalid_l);
		end
	end

	// monitor response protocol;

	always @(negedge sysclk)
	begin
		if(ready) begin
			if(r_evalid_l === 1'b0) begin
				if(slave !== 1'b1)
					$display("ERROR: %t: %M: evalid_l when not in slave state", $time);
				casex(r_syscmd_in)
					5'b1000x,
					5'b1100x,
					5'b1001x,
					5'b1101x: begin
						if(rdi >= nwords)
							$display("ERROR: %t: %M: excessive read response data", $time);
						if(cpu_mon) begin
							$display("%t: %M: rsp[%0d] 0x%h last %b err %b",
								$time, rdi, r_sysad_in, ~r_syscmd_in[3], r_syscmd_in[1]);
						if(r_syscmd_in[3] === 1'b0)
							slave = 0;
						rdi = rdi + 1;
						end
					end
					default:
						$display("ERROR: %t: %M: syscmd_in %b", $time, r_syscmd_in);
				endcase
			end else if(r_evalid_l !== 1'b1)
				$display("ERROR: %t: %M: evalid_l %b", $time, r_evalid_l);
		end
	end

	// detect and log cpu interrupts and nmi;

	always @(int_l)
	begin
		if(ready & cpu_mon)
			$display("%t: %M: int_l %b", $time, int_l);
	end

	always @(nmi_l)
	begin
		if(ready & cpu_mon)
			$display("%t: %M: nmi_l %b", $time, nmi_l);
	end

	// Monitor secure timer
	always @(posedge sysclk) 
	begin
		if (cpu_mon) 
		begin
			if (stim_set) 
				$display("%t: %M: timer set", $time);

			if (stim_load)
				$display("%t: %M: timer load", $time);
		end

	end
endmodule