// Demo eventually controlling an R/C servo using the PCI bus.
// Fair bit of code from fpga4fun.com, and a fair bit Copyright (c) 2004 Joshua Wise.
// I think I've commented the PCI section pretty well, which is what I've been working on recently.
// -- Joshua Wise <joshua@joshuawise.com>

module RCServo(clk, RCServo_pulse);
	input clk;
	output RCServo_pulse;

	////////////////////////////////////////////////////////////////////////////
	// use the serial port to control the servo

	parameter [7:0] RxD_data_reg = 180;

	////////////////////////////////////////////////////////////////////////////
	// divide the clock

	parameter ClkDiv = 98;		// 25000000/1000/256 = 97.56

	reg [6:0] ClkCount;
	reg ClkTick;
	always @(posedge clk) ClkTick <= (ClkCount==ClkDiv-2);
	always @(posedge clk) if(ClkTick) ClkCount <= 0; else ClkCount <= ClkCount + 1;

	////////////////////////////////////////////////////////////////////////////
	reg [11:0] PulseCount;
	always @(posedge clk) if(ClkTick) PulseCount <= PulseCount + 1;

	// make sure the RCServo_position is stable while the pulse is generated
	reg [7:0] RCServo_position;
	always @(posedge clk) if(PulseCount==0) RCServo_position <= RxD_data_reg;

	reg RCServo_pulse;
	always @(posedge clk) RCServo_pulse <= (PulseCount < {4'b0001, RCServo_position});

endmodule

module LCDModule(clk, LCD_Data, LCD_E, LCD_nRS, LCD_RW, InpData, InpDataStrobe);
	input clk;
	output LCD_E;
	output LCD_nRS;
	output LCD_RW;
	output [7:0] LCD_Data;
	input [7:0] InpData;
	input InpDataStrobe;
	
	assign LCD_RW = 0;
	
	// LCD_E / LCD_Data handling
	wire GotData   = InpDataStrobe && (InpData != 8'b00000000);
	
	reg [2:0] LCD_E_Time;
	reg [7:0] LCD_Data;
	reg LCD_E;
		
	always @(posedge clk)
		if ((GotData) || (LCD_E_Time > 0))
			LCD_E_Time <= LCD_E_Time + 1;
			
	always @(posedge clk)
		if (GotData)
			LCD_Data <= InpData;
	
	always @(posedge clk)
		LCD_E <= LCD_E_Time && (LCD_E_Time < 6);
	
	always @(posedge clk)
		if ((InpDataStrobe) && (InpData == 8'b00000000))
			LCD_RS <= 1;
	
	// LCD_RS handling
	wire GotEscape = InpDataStrobe && (InpData == 8'b00000000);
	
	reg LCD_RS = 0;
	always @(posedge clk)
		if (!LCD_RS)				// If we're not already in the command mode...
			LCD_RS <= GotEscape;		// 	Only go in when we see the escape.
		else					// If we are...
			LCD_RS <= (LCD_E_Time < 7);	//	Only bail when the magic number is 7.
			
	assign LCD_nRS = ~LCD_RS;
endmodule

module PCI_Card(PCI_CLK, PCI_nRST, PCI_nFRAME, PCI_AD, PCI_CBE, PCI_nIRDY, PCI_nTRDY, PCI_nDEVSEL, PCI_IDSEL);
	input PCI_CLK;
	input PCI_nRST;
	input PCI_nFRAME;
	input PCI_nIRDY;
	inout [31:0] PCI_AD;
	input [3:0] PCI_CBE;
	inout PCI_nTRDY, PCI_nDEVSEL;
	input PCI_IDSEL;
	
	parameter CBE_IORD	= 4'b0010;
	parameter CBE_IOWR	= 4'b0011;
	parameter CBE_MemRD	= 4'b0110;
	parameter CBE_MemWR	= 4'b0111;
	parameter CBE_ConfRD	= 4'b1010;
	parameter CBE_ConfWR	= 4'b1011;
	
	// Transaction handling logic
	reg inTransaction;
	wire beginTransaction = ~inTransaction			// if we're not in a transaction already,
				&& ~PCI_nFRAME;		//   and FRAME is asserted,
								//   we begin transaction.
	wire endTransaction = inTransaction			// if we're in a transaction,
				&& PCI_nFRAME			//   and the frame is ending,
				&& PCI_nIRDY;			//   and the host is done,
								//   then the transaction is over.
	wire finalDataTransfer = PCI_nFRAME			// if the frame is ending,
				&& ~PCI_nIRDY			//   and the host is ready,
				&& ~PCI_nTRDY;			//   and we're ready,
								//   then it's the final data transfer.
	always @(posedge PCI_CLK or negedge PCI_nRST)
		if (~PCI_nRST)
			inTransaction <= 0;
		else
		begin
			if (inTransaction)			// If we're in a transaction,
				inTransaction <= ~endTransaction;	// we continue if we've not ended.
			else					// Otherwise, 
				inTransaction <= beginTransaction;	// we only start if we begin.
		end
	
	// Handle the various cycles
	reg [3:0] curCommand;
	reg [31:0] PCI_AD_OUT;
	reg PCI_AD_OE;
	
	assign PCI_AD = PCI_AD_OE ? PCI_AD_OUT : 32'bZ;
	
	always @(posedge PCI_CLK or negedge PCI_nRST)
		if (~PCI_nRST)
			curCommand <= 4'b0000;
		else if (beginTransaction)
			curCommand <= PCI_CBE;
			
	// Deal with configuration
	reg [31:0] confDatum;
	always @(posedge PCI_CLK)
		if ((curCommand == CBE_ConfRD) && beginTransaction && PCI_IDSEL)
			case (PCI_AD[3:0])
				8'h00:
					confDatum <= 32'h00010001;
			endcase
		
	always @(posedge PCI_CLK)
		if ((curCommand == CBE_ConfRD) && inTransaction && PCI_IDSEL)
		begin
			PCI_AD_OUT <= confDatum;
			PCI_AD_OE <= 1;
		end
		else
			PCI_AD_OE <= 0;
	
	// DEVSEL handle logic
	
	reg PCI_nDEVSEL_OE = 0;
	reg DEVSEL;
	wire Targetted = beginTransaction
				&& PCI_IDSEL
				&& ((PCI_CBE == CBE_ConfRD)
					|| (PCI_CBE == CBE_ConfWR)) 
				&& (PCI_AD[1:0] == 2'b00);
	
	assign PCI_nDEVSEL = PCI_nDEVSEL_OE ? ~DEVSEL : 1'bZ;
	assign PCI_nTRDY = PCI_nDEVSEL_OE ? ~DEVSEL : 1'bZ;
	
	always @(posedge PCI_CLK or negedge PCI_nRST)
		if (~PCI_nRST)
			DEVSEL <= 0;
		else
		begin
			if (inTransaction)			// if we're in a transaction,
				DEVSEL <= DEVSEL & ~finalDataTransfer;	// keep asserting DEVSEL until the final data transfer.
			else					// otherwise,
				DEVSEL <= Targetted;			// wait til we're targetted to assert DEVSEL.
		end
	
endmodule
	