//
//			modem16.v - Modulator and Demodulator with 16x16-bit I/O	
//
//					(C) Copyright 2004-2010 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 module provides modulation and demodulation for single-carrier applications.
// 
// When receiving, the received signal is present in rectangular form with the in-phase
// component on RDIX and the quadrature component on RDIY. Both inputs are 18-bit signed
// fractions. 
//
// The received signal is then processed by the CORDIC module over the next 18 clock
// cycles. For AM and PM the module operates in vector mode and calculates the 16-bit
// phase and the 16-bit magnitude of the signal. For FM, the phase can then be
// differentiated over 1-16 samples. For SSB, CORDIC operates in the rotate mode and
// rotates the incoming signal at the BFO frequency performing a complex multiplication.
// A BFO frequency of 0 allows pass-through of the X and Y inputs for ISB or QAM
// demodulation by the CPU.
//
// The clock recovery module may be used to perform early-late timing recovery with 3
// samples per symbol. The middle sample is saved as data and the difference between the
// other two samples measures the timing error.
//
// The synchronization detection module processes the CORDIC output to detect OFDM null
// symbols and symbol timing errors. Nulls are detected by averaging the signal magnitude
// over a symbol period and detecting the minimum. Timing errors are detected by comparing
// the phase of the received signal between points one FFT interval apart and performing a
// moving average over the interval of the cyclic prefix. The error signal is minimized
// when the end of the symbol is reached. 
//
// The CORDIC module is used for all modulation tasks. For AM and PM, it is operated
// in the rotation mode and the X input is rotated by Y scaled radians so the X  input
// specifies the signal magnitude and Y the phase. For FM, the Y input is accumulated so
// it specifies the frequency. For SSB, the phase input is obtained from the BFO phase
// accumulator. A BFO frequency of 0 allows pass-through of the X and Y inputs for ISB
// or QAM. The modulator output is 16-bits wide.
//
// The modem module is configured and data tranmitted via 7 output ports:
//
//		 15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |                               X                               | I or magnitude
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 |                               Y                               | Q or phase
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//	 2 |                               |  FM Delay - 1 |   |TRE|SSB|FM | Modem Config.
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//	 3 |                                                               | (unused)
//		+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  4 |     CP Length - 1     |                FFT Size               | Phase Correlator
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  5 |       SOF Delay       |   |           Symbol Length           | Magnitude Filter
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  6 |                         BFO Frequency                         |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  7 |                                                   |INI|RST|XMT|
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//		FM: 0 = PM, 1 = FM (SSB must be 0)
//		SSB: put CORDIC modulator in rotate mode and enable BFO
//		TRE: enable clock recovery
//		RST: Reset FIFO (flush contents)
//		INI: Initialize sync detect logic for new symbol
//
// There are 4 input ports to read data and status:
//
//	    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |                               X                               | I or M
//	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 |                               Y                               | Q or P
//	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  2 |                   Avgerage Magnitude (RSSI)                   |
//	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  3 | 0   0   0   0   0   0   0   0   0   0   0   0   0   0 | E | F | Status
//	   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//		OVF - modem filter overflow
//		E - FIFO empty
//		F - FIFO full
//
// The input to the modem FIFO and the output from the modem FIFO are formatted as follows:
//
//                    X                               Y
//     1 1 1 1 1 1                     1 1 1 1 1 1     
//		 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |               I               |               Q               | SSB
//		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |           Magnitude           |             Phase             | FM/PM/AM & FSK/PSK xmt
//		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |       0       |      Data     |    Timing 1   |   Timing 2    | FSK/PSK rcv (TRE=1)
//		+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 
// 581 slices (12% XC3S500E) plus 2 multipliers and 1 block RAM are used. The maximum clock speed
// is 97 MHz.
// 
// Normal Warnings:
//		Signals <xc<35:33>>, <xc<16:0>>, <yc<35:33>> and <yc<16:0>> are assigned but never used.
//		Signal <z0<14>> is assigned but never used.
//		Signal <dlyphs<1:0>> is assigned but never used. (nulsyndet)
//
// History:
//		M001	6-11-08	added clipper, resampler and streamlined configuration interface
//		M002	1-3-09	modified interface on tuner side for 18 bits and removed gain control
//		M003	1-26-09	added FIFO and clock recovery
//		M004	2-6-09	placed I/O port logic inside module
//		M005	3-3-09	latch status multiplexer output for 2-cycle input instruction
//		M006	4-6-09	new CORDIC module with vector and rotate modes for modulation and demodulation
//		M007	4-10-09	added logic for null symbol and cyclic prefix detection
//		M008	4-15-09	added BFO for LSB and USB transmit/receive
//		M009	6-8-09	put in new compressor and moved FFT switch after CORDIC
//		M010	12-22-09	new null and synchronization detector logic
//		M011	1-10-10	allow resampling on both receive and transmit paths
//		M012	2-1-10	route FM discriminator to test signal y
//		M013	3-3-10	nulsyndet modified
//		M014	3-11-10	add DCD output
//		M015	5-3-10	use compressor with 8-bit magnitude detector (was 920 slices 97 MHz)
//		M016	5-12-10	move resampler and compressor to new module (was 926 slices 95 MHz)
//
module modem16 (
			iocs,ioaddr,din,dout,iowr,iord,
			rdix, rdiy, riv, rif, toe, tdox, tdoy, tov,
			x,y,xyv,m,p,mpv,//minm,maxm,minp,
			start, sync, dcd, clk, rst, xmt
			);
	input iocs;					// select this module
	input [2:0] ioaddr;		// I/O port address
	input [15:0] din;			// data input bus
	output [15:0] dout;		// data output bus
	input iowr;					// write to I/O port
	input iord;					// read from I/O port
	input [15:0] rdix,rdiy;	// receiver input
	input riv;					// valid input
	input rif;					// final input in sequence
	input toe;					// enable transmitter output
	output [15:0] tdox,tdoy; // transmitter data output
	output tov;					// valid output
	output [15:0] x,y;		// test signals
	output xyv;
	output [15:0] m;
	output [11:0] p;
	output mpv;
