lctrl.v 4.84 KB
// lctrl.v v1 Frank Berndt
// local controller behavioral model;
// includes the board level driver;
// :set tabstop=4

`timescale 1ns/1ns

module lctrl (
	lx, ly, lout, lena
);
	output [1:0] lx;		// stick x signals;
	output [1:0] ly;		// stick x signals;
	output [15:0] lout;		// button sample data;
	input lena;				// enable button data onto io bus bus;

	// optionally turn on controller monitor;

	reg lctrl_mon;			// controller monitor flag;

	// behavioral model

	initial
	begin
		$display("%M: local controller behavioral model");
		lctrl_mon = $test$plusargs("lctrl_mon");
	end

	// controller registers used by simulation;
	// write button to set new value;
	// write xmove/ymove to trigger motion pulses;
	// negative values simulate motion towards the left/down;
	// positive values simulate motion towards the right/up;
	// inmotion in set as long as joystick is busy;
	// jitter is a bit mask over a 32-bit random which is added
	// to the programmed pulse times to jitter x/y motion;
	// xglitch/yglitch are used to test glitch removal;

	reg [15:0] button;		// current button status;
	integer xmove, ymove;	// x/y move counts;
	reg [1:0] xglitch;		// x glitch state;
	reg [1:0] yglitch;		// y glitch state;
	reg inmotion;			// x/y sticks in motion;
	integer jitter;			// randomize x/y pulse timing;

	initial
	begin
		button = 16'h0000;	// no button pressed;
		xmove = 0;			// no x motion;
		ymove = 0;			// no y motion;
		inmotion = 0;		// not in motion;
		jitter = 0;			// jitter off;
		xglitch = 2'd0;		// no x glitches;
		yglitch = 2'd0;		// no y glitches;
	end

	// print button changes;

	always @(button)
	begin
		if(lctrl_mon)
			$display("%t: %M: button change %b", $time, button);
	end

	// set motion lock;

	always @(xmove or ymove or xglitch or yglitch)
	begin
		inmotion = (xmove !== 0) | (ymove !== 0) | |xglitch | |yglitch;
	end

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

	// drive button sample onto io data bus;

	assign lout = lena? 16'bz : button;

	always @(posedge lena)
	begin
		if(lctrl_mon)
			$display("%t: %M: button sample %b", $time, button);
	end

	// x/y stick motion logic;
	// physical limits dictate how fast real hardware can move;
	// design limits in si dictate fastest edge detection;
	// si needs three jchan clocks to sample the diff signals,
	// ie. the minimal pulse times must be 4 jchan clocks;
	// for dv purposes, we want to set the pulses to the fastest possible;
	// timing is programmable, see timescale;

	integer tPulse;		// time between diff signals;
	integer tGlitch;	// glitch time;
	integer x_jitter;	// added x jitter;
	integer y_jitter;	// added y jitter;
	integer xd, yd;		// x/y count delta;

	reg [1:0] lx, ly;	// stick x/y signals;

`define	JCHAN_PERIOD	500

	initial begin
		lx = 'b00;
		ly = 'b00;
		tPulse = `JCHAN_PERIOD * 4;
		tGlitch = `JCHAN_PERIOD;
	end

	// next state tables;

	reg [1:0] strans [0:7];
	reg [2:0] sx, sy;	// x/y state index;

	initial
	begin
		strans[0] = 2; strans[1] = 1;
		strans[2] = 0; strans[3] = 3;
		strans[4] = 3; strans[5] = 0;
		strans[6] = 1; strans[7] = 2;
	end

	// x stick pulse modulation;

	always
	begin
		wait(xmove != 0);
		while(xmove != 0) begin
			if(lctrl_mon)
				$display("%t: %M: xmove %0d, lx %b", $time, xmove, lx);
			xd = -1;
			sx = lx << 1;
			if(xmove < 0) begin
				sx = sx + 1;
				xd = 1;
			end
			lx = strans[sx];
			x_jitter = $random & jitter;
			#(tPulse + x_jitter);
			if(xmove != 0)
				xmove = xmove + xd;
		end
	end

	// y stick pulse modulation;

	always
	begin
		wait(ymove != 0);
		while(ymove != 0) begin
			if(lctrl_mon)
				$display("%t: %M: ymove %0d, ly %b", $time, ymove, ly);
			yd = -1;
			sy = ly << 1;
			if(ymove < 0) begin
				sy = sy + 1;
				yd = 1;
			end
			ly = strans[sy];
			y_jitter = $random & jitter;
			#(tPulse + y_jitter);
			if(ymove != 0)
				ymove = ymove + yd;
		end
	end

	// x glitch insertion;

	always @(posedge xglitch[0])
	begin
		if(lctrl_mon)
			$display("%t: %M: xglitch[0] %b", $time, xglitch[0]);
		lx[0] = 1;
		#(tGlitch);
		lx[0] = 0;
		xglitch[0] = 0;
	end

	always @(posedge xglitch[1])
	begin
		if(lctrl_mon)
			$display("%t: %M: xglitch[1] %b", $time, xglitch[1]);
		lx[1] = 1;
		#(tGlitch);
		lx[1] = 0;
		xglitch[1] = 0;
	end

	// y glitch insertion;

	always @(posedge yglitch[0])
	begin
		if(lctrl_mon)
			$display("%t: %M: yglitch[0] %b", $time, yglitch[0]);
		ly[0] = 1;
		#(tGlitch);
		ly[0] = 0;
		yglitch[0] = 0;
	end

	always @(posedge yglitch[1])
	begin
		if(lctrl_mon)
			$display("%t: %M: yglitch[1] %b", $time, yglitch[1]);
		ly[1] = 1;
		#(tGlitch);
		ly[1] = 0;
		yglitch[1] = 0;
	end

	// reset model;
	// one last pulse could be in the xmove or ymove pipe;
	// caller must wait for that to expire;

	task lctrl_reset;
		begin
			$display("%t: %M", $time);
			xmove = 0;
			ymove = 0;
			xglitch = 2'd0;
			yglitch = 2'd0;
			x_jitter = 0;
			y_jitter = 0;
		end
	endtask

endmodule