cbus_mon.v 10.1 KB
// cbus_mon.v v1 Frank Berndt;
// BB cbus monitor;
// turn on with +cbus_mon;
// :set tabstop=4

module cbus_mon (
	sysclk, reset_l,
	cbus_command, cbus_select, cbus_data,
	cbus_val, cbus_dev,
	mi_cbus_write_request, mi_cbus_read_request, mi_dma_request, mi_cbus_write_enable,
	sp_cbus_write_enable,
	span_cbus_write_enable,
	cmd_cbus_write_enable,
	pi_cbus_write_enable,
	ri_cbus_write_enable,
	si_cbus_write_enable,
	ai_cbus_write_enable,
	vi_cbus_write_enable,
	mem_cbus_write_enable,
	ui_cbus_write_enable,
	si_dma_request,
	ui_dma_request,
	vi_dma_request,
	sp_dma_request,
	span_dma_request,
	cmd_dma_request,
	ai_dma_request,
	pi_dma_request,
);

`include "cbus.vh"

	input sysclk;		// system clock;
	input reset_l;		// global reset;

	// cbus globals;

	input [2:0] cbus_command;
	input [1:0] cbus_select;
	input [31:0] cbus_data;
	output cbus_val;
	output [3:0] cbus_dev;

	// per-device controls;

	input mi_cbus_write_request;
	input mi_cbus_read_request;
	input mi_dma_request;
	input mi_cbus_write_enable;

	input sp_cbus_write_enable;
	input span_cbus_write_enable;
	input cmd_cbus_write_enable;
	input pi_cbus_write_enable;
	input ri_cbus_write_enable;
	input si_cbus_write_enable;
	input ai_cbus_write_enable;
	input vi_cbus_write_enable;
	input mem_cbus_write_enable;
	input ui_cbus_write_enable;

	
	input si_dma_request;
	input ui_dma_request;
	input vi_dma_request;
	input sp_dma_request;
	input span_dma_request;
	input cmd_dma_request;
	input ai_dma_request;
	input pi_dma_request;

	// usage statistics;

	reg [63:0] cbus_idle;			// # of idle clocks;
	reg [63:0] cbus_clocks;			// cbus clocks;
	reg [31:0] dev_dma [0:10];		// dma requests per device;
	reg [63:0] dev_dmalen [0:10];	// dma length per device;
	reg [31:0] dev_dbus [0:10];		// dbus data cycles per device;
	reg [31:0] dev_write [0:10];	// write requests per device;
	reg [31:0] dev_read [0:10];		// read requests per device;
	reg [31:0] dev_rsp [0:10];		// read responses per device;
	reg [10:0] max_dma_req_pending;
	reg [10:0] min_dma_req_pending;
	reg [63:0] total_dma_req_pending;
	integer n, req;

	initial
	begin
		zero_idle;
		for(n = 0; n <= 10; n = n + 1)
			zero_stat(n);
		max_dma_req_pending = 0;
		min_dma_req_pending = 10;
	end

	// zero just the idle time;

	task zero_idle;
		begin
			cbus_idle = {64{1'b0}};
			cbus_clocks = {64{1'b0}};

			total_dma_req_pending = 64'b0;
			max_dma_req_pending = 0;
			min_dma_req_pending = 10;
		end
	endtask

	always @(posedge sysclk) begin
		req = 0;
		if (mi_dma_request) req = req+1;
		if (si_dma_request) req = req+1;
		if (ui_dma_request) req = req+1;
		if (vi_dma_request) req = req+1;
		if (sp_dma_request) req = req+1;
		if (span_dma_request) req = req+1;
		if (cmd_dma_request) req = req+1;
		if (ai_dma_request) req = req+1;
		if (pi_dma_request) req = req+1;

		if (req > max_dma_req_pending) max_dma_req_pending = req;
		if (req < min_dma_req_pending) min_dma_req_pending = req;
		total_dma_req_pending = total_dma_req_pending + req;
	end

	// zero per-device stats;

	task zero_stat;
		input [3:0] dev;
		begin
			dev_dma[dev] = 0;
			dev_dmalen[dev] = {64{1'b0}};
			dev_dbus[dev] = 0;
			dev_write[dev] = 0;
			dev_read[dev] = 0;
			dev_rsp[dev] = 0;
		end
	endtask


	// check result of cbus dump stat
	task check_stat;
		reg [63:0] total_read;
		reg [4*8-1:0] name;
		begin
			total_read = 64'b0;

			for (n = 0; n <= 10; n = n + 1) begin
				case(n)
					`CBUS_DEV_SP: name = "sp";
					`CBUS_DEV_CMD: name = "cmd";
					`CBUS_DEV_SPAN: name = "span";
					`CBUS_DEV_MI: name = "mi";
					`CBUS_DEV_VI: name = "vi";
					`CBUS_DEV_SI: name = "si";
					`CBUS_DEV_PI: name = "pi";
					`CBUS_DEV_RI: name = "ri";
					`CBUS_DEV_AI: name = "ai";
					`CBUS_DEV_MEM: name = "mem";
					`CBUS_DEV_UI: name = "ui";
					default: name = "?";
				endcase

				if (n != `CBUS_DEV_MI) begin
					if (dev_read[n] !=0) 
						$display("ERROR: %t: %M: %s reads not 0",  $time, name);
					if (dev_write[n] != 0) 
						$display("ERROR: %t: %M: %s wr not 0",  $time, name);
					total_read = total_read + dev_rsp[n];
				end else begin
					if (dev_rsp[n] != 0)
						$display("ERROR: %t: %M: %s rsp not 0",  $time, name);
				end
			end	

			if (total_read != dev_read[`CBUS_DEV_MI])
				$display("ERROR: %t: %M: Read and rsp not equal",  $time);
		end
	endtask

	// dump cbus statistics;

	task dump_stat;
		reg [63:0] total_dmalen;
		reg [31:0] total_dbus;
		reg [4*8-1:0] name;
		reg [63:0] usage;
		begin
			$display("%M: idle %0d clocks", cbus_idle);
			total_dmalen = 0;
			total_dbus = 0;
			check_stat;
			for(n = 0; n <= 10; n = n + 1) begin
				case(n)
					`CBUS_DEV_SP: name = "sp";
					`CBUS_DEV_CMD: name = "cmd";
					`CBUS_DEV_SPAN: name = "span";
					`CBUS_DEV_MI: name = "mi";
					`CBUS_DEV_VI: name = "vi";
					`CBUS_DEV_SI: name = "si";
					`CBUS_DEV_PI: name = "pi";
					`CBUS_DEV_RI: name = "ri";
					`CBUS_DEV_AI: name = "ai";
					`CBUS_DEV_MEM: name = "mem";
					`CBUS_DEV_UI: name = "ui";
					default: name = "?";
				endcase
				total_dmalen = total_dmalen + dev_dmalen[n];
				total_dbus = total_dbus + dev_dbus[n];
				$display("%t: %M: %0s: dma req %0d, len %0d bytes, dbus clocks %0d",
					$time, name, dev_dma[n], dev_dmalen[n], dev_dbus[n]);
				$display("%t: %M: %0s: writes %0d, reads %0d, rsp %0d",
					$time, name, dev_write[n], dev_read[n], dev_rsp[n]);
			end
			usage = ((cbus_clocks - cbus_idle) * 100) / cbus_clocks;
			$display("%t: %M: cbus usage: %0d clocks, %0d",
				$time, cbus_clocks, usage);
			usage = (total_dbus * 100) / cbus_clocks;
			$display("%t: %M: dbus usage: %0d clocks, %0d",
				$time, total_dbus, usage);
			$display("%t: %M: total dmalen %0d bytes", $time, total_dmalen);
			$display("%t: %M: DMA pending max=%d min=%d avg=%d", 
					 $time, max_dma_req_pending, min_dma_req_pending, 
					 total_dma_req_pending / cbus_clocks);
		end
	endtask

	// internals;

	reg [4*8-1:0] device;
	reg subblk, masked, down, nseq;
	reg [3:0] dev;
	reg [3:0] dev_idx;
	reg [7:0] delay;
	reg read;
	reg [6:0] cnt;
	reg [7:0] len;
	reg [31:0] data;
	reg [31:0] dma_addr;
	integer state;

`define	STATE_IDLE 0		// cbus idle;
`define	STATE_DMA_LENGTH 1	// cbus dma length;
`define	STATE_WRITE_DATA 2	// cbus write data;

	// +cbus_mon turns on the monitor;

	reg cbus_mon;

	initial
		cbus_mon = $test$plusargs("cbus_mon");

	always @(posedge sysclk)
	begin
		if(!reset_l)
			state <= `STATE_IDLE;
	end

	// must always drive controls that go to dbus_mon;

	reg cbus_dma;			// dma address phase;
	reg cbus_val;			// dma length phase;
	reg [3:0] cbus_dev;		// requesting device;

	always @(posedge sysclk)
	begin
		cbus_clocks <= cbus_clocks + 1;
		cbus_dma <= (cbus_command == `CBUS_CMD_DMA);
		cbus_val <= cbus_dma;
		if(cbus_dma)
			cbus_dev <= cbus_data[19:16];
	end

	// monitor code;

	always @(posedge sysclk)
	begin
		data = cbus_data;
		case(1'b1)
			mi_cbus_write_enable: device = "mi";
			sp_cbus_write_enable: device = "sp";
			span_cbus_write_enable: device = "span";
			cmd_cbus_write_enable: device = "cmd";
			pi_cbus_write_enable: device = "pi";
			ri_cbus_write_enable: device = "ri";
			si_cbus_write_enable: device = "si";
			ai_cbus_write_enable: device = "ai";
			vi_cbus_write_enable: device = "vi";
			mem_cbus_write_enable: device = "mem";
			ui_cbus_write_enable: device = "ui";
			default: device = "???";
		endcase
		case(1'b1)
			mi_cbus_write_enable: dev_idx = `CBUS_DEV_MI;
			sp_cbus_write_enable: dev_idx = `CBUS_DEV_SP;
			span_cbus_write_enable: dev_idx = `CBUS_DEV_SPAN;
			cmd_cbus_write_enable: dev_idx = `CBUS_DEV_CMD;
			pi_cbus_write_enable: dev_idx = `CBUS_DEV_PI;
			ri_cbus_write_enable: dev_idx = `CBUS_DEV_RI;
			si_cbus_write_enable: dev_idx = `CBUS_DEV_SI;
			ai_cbus_write_enable: dev_idx = `CBUS_DEV_AI;
			vi_cbus_write_enable: dev_idx = `CBUS_DEV_VI;
			mem_cbus_write_enable: dev_idx = `CBUS_DEV_MEM;
			ui_cbus_write_enable: dev_idx = `CBUS_DEV_UI;
		endcase

		case(state)
			`STATE_IDLE : begin
				case(cbus_command)
					`CBUS_CMD_IDLE : begin
						cbus_idle = cbus_idle + 1;
					end

					`CBUS_CMD_DMA : begin
						if(cbus_mon)
							$display("%t: cbus: %s dma address 0x%h", $time, device, data);
						dma_addr <= data;
						state <= `STATE_DMA_LENGTH;
						dev_dma[dev_idx] <= dev_dma[dev_idx] + 1;
					end

					`CBUS_CMD_WRITE : begin
						if(cbus_mon)
							$display("%t: cbus: %s write address 0x%h", $time, device, data);
						state <= `STATE_WRITE_DATA;
						dev_write[dev_idx] <= dev_write[dev_idx] + 1;
					end

					`CBUS_CMD_READ : begin
						if(cbus_mon)
							$display("%t: cbus: %s read address 0x%h", $time, device, data);
						state <= `STATE_IDLE;
						dev_read[dev_idx] <= dev_read[dev_idx] + 1;
					end

					`CBUS_CMD_RSP : begin
						if(cbus_mon)
							$display("%t: cbus: %s response data 0x%h", $time, device, data);
						state <= `STATE_IDLE;
						dev_rsp[dev_idx] <= dev_rsp[dev_idx] + 1;
					end

					default :
						$display("ERROR: %t: cbus: %s illegal command: %b",
							$time, device, cbus_command);
				endcase
			end

			`STATE_DMA_LENGTH : begin
				{subblk,masked,down,nseq,dev,delay,read,cnt} = cbus_data;
				len = (cnt - dma_addr[2:0]) + 1;
				if(cbus_mon)
					$display("%t: cbus: %s dma, subblk %b, masked %b, down %b, nseq %b, dev %d, delay 0x%h, read %b, cnt 0x%h (%d)",
						$time, device, subblk, masked, down, nseq, dev, delay, read, cnt, len);
				state <= `STATE_IDLE;
				dev_dmalen[dev_idx] <= dev_dmalen[dev_idx] + len;
				dev_dbus[dev_idx] <= dev_dbus[dev_idx] + cnt[6:3] + 1;
			end

			`STATE_WRITE_DATA : begin
				if(cbus_mon)
					$display("%t: cbus: %s write data 0x%h", $time, device, data);
				state <= `STATE_IDLE;
			end
		endcase
	end

	// per device cpu request monitors;
	// XXX add other cbus request assertions;

	wire pi_cbus_write_request;
	wire pi_cbus_read_request;
	wire si_cbus_write_request;
	wire si_cbus_read_request;

	cbus_req_mon mi_req (
		.device("mi  "),
		.write_request(mi_cbus_write_request),
		.read_request(mi_cbus_read_request),
		.dma_request(mi_dma_request)
	);

	cbus_req_mon pi_req (
		.device("pi  "),
		.write_request(pi_cbus_write_request),
		.read_request(pi_cbus_read_request),
		.dma_request(pi_dma_request)
	);

	cbus_req_mon si_req (
		.device("si  "),
		.write_request(si_cbus_write_request),
		.read_request(si_cbus_read_request),
		.dma_request(si_dma_request)
	);

endmodule