//	output [15:0] minm,maxm;
//	output [11:0] minp;
	output start,sync,dcd;	// FFT frame and symbol synchronization
	input clk;					// sample clock
	input rst;					// master reset
	output xmt;					// 0=receive, 1=transmit
// Clock recovery signals
reg [19:1] f;					// delayed final sample flag
wire [7:0] data,slope1,slope2;		// recovered data sample and timing error
wire dtov;						// output valid
// FIFO input and output signals			
wire [1:0] fsel;				// select 1 of 4 inputs
reg [15:0] fxin,fyin;		// FIFO input
reg fvin;
wire [15:0] fxout,fyout;	// FIFO output
wire fvout;
wire rstfifo;					// reset FIFO (power on or programmed)
// CORDIC input and output signals
wire [1:0] csel;				// select 1 of 4 inputs
reg [15:0] cxin,cyin;		// CORDIC input
reg [15:0] czin;
reg cvin,rotate;
wire [15:0] cxout,cyout,czout;	// CORDIC outputs
wire cvout;
// BFO
reg [15:0] bfofrq,bfophs;	// BFO for SSB mod/demod
// sync and null detect logic
wire [15:0] rssi;				// signal strength output
wire [11:0] phscor;			// phase correlation output
wire rssiv;						// above signals valid
wire sync,start;				// FFT synchronization outputs
wire rstdet;					// initialize sync/null detector
// FM/PM conversion
reg [15:0] xin,yin,frqout;
wire [15:0] dlyphs;
reg vin,fmov;
// configuration data
reg xmt;					// T/R switch
reg fm,ssb,tre;		// select FM/PM, SSB and timing recovery
reg [3:0] dly;			// FM detector phase difference
// decode address
wire w0,w1,w2,w4,w5,w6,w7,r1;	// port selects
assign w0 = iocs & (ioaddr == 0) & iowr;	// write X data (X or M)
assign w1 = iocs & (ioaddr == 1) & iowr;	// write Y data (Q or P)
assign w2 = iocs & (ioaddr == 2) & iowr;	// select mode
assign w4 = iocs & (ioaddr == 4) & iowr;	// phase sync.
assign w5 = iocs & (ioaddr == 5) & iowr;	// null det.
assign w6 = iocs & (ioaddr == 6) & iowr;	// clock divider
assign w7 = iocs & (ioaddr == 7) & iowr;	// T/R switch
assign r1 = iocs & (ioaddr == 1) & iord;	// read data
//configuration registers
always @ (posedge clk)
begin
	if (rst) tre <= 0;					// no timing recovery by default
	else if (w2) tre <= din[8];
	if (rst) dly <= 0;					// default FM rcv delay is 1 sample
	else if (w2) dly <= din[7:4];
	if (rst) ssb <= 0;					// AM/PM or AM/FM by default
	else if (w2) ssb <= din[1];
	if (rst) fm <= 0;						// AM/PM by default
	else if (w2) fm <= din[0];
	if (rst) bfofrq <= 0;				// default to DC
	else if (w6) bfofrq <= din[15:0];
	if (rst) xmt <= 0;					// default to receive
	else if (w7) xmt <= din[0];
