1038 lines
25 KiB
Python
1038 lines
25 KiB
Python
"""
|
|
Generate VHDL or Verilog code for a signed multiplier.
|
|
|
|
Usage: genmul.py --lang=vhdl|verilog [--nolib] Xbits Ybits npipe
|
|
Usage: genmul.py --lang=vhdl|verilog --lib
|
|
|
|
--lang=... Specify VHDL or Verilog code
|
|
--nolib Do not generate library components
|
|
--lib Generate only library components
|
|
Xbits Length of input word in bits (Xbits >= 4)
|
|
Ybits Length of input word in bits (Ybits >= Xbits)
|
|
npipe Number of register stages (0 or 1 or 2)
|
|
|
|
See also:
|
|
G. Knagge, "ASIC Design for Signal Processing",
|
|
http://www.geoffknagge.com/fyp/booth.shtml, 2010.
|
|
L. Dadda, "Some schemes for parallel multipliers",
|
|
Associazione Elettrotecnica et Elettronica Italiana, 1965.
|
|
R. P. Brent, H. T. Kung, "A Regular Layout for Parallel Adders",
|
|
IEEE Transactions on Computers, 1982.
|
|
"""
|
|
|
|
#
|
|
# Copyright 2016 Joris van Rantwijk
|
|
#
|
|
# Copying and distribution of this file, with or without modification,
|
|
# are permitted in any medium without royalty provided the copyright
|
|
# notice and this notice are preserved.
|
|
#
|
|
|
|
|
|
import sys
|
|
import argparse
|
|
|
|
|
|
class Expr:
|
|
"""Represent a node in the expression tree."""
|
|
|
|
wire = None
|
|
done = False
|
|
|
|
|
|
class ConstBit(Expr):
|
|
"""Represent constant '0' or '1' bit."""
|
|
|
|
def __init__(self, v):
|
|
assert v in (0, 1)
|
|
self.v = v
|
|
|
|
|
|
class InBit(Expr):
|
|
"""Represent an input bit in the expression tree."""
|
|
|
|
def __init__(self, xy, p):
|
|
assert xy in ('x', 'y')
|
|
self.xy = xy
|
|
self.p = p
|
|
|
|
|
|
class Reg(Expr):
|
|
"""Represent flip-flop."""
|
|
|
|
def __init__(self, v):
|
|
self.v = v
|
|
|
|
|
|
class NotBit(Expr):
|
|
"""Represent inverter."""
|
|
|
|
def __init__(self, v):
|
|
self.v = v
|
|
|
|
|
|
class BoothNeg(Expr):
|
|
"""Represent calculation of radix-4 Booth sign-inversion flag."""
|
|
|
|
def __init__(self, pat):
|
|
assert len(pat) == 3
|
|
self.pat = pat
|
|
|
|
|
|
class BoothProd(Expr):
|
|
"""Represent calculation of partial product bit with radix-4 Booth."""
|
|
|
|
def __init__(self, pat, b):
|
|
assert len(pat) == 3
|
|
assert len(b) == 2
|
|
self.pat = pat
|
|
self.b = b
|
|
|
|
|
|
class AddBitD(Expr):
|
|
"""Represent selection of data bit from adder."""
|
|
|
|
def __init__(self, v):
|
|
self.v = v
|
|
|
|
|
|
class AddBitC(Expr):
|
|
"""Represent selection of carry bit from adder."""
|
|
|
|
def __init__(self, v):
|
|
self.v = v
|
|
|
|
|
|
class HalfAdd(Expr):
|
|
"""Represent half adder."""
|
|
|
|
def __init__(self, a, b):
|
|
self.a = a
|
|
self.b = b
|
|
|
|
|
|
class FullAdd(Expr):
|
|
"""Represent full adder."""
|
|
|
|
def __init__(self, a, b, c):
|
|
self.a = a
|
|
self.b = b
|
|
self.c = c
|
|
|
|
|
|
class CarryProp(Expr):
|
|
"""Represent base node of carry propagation tree."""
|
|
|
|
def __init__(self, a, b):
|
|
self.a = a
|
|
self.b = b
|
|
|
|
|
|
class CarryMerge(Expr):
|
|
"""Represent internal node of carry propagation tree."""
|
|
|
|
def __init__(self, p0, p1):
|
|
self.p0 = p0
|
|
self.p1 = p1
|
|
|
|
|
|
class CarryEval(Expr):
|
|
"""Represent logic to calculate carry-out."""
|
|
|
|
def __init__(self, p, c):
|
|
self.p = p
|
|
self.c = c
|
|
|
|
|
|
def gen_partial_products(xvec, yvec):
|
|
"""Generate list of partial products using radix-4 Booth algorithm.
|
|
|
|
Return [ (exponent, bit), ... ].
|
|
"""
|
|
|
|
partial_products = [ ]
|
|
|
|
# Append zero on LSB side of xvec, sign-extend on MSB side of xvec.
|
|
xtmp = [ ConstBit(0) ] + xvec + xvec[-1:]
|
|
|
|
# Append zero on LSB side of yvec, sign-extend on MSB side of yvec.
|
|
ytmp = [ ConstBit(0) ] + yvec + yvec[-1:]
|
|
|
|
# Step through xvec, 2 bits at a time.
|
|
for i in xrange(0, len(xvec), 2):
|
|
|
|
# Select group of 3 bits from xvec (one bit overlap with last group).
|
|
pat = xtmp[i:i+3]
|
|
|
|
# Add either 0, +1, +2, -1 or -2 times yvec according to Booth method.
|
|
# Step through the bits of yvec.
|
|
for j in xrange(len(yvec)+1):
|
|
|
|
# Use Booth encoder to choose between 0, yvec[j], yvec[j-1] or
|
|
# inverted bits yvec[j] or yvec[j-1].
|
|
t = BoothProd(pat, ytmp[j:j+2])
|
|
|
|
# Invert the MSB bit, except on first row.
|
|
if i > 0 and j == len(yvec):
|
|
t = NotBit(t)
|
|
|
|
# Add result as partial product.
|
|
partial_products.append( (i+j, t) )
|
|
|
|
# For first row, sign-extend by two bits.
|
|
# Apply sign inversion on the new MSB bit.
|
|
if i == 0:
|
|
partial_products.append( (i+len(yvec)+1, t) )
|
|
partial_products.append( (i+len(yvec)+2, NotBit(t)) )
|
|
|
|
# For each row except the first row, add constant 1 in the next column.
|
|
if i > 0:
|
|
partial_products.append( (i+len(yvec)+1, ConstBit(1)) )
|
|
|
|
# Use Booth encoder to add 1 in case of negative factor (-1 or -2).
|
|
t = BoothNeg(pat)
|
|
partial_products.append( (i, t) )
|
|
|
|
return partial_products
|
|
|
|
|
|
def gen_dadda_tree(partial_products, nbits):
|
|
"""Generate carry save adder based on Dadda tree."""
|
|
|
|
# Sort partial products by bit position.
|
|
tvec = [ [ ] for p in xrange(nbits) ]
|
|
for (p, b) in partial_products:
|
|
if p < nbits:
|
|
tvec[p].append(b)
|
|
|
|
# Build Dadda tree.
|
|
while any([ len(t) > 3 for t in tvec ]):
|
|
# New layer.
|
|
nvec = [ [ ] for p in xrange(nbits+1) ]
|
|
for p in xrange(nbits):
|
|
t = tvec[p]
|
|
i = 0
|
|
while i + 2 < len(t):
|
|
# build full adder
|
|
a = FullAdd(t[i], t[i+1], t[i+2])
|
|
nvec[p].append(AddBitD(a))
|
|
nvec[p+1].append(AddBitC(a))
|
|
i += 3
|
|
if i + 1 < len(t) and len(nvec[p]) % 3 == 2:
|
|
# build half adder
|
|
a = HalfAdd(t[i], t[i+1])
|
|
nvec[p].append(AddBitD(a))
|
|
nvec[p+1].append(AddBitC(a))
|
|
i += 2
|
|
if i < len(t):
|
|
# pass through
|
|
nvec[p] += t[i:]
|
|
tvec = nvec[:nbits]
|
|
|
|
# Last layer.
|
|
nvec = [ [ ] for p in xrange(nbits+1) ]
|
|
for p in xrange(nbits):
|
|
t = tvec[p]
|
|
if len(t) == 3:
|
|
# full adder
|
|
a = FullAdd(t[0], t[1], t[2])
|
|
nvec[p].append(AddBitD(a))
|
|
nvec[p+1].append(AddBitC(a))
|
|
elif len(t) == 2 and len(nvec[p]) > 0:
|
|
# half adder
|
|
a = HalfAdd(t[0], t[1])
|
|
nvec[p].append(AddBitD(a))
|
|
nvec[p+1].append(AddBitC(a))
|
|
else:
|
|
# pass through
|
|
nvec[p] += t
|
|
tvec = nvec[:nbits]
|
|
|
|
# Extract remaining two rows of bits.
|
|
avec = [ (t[0] if len(t) > 0 else ConstBit(0)) for t in tvec ]
|
|
bvec = [ (t[1] if len(t) > 1 else ConstBit(0)) for t in tvec ]
|
|
|
|
return (avec, bvec)
|
|
|
|
|
|
def gen_adder(avec, bvec):
|
|
"""Generate carry-lookahead adder."""
|
|
|
|
def carry_lookahead(pvec, cin):
|
|
"""Recursively determine carry propagation."""
|
|
|
|
if len(pvec) == 1:
|
|
prop = pvec[0]
|
|
cvec = [ cin ]
|
|
else:
|
|
k = (len(pvec) + 1) // 2
|
|
(p0, c0) = carry_lookahead(pvec[:k], cin)
|
|
ctmp = CarryEval(p0, cin)
|
|
(p1, c1) = carry_lookahead(pvec[k:], ctmp)
|
|
prop = CarryMerge(p0, p1)
|
|
cvec = c0 + c1
|
|
|
|
return (prop, cvec)
|
|
|
|
assert len(avec) == len(bvec)
|
|
|
|
# Determine carry-generate and carry-propagate for each position.
|
|
pvec = [ CarryProp(a, b) for (a, b) in zip(avec, bvec) ]
|
|
|
|
# Determine carry-in for each position.
|
|
(prop, cvec) = carry_lookahead(pvec, ConstBit(0))
|
|
|
|
# Array of full adders.
|
|
sumvec = [ AddBitD(FullAdd(a, b, c))
|
|
for (a, b, c) in zip(avec, bvec, cvec) ]
|
|
|
|
return sumvec
|
|
|
|
|
|
def gen_multiplier(xbits, ybits, npipe):
|
|
"""Generate expression tree describing multiplier logic."""
|
|
|
|
xvec = [ InBit('x', p) for p in xrange(xbits) ]
|
|
yvec = [ InBit('y', p) for p in xrange(ybits) ]
|
|
|
|
partial_products = gen_partial_products(xvec, yvec)
|
|
(avec, bvec) = gen_dadda_tree(partial_products, xbits+ybits)
|
|
|
|
if npipe > 0:
|
|
avec = [ Reg(a) for a in avec ]
|
|
bvec = [ Reg(b) for b in bvec ]
|
|
|
|
zvec = gen_adder(avec, bvec)
|
|
|
|
if npipe > 1:
|
|
zvec = [ Reg(z) for z in zvec ]
|
|
|
|
return zvec
|
|
|
|
|
|
def gen_netlist(node, wires, insts):
|
|
"""Generate netlist consisting of wires and component instances."""
|
|
|
|
if node.done:
|
|
# already processed this node
|
|
return
|
|
|
|
node.done = True
|
|
|
|
if isinstance(node, ConstBit):
|
|
# resolve during code generation
|
|
node.wire = node
|
|
elif isinstance(node, InBit):
|
|
# resolve during code generation
|
|
node.wire = node
|
|
elif isinstance(node, Reg):
|
|
# create output wire
|
|
node.wire = 'wreg%d' % len(wires)
|
|
wires.append(node.wire)
|
|
# recurse
|
|
gen_netlist(node.v, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, NotBit):
|
|
# create output wire
|
|
node.wire = 'winv%d' % len(wires)
|
|
wires.append(node.wire)
|
|
# recurse
|
|
gen_netlist(node.v, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, BoothNeg):
|
|
# create output wire
|
|
node.wire = 'wboothneg%d' % len(wires)
|
|
wires.append(node.wire)
|
|
# recurse
|
|
for v in node.pat:
|
|
gen_netlist(v, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, BoothProd):
|
|
# create output wire
|
|
node.wire = 'wboothprod%d' % len(wires)
|
|
wires.append(node.wire)
|
|
# recurse
|
|
for v in node.pat:
|
|
gen_netlist(v, wires, insts)
|
|
for v in node.b:
|
|
gen_netlist(v, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, AddBitD):
|
|
# recurse
|
|
gen_netlist(node.v, wires, insts)
|
|
node.wire = node.v.wire + 'd'
|
|
elif isinstance(node, AddBitC):
|
|
# recurse
|
|
gen_netlist(node.v, wires, insts)
|
|
node.wire = node.v.wire + 'c'
|
|
elif isinstance(node, HalfAdd):
|
|
# create output wires
|
|
node.wire = 'wadd%d' % len(wires)
|
|
wires.append(node.wire + 'd')
|
|
wires.append(node.wire + 'c')
|
|
# recurse
|
|
gen_netlist(node.a, wires, insts)
|
|
gen_netlist(node.b, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, FullAdd):
|
|
# create output wires
|
|
node.wire = 'wadd%d' % len(wires)
|
|
wires.append(node.wire + 'd')
|
|
wires.append(node.wire + 'c')
|
|
# recurse
|
|
gen_netlist(node.a, wires, insts)
|
|
gen_netlist(node.b, wires, insts)
|
|
gen_netlist(node.c, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, CarryProp):
|
|
# create output wires
|
|
node.wire = 'wcarry%d' % len(wires)
|
|
wires.append(node.wire + 'g')
|
|
wires.append(node.wire + 'p')
|
|
# recurse
|
|
gen_netlist(node.a, wires, insts)
|
|
gen_netlist(node.b, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, CarryMerge):
|
|
# create output wires
|
|
node.wire = 'wcarry%d' % len(wires)
|
|
wires.append(node.wire + 'g')
|
|
wires.append(node.wire + 'p')
|
|
# recurse
|
|
gen_netlist(node.p0, wires, insts)
|
|
gen_netlist(node.p1, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
elif isinstance(node, CarryEval):
|
|
# create output wire
|
|
node.wire = 'wcarry%d' % len(wires)
|
|
wires.append(node.wire)
|
|
# recurse
|
|
gen_netlist(node.p, wires, insts)
|
|
gen_netlist(node.c, wires, insts)
|
|
# create instance
|
|
insts.append(node)
|
|
else:
|
|
assert False
|
|
|
|
|
|
def vhdl_inst(node):
|
|
"""Return (name, ports) for a given instance."""
|
|
|
|
if isinstance(node, Reg):
|
|
name = 'smul_flipflop'
|
|
ports = ( 'clk', 'clken', node.v.wire, node.wire )
|
|
elif isinstance(node, NotBit):
|
|
name = 'smul_inverter'
|
|
ports = ( node.v.wire, node.wire )
|
|
elif isinstance(node, BoothNeg):
|
|
name = 'smul_booth_neg'
|
|
ports = ( node.pat[0].wire, node.pat[1].wire, node.pat[2].wire,
|
|
node.wire )
|
|
elif isinstance(node, BoothProd):
|
|
name = 'smul_booth_prod'
|
|
ports = ( node.pat[0].wire, node.pat[1].wire, node.pat[2].wire,
|
|
node.b[0].wire, node.b[1].wire,
|
|
node.wire )
|
|
elif isinstance(node, HalfAdd):
|
|
name = 'smul_half_add'
|
|
ports = ( node.a.wire, node.b.wire,
|
|
node.wire + 'd', node.wire + 'c' )
|
|
elif isinstance(node, FullAdd):
|
|
name = 'smul_full_add'
|
|
ports = ( node.a.wire, node.b.wire, node.c.wire,
|
|
node.wire + 'd', node.wire + 'c' )
|
|
elif isinstance(node, CarryProp):
|
|
name = 'smul_carry_prop'
|
|
ports = ( node.a.wire, node.b.wire,
|
|
node.wire + 'g', node.wire + 'p' )
|
|
elif isinstance(node, CarryMerge):
|
|
name = 'smul_carry_merge'
|
|
ports = ( node.p0.wire + 'g', node.p0.wire + 'p',
|
|
node.p1.wire + 'g', node.p1.wire + 'p',
|
|
node.wire + 'g', node.wire + 'p' )
|
|
elif isinstance(node, CarryEval):
|
|
name = 'smul_carry_eval'
|
|
ports = ( node.p.wire + 'g', node.p.wire + 'p', node.c.wire,
|
|
node.wire )
|
|
else:
|
|
assert False
|
|
|
|
return (name, ports)
|
|
|
|
|
|
def vhdl_wire(wire):
|
|
"""Resolve wire to VHDL expression string."""
|
|
|
|
if isinstance(wire, ConstBit):
|
|
return "'%d'" % wire.v
|
|
elif isinstance(wire, InBit):
|
|
return "%sin(%d)" % (wire.xy, wire.p)
|
|
else:
|
|
assert isinstance(wire, str)
|
|
return wire
|
|
|
|
|
|
def gen_vhdl_lib():
|
|
"""Generate VHDL code for library components."""
|
|
|
|
print """
|
|
--
|
|
-- Flip-flop.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_flipflop is
|
|
port (
|
|
clk: in std_ulogic;
|
|
clken: in std_ulogic;
|
|
d: in std_ulogic;
|
|
q: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_flipflop_arch of smul_flipflop is
|
|
begin
|
|
process (clk) is
|
|
begin
|
|
if rising_edge(clk) then
|
|
if to_x01(clken) = '1' then
|
|
q <= d;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Inverter.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_inverter is
|
|
port (
|
|
d: in std_ulogic;
|
|
q: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_inverter_arch of smul_inverter is
|
|
begin
|
|
q <= not d;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Half-adder.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_half_add is
|
|
port (
|
|
x: in std_ulogic;
|
|
y: in std_ulogic;
|
|
d: out std_ulogic;
|
|
c: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_half_add_arch of smul_half_add is
|
|
begin
|
|
d <= x xor y;
|
|
c <= x and y;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Full-adder.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_full_add is
|
|
port (
|
|
x: in std_ulogic;
|
|
y: in std_ulogic;
|
|
z: in std_ulogic;
|
|
d: out std_ulogic;
|
|
c: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_full_add_arch of smul_full_add is
|
|
begin
|
|
d <= x xor y xor z;
|
|
c <= (x and y) or (y and z) or (x and z);
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Booth negative flag.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_booth_neg is
|
|
port (
|
|
p0: in std_ulogic;
|
|
p1: in std_ulogic;
|
|
p2: in std_ulogic;
|
|
f: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_booth_neg_arch of smul_booth_neg is
|
|
begin
|
|
f <= p2 and ((not p1) or (not p0));
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Booth partial product generation.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_booth_prod is
|
|
port (
|
|
p0: in std_ulogic;
|
|
p1: in std_ulogic;
|
|
p2: in std_ulogic;
|
|
b0: in std_ulogic;
|
|
b1: in std_ulogic;
|
|
y: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_booth_prod_arch of smul_booth_prod is
|
|
begin
|
|
process (p0, p1, p2, b0, b1) is
|
|
variable p: std_ulogic_vector(2 downto 0);
|
|
begin
|
|
p := (p2, p1, p0);
|
|
case p is
|
|
when "000" => y <= '0'; -- factor 0
|
|
when "001" => y <= b1; -- factor 1
|
|
when "010" => y <= b1; -- factor 1
|
|
when "011" => y <= b0; -- factor 2
|
|
when "100" => y <= not b0; -- factor -2
|
|
when "101" => y <= not b1; -- factor -1
|
|
when "110" => y <= not b1; -- factor -1
|
|
when others => y <= '0'; -- factor 0
|
|
end case;
|
|
end process;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Determine carry generate and carry propagate.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_carry_prop is
|
|
port (
|
|
a: in std_ulogic;
|
|
b: in std_ulogic;
|
|
g: out std_ulogic;
|
|
p: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_carry_prop of smul_carry_prop is
|
|
begin
|
|
g <= a and b;
|
|
p <= a xor b;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Merge two carry propagation trees.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_carry_merge is
|
|
port (
|
|
g0: in std_ulogic;
|
|
p0: in std_ulogic;
|
|
g1: in std_ulogic;
|
|
p1: in std_ulogic;
|
|
g: out std_ulogic;
|
|
p: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_carry_merge of smul_carry_merge is
|
|
begin
|
|
g <= g1 or (g0 and p1);
|
|
p <= p0 and p1;
|
|
end architecture;
|
|
|
|
|
|
--
|
|
-- Calculate carry-out through a carry propagation tree.
|
|
--
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_carry_eval is
|
|
port (
|
|
g: in std_ulogic;
|
|
p: in std_ulogic;
|
|
cin: in std_ulogic;
|
|
cout: out std_ulogic );
|
|
end entity;
|
|
|
|
architecture smul_carry_eval of smul_carry_eval is
|
|
begin
|
|
cout <= g or (p and cin);
|
|
end architecture;
|
|
"""
|
|
|
|
|
|
def gen_vhdl_mul(xbits, ybits, npipe, wires, insts, outputs):
|
|
"""Generate VHDL code and write to stdout."""
|
|
|
|
# Declaration.
|
|
print """
|
|
---
|
|
--- %(xbits)d x %(ybits)d bit signed multiplier
|
|
---
|
|
--- %(npipe)d cycles pipeline delay
|
|
---
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
entity smul_%(xbits)d_%(ybits)d is
|
|
port (
|
|
clk: in std_ulogic;
|
|
clken: in std_ulogic;
|
|
xin: in std_logic_vector(%(xleft)d downto 0);
|
|
yin: in std_logic_vector(%(yleft)d downto 0);
|
|
zout: out std_logic_vector(%(zleft)d downto 0) );
|
|
end entity;
|
|
|
|
architecture arch of smul_%(xbits)d_%(ybits)d is
|
|
""" % { 'xbits': xbits, 'ybits': ybits,
|
|
'npipe': npipe,
|
|
'xleft': xbits-1, 'yleft': ybits-1,
|
|
'zleft': xbits+ybits-1 }
|
|
|
|
# Declare signals.
|
|
for w in wires:
|
|
print "signal %s: std_ulogic;" % w
|
|
|
|
# Start architecture body.
|
|
print
|
|
print "begin"
|
|
print
|
|
|
|
# Instantiate components.
|
|
for (i, node) in enumerate(insts):
|
|
(name, ports) = vhdl_inst(node)
|
|
print "u%d: entity work.%s port map (" % (i, name),
|
|
print ", ".join([ vhdl_wire(p) for p in ports ]),
|
|
print ");"
|
|
|
|
print
|
|
|
|
# Drive output signals.
|
|
for (i, wire) in enumerate(outputs):
|
|
print "zout(%d) <= %s;" % (i, vhdl_wire(wire))
|
|
|
|
# End architecture.
|
|
print
|
|
print "end architecture;"
|
|
|
|
|
|
def verilog_wire(wire):
|
|
"""Resolve wire to Verilog expression string."""
|
|
|
|
if isinstance(wire, ConstBit):
|
|
return "1'b%d" % wire.v
|
|
elif isinstance(wire, InBit):
|
|
return "%sin[%d]" % (wire.xy, wire.p)
|
|
else:
|
|
assert isinstance(wire, str)
|
|
return wire
|
|
|
|
|
|
def gen_verilog_lib():
|
|
"""Generate Verilog code for library components."""
|
|
|
|
print """
|
|
// Flip-flop.
|
|
|
|
module smul_flipflop (
|
|
input wire clk,
|
|
input wire clken,
|
|
input wire d,
|
|
output reg q );
|
|
|
|
always @(posedge clk)
|
|
begin
|
|
if (clken)
|
|
q <= d;
|
|
end
|
|
|
|
endmodule
|
|
|
|
|
|
// Inverter.
|
|
|
|
module smul_inverter (
|
|
input wire d,
|
|
output wire q );
|
|
|
|
assign q = ~d;
|
|
|
|
endmodule
|
|
|
|
|
|
// Half-adder.
|
|
|
|
module smul_half_add (
|
|
input wire x,
|
|
input wire y,
|
|
output wire d,
|
|
output wire c );
|
|
|
|
assign d = x ^ y;
|
|
assign c = x & y;
|
|
|
|
endmodule
|
|
|
|
|
|
// Full-adder.
|
|
|
|
module smul_full_add (
|
|
input wire x,
|
|
input wire y,
|
|
input wire z,
|
|
output wire d,
|
|
output wire c );
|
|
|
|
assign d = x ^ y ^ z;
|
|
assign c = (x & y) | (y & z) | (x & z);
|
|
|
|
endmodule
|
|
|
|
|
|
// Booth negative flag.
|
|
|
|
module smul_booth_neg (
|
|
input wire p0,
|
|
input wire p1,
|
|
input wire p2,
|
|
output wire f );
|
|
|
|
assign f = p2 & ((~p1) | (~p0));
|
|
|
|
endmodule
|
|
|
|
|
|
// Booth partial product generator.
|
|
|
|
module smul_booth_prod (
|
|
input wire p0,
|
|
input wire p1,
|
|
input wire p2,
|
|
input wire u0,
|
|
input wire u1,
|
|
output reg y );
|
|
|
|
always @ (*)
|
|
begin
|
|
case ({p2, p1, p0})
|
|
3'b000 : y = 1'b0;
|
|
3'b001 : y = u1;
|
|
3'b010 : y = u1;
|
|
3'b011 : y = u0;
|
|
3'b100 : y = ~u0;
|
|
3'b101 : y = ~u1;
|
|
3'b110 : y = ~u1;
|
|
default : y = 1'b0;
|
|
endcase
|
|
end
|
|
|
|
endmodule
|
|
|
|
|
|
// Deterimine carry generate and carry propagate.
|
|
|
|
module smul_carry_prop (
|
|
input wire a,
|
|
input wire b,
|
|
output wire g,
|
|
output wire p );
|
|
|
|
assign g = a & b;
|
|
assign p = a ^ b;
|
|
|
|
endmodule
|
|
|
|
|
|
// Merge two carry propagation trees.
|
|
|
|
module smul_carry_merge (
|
|
input wire g0,
|
|
input wire p0,
|
|
input wire g1,
|
|
input wire p1,
|
|
output wire g,
|
|
output wire p );
|
|
|
|
assign g = g1 | (g0 & p1);
|
|
assign p = p0 & p1;
|
|
|
|
endmodule
|
|
|
|
|
|
// Calculate carry-out through a carry propagation tree.
|
|
|
|
module smul_carry_eval (
|
|
input wire g,
|
|
input wire p,
|
|
input wire cin,
|
|
output wire cout );
|
|
|
|
assign cout = g | (p & cin);
|
|
|
|
endmodule
|
|
"""
|
|
|
|
|
|
def gen_verilog_mul(xbits, ybits, npipe, wires, insts, outputs):
|
|
"""Generate Verilog code and write to stdout."""
|
|
|
|
# Preamble.
|
|
print """
|
|
/*
|
|
* %(xbits)d x %(ybits)d bit signed multiplier
|
|
*
|
|
* %(npipe)d cycles pipeline delay
|
|
*/
|
|
|
|
module smul_%(xbits)d_%(ybits)d (
|
|
input wire clk,
|
|
input wire clken,
|
|
input wire [%(xleft)d:0] xin,
|
|
input wire [%(yleft)d:0] yin,
|
|
output wire [%(zleft)d:0] zout );
|
|
""" % { 'xbits': xbits, 'ybits': ybits,
|
|
'npipe': npipe,
|
|
'xleft': xbits-1, 'yleft': ybits-1,
|
|
'zleft': xbits+ybits-1 }
|
|
|
|
# Declare signals.
|
|
for w in wires:
|
|
print "wire %s;" % w
|
|
|
|
# Instantiate components.
|
|
for (i, node) in enumerate(insts):
|
|
(name, ports) = vhdl_inst(node)
|
|
print "%s u%d (" % (name, i),
|
|
print ", ".join([ verilog_wire(p) for p in ports ]),
|
|
print ");"
|
|
|
|
print
|
|
|
|
# Drive output signals.
|
|
for (i, wire) in enumerate(outputs):
|
|
print "assign zout[%d] = %s;" % (i, verilog_wire(wire))
|
|
|
|
# End module.
|
|
print
|
|
print "endmodule"
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.format_help = lambda: __doc__
|
|
parser.format_usage = lambda: __doc__
|
|
|
|
parser.add_argument('--lang', action='store', type=str)
|
|
parser.add_argument('--nolib', action='store_true', default=False)
|
|
parser.add_argument('--lib', action='store_true', default=False)
|
|
parser.add_argument('Xbits', action='store', type=int, nargs='?')
|
|
parser.add_argument('Ybits', action='store', type=int, nargs='?')
|
|
parser.add_argument('npipe', action='store', type=int, nargs='?')
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.lang is None or args.lang.upper() not in ('VHDL', 'VERILOG'):
|
|
print >>sys.stderr, __doc__
|
|
print >>sys.stderr, "ERROR: Must specify --lang=vhdl or --lang=verilog"
|
|
sys.exit(1)
|
|
|
|
if args.lib:
|
|
if (args.nolib or
|
|
args.Xbits is not None or
|
|
args.Ybits is not None or
|
|
args.npipe is not None):
|
|
print >>sys.stderr, __doc__
|
|
print >>sys.stderr, "ERROR: Must specify either --lib or",
|
|
print >>sys.stderr, "Xbits, Ybits, npipe"
|
|
sys.exit(1)
|
|
else:
|
|
if (args.Xbits is None or args.Ybits is None or args.npipe is None):
|
|
print >>sys.stderr, __doc__
|
|
print >>sys.stderr, "ERROR: Must specify either --lib or",
|
|
print >>sys.stderr, "Xbits, Ybits, npipe"
|
|
sys.exit(1)
|
|
|
|
if args.Xbits < 4 or args.Ybits < args.Xbits:
|
|
print >>sys.stderr, "ERROR: invalid word lengths"
|
|
sys.exit(1)
|
|
|
|
if args.npipe < 0 or args.npipe > 2:
|
|
print >>sys.stderr, "ERROR: invalid number of register stages"
|
|
sys.exit(1)
|
|
|
|
if not args.lib:
|
|
# Generate expression tree.
|
|
zvec = gen_multiplier(args.Xbits, args.Ybits, args.npipe)
|
|
|
|
# Generate wires and instances.
|
|
wires = [ ]
|
|
insts = [ ]
|
|
for node in zvec:
|
|
gen_netlist(node, wires, insts)
|
|
outputs = [ node.wire for node in zvec ]
|
|
|
|
# Write library components.
|
|
if not args.nolib:
|
|
if args.lang.upper() == 'VHDL':
|
|
gen_vhdl_lib()
|
|
elif args.lang.upper() == 'VERILOG':
|
|
gen_verilog_lib()
|
|
|
|
# Write multiplier.
|
|
if not args.lib:
|
|
if args.lang.upper() == 'VHDL':
|
|
gen_vhdl_mul(args.Xbits, args.Ybits, args.npipe,
|
|
wires, insts, outputs)
|
|
elif args.lang.upper() == 'VERILOG':
|
|
gen_verilog_mul(args.Xbits, args.Ybits, args.npipe,
|
|
wires, insts, outputs)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
# end
|