//
//					  PC14.v - 14-bit Program Flow Controller
//
//					(C) Copyright 2007-2008 John B. Stephensen
//
// This Verilog source file and all its derivatives are licensed only for
// personal non-profit educational use in the Amateur Radio Service and
// the license is not transferrable. The information is provided as-is for
// experimental purposes and the author does not warranty its freedom
// from defects or its suitability for any specific application.
//
// This unit consists of a program counter (PC), return address stack, stack pointer (SP)
// and associated logic used to control program flow in a microcontroller. It recognizes
// the jump, call, return and repeat instructions. Instruction formats are:
//
//		 15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   0 |                   Absolute Address                    | Call
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 0   0 | Flag  | C |         Relative Address          | Jump
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 0   1 | Flag  | C | 0 |                               | Return
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 0   1 | Flag  | C | 1 |                               | Loop
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 1   0   0 | 0 |                                       | Mark
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 1   0   0 | 1 |                       |       A       | Set return addr. 
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//		| 0   1 | 1   0   1 |           |             Count             | Repeat
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//		C = flag condition (0=true, 1=false)
//
// Since the instruction ROM has a 1 clock delay, the PC always contains the address of
// the next instruction in sequence. This is used to advantage when storing the return
// address. The PC is bypassed on jumps, calls and returns to eliminate this delay and
// the PC is loaded with the address of the instruction following the return.
//
//	120 MHz maximum clock speed and 72 slices used. Max. delay is condition code multiplexer
// to program counter increment logic.
//
// History:
//		M001	5-31-08	conditional call and return
//		M002	8-5-08	16-bit format - add mark and loop, rel. jump only, remove cond. call
//		M003	8-26-08	modify encoding to allow future use of 3-bit condition code
//		M004	8-29-08	allow 9-bit relative address
//		M005	2-25-09	add suport for 2-cycle instructions
//		M006	3-26-09	make program memory external
//		M007	7-14-09	add STRA instruction
//
module PC14(paddr, pdata, rdata, flags, ce, clk, rst);
	 output [13:0] paddr;	// program address
	 input [15:0] pdata;		// program data
	 input [13:0] rdata;		// register A data
	 input [3:0] flags;		// flag input
	 input ce;					// PC count enable 
	 input clk;					// master clock
	 input rst;					// reset PC and SP
// internal signals
reg [13:0] pc,ra;							// program counter and return address registers
wire [13:0] pcin;							// program counter input
wire [1:0] sel;							// select address input
wire inc;									// increment program counter
reg [3:0] sp;								// stack pointer
wire push,pop;								// push or pop stack (increment or decrement)
reg push1;									// delayed write strobe for stack
wire call,jmp,ret,mark,rpt,stra;		// PC control instructions
wire [13:0] addr,absaddr,retaddr;	// absolute addresses
wire [8:0] reladdr;						// relative address
wire [1:0] flag;							// selects flag to test
wire cond;									// flag condition
wire fv,jmpv,retv;						// selected condition true
reg [7:0] count;							// repeat counter
wire zero;									// counter is zero
wire loop;									// pop stack if return condition false (loop)
// instuction fields
assign call = (pdata[15:14] == 2'b00);		// call instruction decoded
assign jmp = (pdata[15:12] == 4'b0100);	// jump instruction group
assign ret = (pdata[15:12] == 4'b0101);	// return instruction decoded
assign mark = (pdata[15:11] == 5'b01100);	// mark stack instruction decoded
assign rpt = (pdata[15:11] == 5'b01101);	// repeat instruction decoded
assign cond = pdata[9];							// flag condition
assign flag = pdata[11:10];					// flag select (jump/return condition)
assign reladdr = pdata[8:0];					// jump address
assign loop = pdata[8];							// reutrn used in loop
assign absaddr = pdata[13:0];					// call address
// condition code multiplexer
MUX4I ccmux (
	.D(flags),	// status flags
	.S(flag),	// select flag to test
	.I(cond),	// select flag condition
	.Y(fv)		// condition true?
	);
assign retv = ret & fv;
assign jmpv = jmp & fv;
// select source of next address:
// 0 = PC, 1 = relative address, 2 = absolute address, 3 = address from stack
assign sel[0] = jmpv | retv;
assign sel[1] = call | retv;
// calculate absolute address from relative address
assign addr = pc + {reladdr[8],reladdr[8],reladdr[8],reladdr[8],reladdr[8],reladdr};
// program counter - increment, except when program flow instruction decoded
// select jump/call address or return address
MUX4X14 pcimux (
	.D0(pc),			// increment
	.D1(addr),		// jump
	.D2(absaddr),	// call
	.D3(retaddr),	// return
	.S(sel),
	.Y(pcin)
	);
// PC is always current address plus one
always @ (posedge clk)
begin
	if (rst) pc <= 14'b00000000000000;					// reset PC
	else if (inc) pc <= pcin + 14'b00000000000001;	// increment PC
end
assign inc = ce & zero & ~rpt;	// count up when repeat instruction inactive and not memory read
// select PC as program address unless jump/call/return valid
MUX4X14 pcomux (
	.D0(pc),			// increment
	.D1(addr),		// jump
	.D2(absaddr),	// call
	.D3(retaddr),	// return
	.S(sel),
	.Y(paddr)
	);
// stack pointer - increment on call and decrement on return
assign stra = mark & pdata[10];	// decode set return address instruction (M009)
assign push = call|mark;			// push during call, mark or set return address instructions
assign pop = ret & (fv ^ loop);	// pop on successful return or loop instructions
always @ (posedge clk)
begin
	if (rst) sp <= 4'b0000;		// for simulation
	else sp <= sp + {pop,pop,pop,pop|push};
	push1 <= push;					// delayed stack write enable
//	ra <= pc;						// delay data from PC for stack push
	ra <= stra ? rdata : pc;	// delay data from register or PC for stack push (M009)
end
// calling address stack
// pre-increment on write and post-decrement on read
RAM16X14S stack (
	.A(sp),
	.D(ra),		// RA used to delay PC+1 until write
	.O(retaddr),
	.WE(push1),
	.WCLK(clk)
	);
// Repeat counter - inhibits incrementing of program counter
always @ (posedge clk)
begin
	if (rst) count <= 0;						// reset on power-up
	else if (rpt) count <= reladdr;		// load on repeat instruction
	else if (~zero) count <= count - 1;	// count down when not zero
end
assign zero = ~|count;	// detect all zeros
endmodule
