//
//								mux16x8s.v - 16 BCH CODECs
//
//						(C) Copyright 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 1-16 BCH encoder/decoders as an I/O device. 1-16 bit data is
// written to port 0, causing bits to be added to 1-16 BCH parity/syndrome registers.
// The output may read srially or in parallel after all data bits have been written.
// This module is suitable for codes from BCH(7,4) to BCH(255,239). As N-K grows
// external syndrome lookup will require larger amounts of external memory.
//
// There are 4 output ports for data, tap configuration, shift register length (n-k)
// and the accumulate/initialize control:
//
//     15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |                              Data                             | Data
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 |   |  Length   |               |G7 |G6 |G5 |G4 |G3 |G2 |G1 |   | Config.
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  2 |                                               |     Width     | Config.
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  3 |                                                           |ACC| Control
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//		Length select the last tap in the encoder/decoder (N-K-1)
//		Width select the word size (number of parallel BCH computations)
//		XMT selects code word accumulation (1) or syndrome accumulation (0)
//		RST resets the code/syndrome register.
//
// There are 2 input ports to read accumulated paity bits the or syndrome bits:
//
//     15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |                             Parity                            | Data
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 | 0   0   0   0   0   0   0   0 |           Syndrome            | Status
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// Parity bits are accumulated when ACC = 1 and read out when ACC = 0. Write
// uncoded data to port 0 to calculate parity and write 0 to port 0 to read
// parity words. XMT must be 1 and the value of width register must not change
// during these opererations. The parity bits are set to zero when all are read.
//
// Syndrome bytes are calculated when ACC = 1 and XMT = 0. Write uncoded data
// and parity bits to port 0 with width set to the word length. The syndromes
// may then be read by loading the width register with the bit to be corrected
// and then reading port 1.
//
// The syndrome/parity register may be reset by setting ACC to 0 and writing
// length+1 words with a value of zero to port 0.
//
// 47 slices used. 126 MHz max.
//
// Normal Warnings:
//		Input <ioaddr<2>> is never used.
//
module bch16x8s(iocs, ioaddr, din, dout, iord, iowr, clk, rst, xmt);
	input iocs;				// select this module
	input [2:0] ioaddr;	// address registers in this module
	input [15:0] din;		// uncoded data and configuration input
	output [15:0] dout;	// encoded PSK output
	input iord;				// read register
	input iowr;				// write register
	input clk;				// master clock
	input rst;				// reset
	input xmt;				// 0=rcv, 1=xmt
// internal signals
wire w0,w1,w2,w3;			// write enables
reg acc;						// accumulate parity or syndrome
wire cc;						// last bit in parity shift register
wire fb;						// feedback bit
reg [15:0] d,omux;		// data I/O shift register and output multiplexer
wire [7:0] ci,co;			// accumulator for coded bits
wire ce;						// enable writing bits into accumulator
reg [7:1] g;				// feedback enable
reg [2:0] length;			// length of shift register
reg [3:0] width;			// number of bits per word
reg [3:0] b;				// bit counter
wire nz;						// counter not zero
reg nzd;						// delayed not zero
// decode addresses
assign w0 = iocs & iowr & (ioaddr[1:0] == 0);	// data
assign w1 = iocs & iowr & (ioaddr[1:0] == 1);	// length and tap selection
assign w2 = iocs & iowr & (ioaddr[1:0] == 2);	// width
assign w3 = iocs & iowr & (ioaddr[1:0] == 3);	// control
// configuration registers and bit counter
always @ (posedge clk)
begin
	if (rst) g <= 0;				// configuration register
	else if (w1) g <= din[7:1];
	if (rst) length <= 0;		// length register
	else if (w1) length <= din[14:12];
	if (rst) width <= 0;			// word width register and syndrome select
	else if (w2) width <= din[3:0];
	if (rst) acc <= 0;
	else if (w3) acc <= din[0];
	if (rst) b <= 0;				// default to 16-bit words
	else if (w0) b <= width;	// initialize bit counter to word width
	else if (nz) b <= b-1;		// and then count down to zero
	if (rst) nzd <= 0;
	else nzd <= nz|w0;
end
assign nz = |b;		// bit counter not zero
assign ce = nz|nzd;	// enable parity/syndrome shift register
// setect last bit in parity/syndrome register
MUX8 mux (
	.D(co),
	.S(length),
	.Y(cc)
	);
// compute feedback bit when accumulating parity or syndrome
assign fb = acc & (cc ^ (d[0] & xmt));
// Calculate parity bits to store in shift register.
assign ci[0] = (d[0] & ~xmt) ^ fb;
assign ci[1] = co[0] ^ (g[1] & fb);
assign ci[2] = co[1] ^ (g[2] & fb);
assign ci[3] = co[2] ^ (g[3] & fb);
assign ci[4] = co[3] ^ (g[4] & fb);
assign ci[5] = co[4] ^ (g[5] & fb);
assign ci[6] = co[5] ^ (g[6] & fb);
assign ci[7] = co[6] ^ (g[7] & fb);
// Store encoder state (C) in shift register.
srl16x8e sr (
	.d(ci),
	.a(width),
	.y(co),
	.ce(ce),
	.clk(clk)
	);
// data I/O shift register and I/O multiplexer
// read port 0 for parity words as shifted out of parity register
// read port 1 for syndrome byte selected by width parameter
always @ (posedge clk)
begin
	if (w0) d <= din;						// parallel load
	else if (ce) d <= {cc,d[15:1]};	// shift MSB->LSB
	if (iord) omux <= ioaddr[0] ? {8'h00,co} : d;
end
// connect output
assign dout = omux;
endmodule
