272 lines
6.1 KiB
Verilog
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
|