// 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