UberDDR3/delete_later/rtl/cpu/slowmpy.v

272 lines
6.1 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: slowmpy.v
// {{{
// Project: 10Gb Ethernet switch
//
// Purpose: This is a signed (OPT_SIGNED=1) or unsigned (OPT_SIGNED=0)
// multiply designed for low logic and slow data signals. It
// takes one clock per bit plus two more to complete the multiply.
//
// The OPT_SIGNED version of this algorithm was found on Wikipedia at
// https://en.wikipedia.org/wiki/Binary_multiplier.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
// }}}
// http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module slowmpy #(
// {{{
parameter LGNA = 6,
parameter [LGNA:0] NA = 33,
parameter [0:0] OPT_SIGNED = 1'b1,
parameter [0:0] OPT_LOWPOWER = 1'b0,
localparam NB = NA // Must be = NA for OPT_SIGNED to work
// }}}
) (
// {{{
input wire i_clk, i_reset,
//
input wire i_stb,
input wire signed [(NA-1):0] i_a,
input wire signed [(NB-1):0] i_b,
// verilator coverage_off
input wire i_aux,
// verilator coverage_on
output reg o_busy, o_done,
output reg signed [(NA+NB-1):0] o_p,
// verilator coverage_off
output reg o_aux
// verilator coverage_on
// }}}
);
// Declarations
// {{{
reg [LGNA-1:0] count;
reg [NA-1:0] p_a;
reg [NB-1:0] p_b;
reg [NA+NB-1:0] partial;
// verilator coverage_off
reg aux;
// verilator coverage_on
reg almost_done;
wire pre_done;
wire [NA-1:0] pwire;
// }}}
assign pre_done = (count == 0);
// almost_done
// {{{
initial almost_done = 1'b0;
always @(posedge i_clk)
almost_done <= (!i_reset)&&(o_busy)&&(pre_done);
// }}}
// aux, o_done, o_busy
// {{{
initial aux = 0;
initial o_done = 0;
initial o_busy = 0;
always @(posedge i_clk)
if (i_reset)
begin
// {{{
aux <= 0;
o_done <= 0;
o_busy <= 0;
// }}}
end else if (!o_busy)
begin
// {{{
o_done <= 0;
o_busy <= i_stb;
aux <= (!OPT_LOWPOWER || i_stb) ? i_aux : 0;
// }}}
end else if (almost_done)
begin
// {{{
o_done <= 1;
o_busy <= 0;
// }}}
end else
o_done <= 0;
// }}}
assign pwire = (p_b[0] ? p_a : 0);
// count, partial, p_a, p_b
// {{{
always @(posedge i_clk)
if (!o_busy)
begin
count <= NA[LGNA-1:0]-1;
partial <= 0;
p_a <= i_a;
p_b <= i_b;
if (OPT_LOWPOWER && !i_stb)
begin
p_a <= 0;
p_b <= 0;
end
end else begin
p_b <= (p_b >> 1);
// partial[NA+NB-1:NB] <= partial[NA+NB
partial[NB-2:0] <= partial[NB-1:1];
if ((OPT_SIGNED)&&(pre_done))
partial[NA+NB-1:NB-1] <= { 1'b0, partial[NA+NB-1:NB]} +
{ 1'b0, pwire[NA-1], ~pwire[NA-2:0] };
else if (OPT_SIGNED)
partial[NA+NB-1:NB-1] <= {1'b0,partial[NA+NB-1:NB]} +
{ 1'b0, !pwire[NA-1], pwire[NA-2:0] };
else
partial[NA+NB-1:NB-1] <= {1'b0, partial[NA+NB-1:NB]}
+ ((p_b[0]) ? {1'b0,p_a} : 0);
count <= count - 1;
end
// }}}
// o_p, o_aux
// {{{
always @(posedge i_clk)
if (almost_done)
begin
if (OPT_SIGNED)
o_p <= partial[NA+NB-1:0]
+ { 1'b1, {(NA-2){1'b0}}, 1'b1, {(NB){1'b0}} };
else
o_p <= partial[NA+NB-1:0];
o_aux <= aux;
end
// }}}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
// Declarations and reset
// {{{
`define ASSERT assert
`ifdef SLOWMPY
`define ASSUME assume
`else
`define ASSUME assert
`endif
reg f_past_valid;
initial f_past_valid = 1'b0;
always @(posedge i_clk)
f_past_valid <= 1'b1;
initial assume(i_reset);
always @(*)
if (!f_past_valid)
`ASSUME(i_reset);
// }}}
always @(posedge i_clk)
if ((!f_past_valid)||($past(i_reset)))
begin
`ASSERT(almost_done == 0);
`ASSERT(o_done == 0);
`ASSERT(o_busy == 0);
`ASSERT(aux == 0);
end
// Assumptions about our inputs
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_stb))&&($past(o_busy)))
begin
`ASSUME(i_stb);
`ASSUME($stable(i_a));
`ASSUME($stable(i_b));
end
//
// For now, just formally verify our internal signaling
// {{{
always @(posedge i_clk)
`ASSERT(almost_done == (o_busy&&(&count)));
always @(*)
if (!(&count[LGNA-1:1])||(count[0]))
`ASSERT(!o_done);
always @(posedge i_clk)
if (o_done)
`ASSERT(!o_busy);
always @(posedge i_clk)
if (!o_busy)
`ASSERT(!almost_done);
reg [NA-1:0] f_a, f_b;
always @(posedge i_clk)
if ((i_stb)&&(!o_busy))
begin
f_a <= i_a;
f_b <= i_b;
end
always @(*)
if (o_done)
begin
if ((f_a == 0)||(f_b == 0))
begin
`ASSERT(o_p == 0);
end else
`ASSERT(o_p[NA+NB-1] == f_a[NA-1] ^ f_b[NA-1]);
end
// }}}
////////////////////////////////////////////////////////////////////////
//
// Cover properties
// {{{
////////////////////////////////////////////////////////////////////////
//
always @(posedge i_clk)
cover(o_done);
reg cvr_past_done;
initial cvr_past_done = 1'b0;
always @(posedge i_clk)
if (o_done)
cvr_past_done <= 1'b1;
always @(posedge i_clk)
cover((o_done)&&(cvr_past_done));
// }}}
`endif
// }}}
endmodule