end
// FIFO reset command
assign rstfifo = rst | (w7 & din[1]);
assign rstdet = w7 & din[2];
// phase accumulator for FSK and FM modulation
// pass-through for SSB, ISB, PSK and PM modulation
always @ (posedge clk)
begin
	if (w0) xin <= din;	// delay X to match Y
	if (rst) yin <= 0;	// for simulation
	else if (w1) yin <= din + (fm ? yin : 0);
	vin <= w1;				// delay data strobe
end
// connect demodulator, clock recovery, receiver input or transmitter input to FIFO
// XMT SSB TRE Port
//  0   1   x   0
//  0   0   0   1
//  0   0   1   2
//  1   x   x   3
assign fsel = ({~ssb,~ssb} & {tre,~tre}) | {xmt,xmt};
always @ (posedge clk)
begin
	case (fsel)
	0: fxin <= cxout;				// demodulator X output (SSB/ISB)
	1: fxin <= cxout;				// demodulator magnitude output (AM)
	2: fxin <= {8'h00,data};	// recovered data
	3: fxin <= xin;				// transmit X input
	default: fxin <= 16'hxxxx;
	endcase
	case (fsel)
	0: fyin <= cyout;				// demodulator Y output (SSB/ISB)
	1: fyin <= frqout;			// demodulator phase or frequency output (PM/FM)
	2: fyin <= {slope1,slope2};// recovered timing error (PSK/FSK)
	3: fyin <= yin;				// transmit Y input
	default: fyin <= 16'hxxxx;
	endcase
	case (fsel)
	0: fvin <= cvout;				// demodulator output valid
	1: fvin <= fmov;				// demodulator frequency output valid
	2: fvin <= dtov;				// recovered data and timing valid
	3: fvin <= vin;				// X and Y inputs valid
	default: fvin <= 1'bx;
	endcase
end
// 15 entry x 32 bit FIFO
// receives 32-bit data input on transmit and multiplexer output on receive
fifo16x32s fifo (
//	.pdi({rdix[17:2],rdiy[17:2]}),	// *** TEST ***
//	.iv(riv),				// receiver input valid
	.pdi({fxin,fyin}),	// FIFO
	.iv(fvin),				// FIFO output valid
	.oe(xmt ? toe : r1),	// enable output (r1 = read Y)
	.pdo({fxout,fyout}),
	.ov(fvout),				// output valid (gated with OE)
	.empty(empty),
	.full(full),
	.clk(clk),
	.rst(rstfifo)			// special reset line
	);
// increment BFO phase with every input
always @ (posedge clk)
begin
	if (~ssb) bfophs <= 0;
	else if (cvin) bfophs <= bfophs + bfofrq;
end
// CORDIC input multiplexer
// XMT SSB Port
//  0   X  0/1
//  1   1   2
//  1   0   3
assign csel = {xmt,~ssb};
always @ (posedge clk)
begin
	case (csel)
	0: cxin <= rdix;		// receiver input
	1: cxin <= rdix;
	2: cxin <= fxout;		// SSB/ISB transmit
	3: cxin <= fxout;		// AM/PM/FM transmit amplitude
	default: cxin <= 16'hxxxx;
	endcase
	case (csel)
	0: cyin <= rdiy;		// receiver input
	1: cyin <= rdiy;
	2: cyin <= fyout;		// SSB/ISB transmit
	3: cyin <= 16'h0000;	// unused for AM/PM/FM transmit
	default: cyin <= 16'hxxxx;
	endcase
	case (csel)
	0: czin <= bfophs;	// receiver BFO or 0 (SSB bit selects BFO)
	1: czin <= bfophs;
	2: czin <= bfophs;	// SSB/ISB transmit
	3: czin <= fyout;		// AM/PM/FM transmit phase
	default: czin <= 16'hxxxx;
	endcase
	case (csel)
	0: cvin <= riv;		// receiver input valid
	1: cvin <= riv;
	2: cvin <= fvout;		// FIFO output valid for SSB/ISB transmit
	3: cvin <= fvout;		// FIFO output valid for AM/PM/FM transmit
	default: cvin <= 1'bx;
	endcase
	//	0: vector mode for AM/PM/FM receive or rotate mode for SSB receive
	//	1: vector mode for AM/PM/FM receive or rotate mode for SSB receive
	//	2: rotate mode for AM/FM/PM transmit
	//	3: rotate mode for SSB transmit
	rotate <= xmt | ssb;
end
// CORDIC engine to convert between 18x18 rectangular and 16x16 polar signals
cordic16 prc (
//	.xi({cxin,2'b00}),	// 16->18-bit inputs
//	.yi({cyin,2'b00}),
	.xi(cxin),	// 16-bit inputs
	.yi(cyin),
	.zi(czin),
	.mi(rotate),	// 0 = calculate arctangent, 1 = rotate X and Y inputs
	.load(cvin),
	.xo(cxout),		// 16-bit outputs
	.yo(cyout),
	.zo(czout),
	.mo(),			// unused
	.rdy(cvout),
	.clk(clk),		// common clock and reset
	.rst(rst)
	);
// filter phase for sync. detect and filter magnitude for RSSI
nulsyndet nsd (
	.mag(cxout),	// magnitude and phase inputs
	.phs(czout[15:8]),
	.iv(cvout),
	.init(rstdet),	// initialize min/max and reset flags
	.rssi(rssi),	// average magnitude and phase difference outputs
	.phscor(phscor),
	.ov(rssiv),
//	.minm(minm),	// test signals
//	.maxm(maxm),
//	.minp(minp),
	.start(start),	// null symbol and CP detect outputs
	.sync(sync),
	.dcd(dcd),
	.clk(clk),		// common clock and reset
	.rst(rst),
	.cin(din),		// configuration data input
	.cwe({w5,w4})	// write configuration data strobe
	);
// Phase memory for FM detection - 1-16 sample delay
srl16x16e phsdly (
	.d(czout),
	.a(dly),
	.y(dlyphs),
	.clk(clk),
	.ce(cvout)
	);
// calculate phase difference - 1 clock delay
always @ (posedge clk)
begin
	f <= {f[18:1],rif};	// delay final resampler output flag
	if (cvout) frqout <= czout - (fm ? dlyphs[15:0] : 16'h0000);
	fmov <= cvout;			// differentiator delay
end
// calculate receiver timing error for resampler adjustment
clkrec rxclk (
	.din(frqout[15:8]),	// upper 8 bits of phase or frequency
	.iv(fmov),
	.fv(f[19]),				// 19 clock delay in multiplexer and CORDIC
	.dout(data),			// 8-bit sample at nominal data sampling time
	.tout1(slope1),		// difference between early and nominal samples
	.tout2(slope2),		// difference between nominal and late samples
	.ov(dtov),				// output valid
	.clk(clk),
	.rst(rst)
	);
// collect status information via synchronous multiplexer
MUX4X16S din7mux (
	.D0(fxout),	// modem FIFO LSW
	.D1(fyout),	// modem FIFO MSW
	.D2(rssi),	// received signal strength indication
	.D3({14'b00000000000000,empty,full}),	// flags
	.S(ioaddr[1:0]),
	.Q(dout),
	.CE(iord),
	.CLK(clk),
	.RST(rst)
	);
// connect outputs
assign tdox = cxout;
assign tdoy = cyout;
assign tov = cvout;
// connect test signals
assign x = cxout;
assign y = frqout;	// M012
assign xyv = fmov;	// M012
assign m = rssi;
assign p = phscor;
assign mpv = rssiv;
endmodule
