// Constant Coefficient Multiplier (KCM)
// Larry Doolittle, LBNL
// $Id: kcm.v,v 1.3 2003/06/11 19:30:06 ldoolitt Exp $

// The constant can be reloaded on the fly,
// based on Xilinx's SRL16E primitive.
// It is therefore useful only on Xilinx Spartan-II and
// Virtex chips (October 2002 list).
//
// Unlike Xilinx's CoreGen DKCM, I have split out the loading
// logic, so that can be shared by a set of multipliers.
// My controller takes 24 clock cycles, not 16, to reload
// the constant.  I have only coded a single configuration:
//      12-bit signed constant
//      12-bit signed input
//      24-bit signed output
//      combinitorial (not pipelined) operation
// If anyone wants to parameterize it, start by turning my hard-coded
// srl16x16e into a parameterized module instance array, and adding
// support for that feature to Icarus Verilog.
//
// I estimate the per-multiplier LUT count at 82
//   (48 SRL16E, 32 adder, 2 address decoder)
// Xilinx Foundation 4.2i says it implements it in 44 slices (88 LUT).
// The controller (dkcm_controller) is designed as a peripheral to a
// general purpose computer.  If you wanted to shave the LUT count,
// you could perform this function in software instead, and tie the
// mass of SRL16E's directly to the processor data bus.  I do it
// in hardware for speed and implementation-detail-hiding; besides,
// it only costs about 40 LUT.

`timescale 1ns / 1ns

// module srl16x16e(Q, A0, A1, A2, A3, CE, CLK, D);
// see srl16x16e.v

module dkcm_bussed(var, product, clk, dkcm_bus, ident);
	input [11:0] var;
	input clk;
	input  [20:0] dkcm_bus;
	input   [2:0] ident;
	output [23:0] product;
	wire [11:0] var;
	wire [23:0] product;
	wire [20:0] dkcm_bus;
	wire [2:0] ident;

	wire [15:0] load_data;     assign load_data     = dkcm_bus[20:5];
	wire  [2:0] load_addr;     assign load_addr     = dkcm_bus[4:2];
	wire        load_unsigned; assign load_unsigned = dkcm_bus[1] & (load_addr == ident);
	wire        load_signed;   assign load_signed   = dkcm_bus[0] & (load_addr == ident);

	wire [15:0] pprod0, pprod1, pprod2;
	srl16x16e bank0(.Q(pprod0), .A0(var[0]), .A1(var[1]), .A2(var[2]),  .A3(var[3]),  .CE(load_unsigned), .CLK(clk), .D(load_data));
	srl16x16e bank1(.Q(pprod1), .A0(var[4]), .A1(var[5]), .A2(var[6]),  .A3(var[7]),  .CE(load_unsigned), .CLK(clk), .D(load_data));
	srl16x16e bank2(.Q(pprod2), .A0(var[8]), .A1(var[9]), .A2(var[10]), .A3(var[11]), .CE(load_signed),   .CLK(clk), .D(load_data));
	assign product =
		 {{8{pprod0[15]}}, pprod0} +
		 {{4{pprod1[15]}}, pprod1, 4'b0000} +
		 {                 pprod2, 8'b00000000};
	always @(negedge clk) if (0) begin
	 	#1;
	 	$display("     %x*K +  %x*K<<4 +  %x*K<<8",var[3:0],var[7:4],var[11:8]);
	 	$display("    %x + %x<<4 + %x<<8 = %d",pprod0,pprod1,pprod2,product);
	end
endmodule

module dkcm_controller(clk, dkcm_bus, konstant, load, addr, busy);
	input clk, load;
	output busy;
	input [11:0] konstant;
	input [2:0] addr;
	output [20:0] dkcm_bus;
	reg [15:0] acc;
	reg [1:0] sel;
	reg [4:0] state;
`ifdef SIMULATE
	initial state = 0;
`endif
	assign busy = state != 5'b00000;
	always @(posedge clk)
		if (busy | load) state <= (state == 5'b11000)?0:(state+1'b1);
	always @(posedge clk)
		sel <= (busy | load) ? ( state[4:3] + 1'b1 ) : 0;
	always @(posedge clk)
		acc <= ((state==8) ? {konstant, 4'b0000} :
			( (state==0) ? 0 : acc ) ) - {{4{konstant[11]}},konstant};
	always @(negedge clk) if (busy)
		$display("reloading: %d %x %d", state, acc, sel);
	assign dkcm_bus = {acc, addr, sel};
endmodule