kclkgen.v 3.8 KB
// kclkgen.v v1 Frank Berndt
// pll clock generator module for r4300;
// :set tabstop=4

`timescale 1ps/1ps

module kclkgen (
	ksyncout, ktclock, csclk4xb, cscd2xb, cscd1xb, cspll1xb, kcoldreset,
	pbypasspllb, pdivmode, pmripll1x, pllcap0, pllcap1, padcoldresetb, crrp2, psyncin,
	pmasterclock, phi1, phi2, sclk, sclkb
);
	input sclkb;			// for synchronization, not used;
	input sclk;				// for synchronization, not used;
	input psyncin;			// for synchronization, not used;
	input pmripll1x;		// for synchronization, not used;
	input pmasterclock;		// master clock;
	inout pllcap0;			// analog pll filter, not used;
	inout pllcap1;			// analog pll filter, not used;
	inout phi2;				// for synchronization, not used;
	inout phi1;				// for synchronization, not used;
	input [2:0] pdivmode;	// divide mode;
	input padcoldresetb;	// cold reset input;
	input crrp2;			// rp mode is not implemented;
	input pbypasspllb;		// pll bypass, not used;
	output ktclock;
	output ksyncout;
	output kcoldreset;
	output cspll1xb;
	output csclk4xb;
	output cscd2xb;
	output cscd1xb;

	// measure time between rising edges of master clock;
	// require 8 master clocks before starting pll lock;

	reg [3:0] nmclock;
	wire lock;					// start locking pll;
	reg locked;					// pll is locked;
	integer t0, t1;
	integer mclk_period;		// masterclock period;
	integer pclk_period;		// pipeline clock period;

	initial
	begin
		nmclock = 0;
		locked = 0;
		t0 = -1;
		mclk_period = 10000;
		pclk_period = 10000;
	end

	assign lock = nmclock[3];

	always @(posedge pmasterclock)
	begin
		if( !lock) begin
			t1 = $time;
			mclk_period = t1 - t0;
			t0 = t1;
			nmclock = nmclock + 1;
		end
	end

	// restart pll locking on assertion of cold reset;

	always @(negedge padcoldresetb)
	begin
		nmclock = 4'd0;
		locked = 0;
	end

	// monitor change in master clock;

	always @(mclk_period)
	begin
		if(lock)
			$display("ERROR: %t: %M: masterclock changed after pll started locking", $time);
	end

	// monitor change of divmode;

	always @(pdivmode)
	begin
		if(lock)
			$display("ERROR: %t: %M: divmode changed after pll started locking", $time);
	end

	// drive x1 clock;

	reg cscd1xb;

	initial
		cscd1xb = 0;
	always
		#(mclk_period / 2) cscd1xb = ~cscd1xb;

	// create pipeline clock;

	reg pclock;				// pipeline clock;
	integer mult, div;		// clock ratio;
	integer pclk_check;		// for fraction check;
	integer pclk_rand;		// random pclock before lock;

	initial
	begin
		pclock = 1;
	end

	always @(mclk_period or pdivmode or lock)
	begin
		case(pdivmode)
			3'b000: begin mult = 1; div = 1; end	// x1
			3'b001: begin mult = 1; div = 2; end	// x2
			3'b010: begin mult = 2; div = 5; end	// x2.5
			3'b011: begin mult = 1; div = 4; end	// x4
			3'b100: begin mult = 1; div = 4; end	// x4
			3'b101: begin mult = 2; div = 3; end	// x1.5
			3'b110: begin mult = 1; div = 2; end	// x2
			3'b111: begin mult = 1; div = 3; end	// x3
		endcase
		pclk_period = (mclk_period * mult) / div;
		pclk_check = (pclk_period * div) / mult;
		if(lock & (mclk_period != pclk_check)) begin
			$display("ERROR: %t: %M: fraction in clock ratios, mclk %0dps, pclk %0dps",
				$time, mclk_period, pclk_period);
			$finish;
		end
	end

	// produce random pclock until locked;
	// limit frequency range between 5ns...5ns+2^15;
	// align rising edges of pmasterclock and pclock;

	always
	begin
		if(locked)
			#(pclk_period / 2);
		else if(lock) begin
			$display("%t: %M: mclk %0dps, pclk %0dps", $time, mclk_period, pclk_period);
			@(posedge pmasterclock);
			locked = 1;
			pclock = 1;
			#(pclk_period / 2);
		end else begin
			pclk_rand = 5000 + ($random & 16'h7fff);
			#(pclk_rand);
		end
		pclock = ~pclock;
	end

	assign cscd2xb = pclock;

	// hard-wire fixed signals;

	assign kcoldreset = ~padcoldresetb;
	assign ksyncout = pmasterclock;
	assign ktclock = ~cscd1xb;
	assign cspll1xb = 0;
	assign csclk4xb = 0;

endmodule