si_lctrl.v 10.8 KB
// si_lctrl.v v1 Frank Berndt
// si local controller module;
// :set tabstop=4

// x/y stick thoughts;
// A very fast hand could move the stick from left to right about
// ten times per second max, resulting in 10*256=2560 counts/sec.
// If we assume that lead, high, tail and low time make up 25%
// of the pulse time, and that we need two samples, then the maximum
// sample period must be no more than 90us.

module si_lctrl (
	sysclk, reset,
	write_data, write_enable, read_data,
	sgl_mode, sgl_err,
	clk, start, busy,
	b_req, b_val, b_but, b_x, b_y,
	bclk_div, bsmp_en
);

`include "jctrl.vh"

	// module io ports;

	input sysclk;				// system clock;
	input reset;				// controller and system reset;

	input [63:0] write_data;	// dma write data;
	input write_enable;			// dma write enable;
	output [55:0] read_data;	// dma read data;
	input sgl_mode;				// single command error detection mode;
	output sgl_err;				// single command error;
	input clk;					// clock reference (jchan);
	input start;				// start controller command;
	output busy;				// controller interface busy;

	output b_req;				// request button sample;
	input b_val;				// button sample valid;
	input [13:0] b_but;			// button sample;
	input [1:0] b_x;			// stick x signals;
	input [1:0] b_y;			// stick y signals;

	input [5:0] bclk_div;		// button sample clock divider;
	input bsmp_en;				// enable button sampling;

	// local controller is never busy;

	assign busy = 0;

	// channel data registers;
	// most only here for joychannel compatibility;

	wire [4:0] status;		// joychannel cmd status;
	reg sgl_err;			// single command error;
	reg [5:0] tx_size;		// # of bytes to transmit, including cmd;
	reg [5:0] rx_size;		// # of receive bytes;
	reg [7:0] cmd;			// controller command;
	reg size_err;			// size error;

	// tx size, rx size and cmd are only set on write dma;
	// only commands with tx_size 0 or 1 are supported;

	wire rx3, rx4;			// valid rx sizes;
	wire bad_size;			// bad tx or rx size;

	assign bad_size = |write_data[55:49]	// tx 2..255;
		| (|write_data[47:46]);				// rx 64..255;

	always @(posedge sysclk)
	begin
		if(write_enable) begin
			tx_size <= write_data[53:48];
			rx_size <= write_data[45:40];
			size_err <= bad_size;
			cmd <= write_data[39:32];
		end
		if(reset)
			sgl_err <= 0;
		else if(write_enable)
			sgl_err <= sgl_mode & (write_data[63:56] == 8'h00);
	end

	assign rx3 = (rx_size == 6'd3);
	assign rx4 = (rx_size == 6'd4);

	// decode supported commands;

	reg ctrlrst;			// controller reset;
	reg [2:0] cmd_enable;	// delayed write enable;
	reg cmd_status;			// get controller status;
	reg cmd_query;			// query controller;
	reg cmd_reset;			// reset controller and get status;
	wire size_val;			// size if valid for command;

	always @(posedge sysclk)
	begin
		ctrlrst <= ~start & (ctrlrst | reset);
		cmd_enable[1:0] <= { cmd_enable[0], write_enable };
		cmd_enable[2] <= cmd_enable[1] & ~size_err & size_val;
		if(cmd_enable[0]) begin
			cmd_status <= (cmd == `JCTRL_STATUS);
			cmd_query <= (cmd == `JCTRL_QUERY);
			cmd_reset <= (cmd == `JCTRL_RESET);
		end
	end

	assign size_val = ((cmd_status | cmd_reset) & rx3) | (cmd_query & rx4);

	// return channel bytes as dma read dma;
	// set no response error for illegal commands;
	// set frame error for bad tx or rx sizes;

	wire norsp;				// no response;
	wire reqerr;			// request error;
	wire framerr;			// frame error;
	wire collision;			// collision;
	wire [31:0] rsp_status;	// status response;
	wire [31:0] rsp_query;	// query response;
	wire [31:0] rsp_data;	// response data;

	assign norsp = ~cmd_status & ~cmd_query & ~cmd_reset;
	assign reqerr = size_err | ~size_val;
	assign framerr = 1'b0;
	assign collision = 1'b0;

	assign status = { norsp, reqerr, framerr, collision, ctrlrst };
	assign rsp_data = (cmd_status | cmd_reset)? rsp_status : rsp_query;
	assign read_data = { 2'd0, tx_size, status, rx_size[2:0], cmd, rsp_data };

	// return fixed type/status or query bits;
	// no extended command support;
	// no joyport;
	// x/y counts are absolute;
	// no EEPROM;
	// device ID is 0x0 for BB;
	// no joyport CRC error, or card presense info;

	assign rsp_status = { 8'h05, 8'h00, 8'h00, 8'h00 };

	// button sample clock divider;

	reg [15:0] bclk_cnt;	// button sample clock counter;
	wire bclk_eq;			// bclk counter equal divider;
	reg bclk_zero;			// zero bclk divider;
	reg b_req;				// button request;

	assign bclk_eq = (bclk_cnt[15:10] == bclk_div);

	always @(posedge sysclk)
	begin
		bclk_zero <= reset | ~bsmp_en | bclk_eq;
		if(bclk_zero)
			bclk_cnt <= 16'b0;
		else if(clk)
			bclk_cnt <= (bclk_cnt + 1);
		b_req <= bsmp_en & bclk_zero;
	end

	// button glitch removal;
	// require 3 consequutive 0s and 1s to change button status;
	// button 13..0 correspond to bit position in jchan response;

	reg [1:0] b0in;			// button 0 sampling register;
	reg [1:0] b1in;			// button 1 sampling register;
	reg [1:0] b2in;			// button 2 sampling register;
	reg [1:0] b3in;			// button 3 sampling register;
	reg [1:0] b4in;			// button 4 sampling register;
	reg [1:0] b5in;			// button 5 sampling register;
	reg [1:0] b6in;			// button 6 sampling register;
	reg [1:0] b7in;			// button 7 sampling register;
	reg [1:0] b8in;			// button 8 sampling register;
	reg [1:0] b9in;			// button 9 sampling register;
	reg [1:0] b10in;		// button 10 sampling register;
	reg [1:0] b11in;		// button 11 sampling register;
	reg [1:0] b12in;		// button 12 sampling register;
	reg [1:0] b13in;		// button 13 sampling register;
	wire b0one, b0val;
	wire b1one, b1val;
	wire b2one, b2val;
	wire b3one, b3val;
	wire b4one, b4val;
	wire b5one, b5val;
	wire b6one, b6val;
	wire b7one, b7val;
	wire b8one, b8val;
	wire b9one, b9val;
	wire b10one, b10val;
	wire b11one, b11val;
	wire b12one, b12val;
	wire b13one, b13val;
	reg [13:0] button;		// button status;
	wire jsrst;				// magic L, R, START press to reset stick reference;

	assign b0one = &{ b_but[0], b0in };
	assign b0val = ~|{ b_but[0], b0in } | b0one;
	assign b1one = &{ b_but[1], b1in };
	assign b1val = ~|{ b_but[1], b1in } | b1one;
	assign b2one = &{ b_but[2], b2in };
	assign b2val = ~|{ b_but[2], b2in } | b2one;
	assign b3one = &{ b_but[3], b3in };
	assign b3val = ~|{ b_but[3], b3in } | b3one;
	assign b4one = &{ b_but[4], b4in };
	assign b4val = ~|{ b_but[4], b4in } | b4one;
	assign b5one = &{ b_but[5], b5in };
	assign b5val = ~|{ b_but[5], b5in } | b5one;
	assign b6one = &{ b_but[6], b6in };
	assign b6val = ~|{ b_but[6], b6in } | b6one;
	assign b7one = &{ b_but[7], b7in };
	assign b7val = ~|{ b_but[7], b7in } | b7one;
	assign b8one = &{ b_but[8], b8in };
	assign b8val = ~|{ b_but[8], b8in } | b8one;
	assign b9one = &{ b_but[9], b9in };
	assign b9val = ~|{ b_but[9], b9in } | b9one;
	assign b10one = &{ b_but[10], b10in };
	assign b10val = ~|{ b_but[10], b10in } | b10one;
	assign b11one = &{ b_but[11], b11in };
	assign b11val = ~|{ b_but[11], b11in } | b11one;
	assign b12one = &{ b_but[12], b12in };
	assign b12val = ~|{ b_but[12], b12in } | b12one;
	assign b13one = &{ b_but[13], b13in };
	assign b13val = ~|{ b_but[13], b13in } | b13one;

	always @(posedge sysclk)
	begin
		if(b_val) begin
			b0in <= { b0in[0], b_but[0] };
			b1in <= { b1in[0], b_but[1] };
			b2in <= { b2in[0], b_but[2] };
			b3in <= { b3in[0], b_but[3] };
			b4in <= { b4in[0], b_but[4] };
			b5in <= { b5in[0], b_but[5] };
			b6in <= { b6in[0], b_but[6] };
			b7in <= { b7in[0], b_but[7] };
			b8in <= { b8in[0], b_but[8] };
			b9in <= { b9in[0], b_but[9] };
			b10in <= { b10in[0], b_but[10] };
			b11in <= { b11in[0], b_but[11] };
			b12in <= { b12in[0], b_but[12] };
			b13in <= { b13in[0], b_but[13] };
			if(b0val)
				button[0] <= b0one;
			if(b1val)
				button[1] <= b1one;
			if(b2val)
				button[2] <= b2one;
			if(b3val)
				button[3] <= b3one;
			if(b4val)
				button[4] <= b4one;
			if(b5val)
				button[5] <= b5one;
			if(b6val)
				button[6] <= b6one;
			if(b7val)
				button[7] <= b7one;
			if(b8val)
				button[8] <= b8one;
			if(b9val)
				button[9] <= b9one;
			if(b10val)
				button[10] <= b10one;
			if(b11val)
				button[11] <= b11one;
			if(b12val)
				button[12] <= b12one;
			if(b13val)
				button[13] <= b13one;
		end
	end

	assign jsrst = button[4] & button[12] & button[13];

	// stick inputs are sampled every jchan clk;
	// require three consequitive samples to deglitch;

	reg [2:0] x0_smp;		// sampled x0;
	reg [2:0] x1_smp;		// sampled x1;
	reg [2:0] y0_smp;		// sampled y0;
	reg [2:0] y1_smp;		// sampled y1;
	wire [1:0] x_sig;		// deglitched x signals;
	wire [1:0] y_sig;		// deglitched y signals;

	always @(posedge sysclk)
	begin
		if(clk) begin
			x0_smp <= { x0_smp[1:0], b_x[0] };
			x1_smp <= { x1_smp[1:0], b_x[1] };
			y0_smp <= { y0_smp[1:0], b_y[0] };
			y1_smp <= { y1_smp[1:0], b_y[1] };
		end
	end

	assign x_sig[0] = &x0_smp;
	assign x_sig[1] = &x1_smp;
	assign y_sig[0] = &y0_smp;
	assign y_sig[1] = &y1_smp;

	// stick x/y pulse discriminator;
	// detect an edge on either diff signal;

	reg [1:0] x_edge;		// x edge detects;
	reg [1:0] y_edge;		// y edge detects;
	wire x_inc, x_dec;		// x increment/decrement;
	wire y_inc, y_dec;		// y increment/decrement;

	always @(posedge sysclk)
	begin
		x_edge <= x_sig;
		y_edge <= y_sig;
	end

	// decrement on rising edge of sig1 and sig0=1;
	// increment on rising edge of sig0 and sig1=1;

	assign x_dec = (~x_edge[1] & x_sig[1] & x_sig[0])
		| (x_edge[1] & ~x_sig[1] & ~x_sig[0])
		| (x_edge[0] & ~x_sig[0] & x_sig[1])
		| (~x_edge[0] & x_sig[0] & ~x_sig[1]);
	assign x_inc = (~x_edge[0] & x_sig[0] & x_sig[1])
		| (x_edge[0] & ~x_sig[0] & ~x_sig[1])
		| (~x_edge[1] & x_sig[1] & ~x_sig[0])
		| (x_edge[1] & ~x_sig[1] & x_sig[0]);
	assign y_dec = (~y_edge[1] & y_sig[1] & y_sig[0])
		| (y_edge[1] & ~y_sig[1] & ~y_sig[0])
		| (y_edge[0] & ~y_sig[0] & y_sig[1])
		| (~y_edge[0] & y_sig[0] & ~y_sig[1]);
	assign y_inc = (~y_edge[0] & y_sig[0] & y_sig[1])
		| (y_edge[0] & ~y_sig[0] & ~y_sig[1])
		| (~y_edge[1] & y_sig[1] & ~y_sig[0])
		| (y_edge[1] & ~y_sig[1] & y_sig[0]);

	// stick x/y counters;
	// count from -128..+127, no overflow;

	reg [7:0] x, y;			// x/y position;
	reg xy_rst;				// reset x/y counters;
	reg x_min, x_max;		// x counter bounds;
	reg y_min, y_max;		// y counter bounds;

	always @(posedge sysclk)
	begin
		x_min <= (x == 8'h80);
		x_max <= (x == 8'h7f);
		y_min <= (y == 8'h80);
		y_max <= (y == 8'h7f);
		if(xy_rst)
			x <= 8'h00;
		else if(x_inc & ~x_max)
			x <= x + 1;
		else if(x_dec & ~x_min)
			x <= x - 1;
		if(xy_rst)
			y <= 8'h00;
		else if(y_inc & ~y_max)
			y <= y + 1;
		else if(y_dec & ~y_min)
			y <= y - 1;
	end

	// reset x/y counters;
	// on controller or system reset;
	// on reset command;
	// or magic L, R, START button press;

	wire js_xyrst;			// button L,R,START reset;
	wire cmd_xyrst;			// cmd 255 reset;

	assign js_xyrst = jsrst & b_val;
	assign cmd_xyrst = cmd_enable[2] & cmd_reset & tx_size[0];

	always @(posedge sysclk)
	begin
		xy_rst <= reset | js_xyrst | cmd_xyrst;
	end

	// return query response;

	assign rsp_query = { button[7:0], { jsrst, 1'b0, button[13:8] }, x, y };

endmodule