In [1]:
import magma as m
In this example, we are going to use the built-in fulladder from Mantle.
In [2]:
from mantle import FullAdder
A common name for a full adder is a carry-sum adder, or csa.
Let's define two csa functions to help us construct the popcount.
In [3]:
# 2 input
def csa2(I0, I1):
return m.bits(FullAdder()(I0, I1, 0))
# 3 input
def csa3(I0, I1, I2):
return m.bits(FullAdder()(I0, I1, I2))
To construct the 8-bit popcount, we first use 3 csa's to sum bits 0 through 2, 3 through 5, and 6 through 7. This forms 3 2-bit results. We can consider the results to be two columns, one for each place. The first column is the 1s and the second column is the 2s.
We then use two fulladders to sum these columns. We continue summing 3-bits at a time until we get a single bit in each column.
A common way to show these operations is with Dadda dot notation which shows how many bits are in each colum.
In [4]:
def popcount8(I):
# Dadda dot notation (of the result)
# o o csa0_0_21 - row 0, bits 2 and 1
# o o csa0_1_21 - row 1, bits 2 and 1
# o o csa0_2_21 - row 2, bits 2 and 1
csa0_0_21 = csa3(I[0], I[1], I[2])
csa0_1_21 = csa3(I[3], I[4], I[5])
csa0_2_21 = csa2(I[6], I[7])
# o o csa1_0_21 - row 0, bits 2 and 1
# o o csa1_1_43 - row 1, bits 4 and 2
csa1_0_21 = csa3(csa0_0_21[0], csa0_1_21[0], csa0_2_21[0])
csa1_1_42 = csa3(csa0_0_21[1], csa0_1_21[1], csa0_2_21[1])
# o o csa2_0_42 - row 0, bits 4 and 2
csa2_0_42 = csa2(csa1_0_21[1], csa1_1_42[0])
# o o csa3_0_84 - row 0, bits 8 and 4
csa3_0_84 = csa2(csa1_1_42[1], csa2_0_42[1])
return m.bits([csa1_0_21[0], csa2_0_42[0], csa3_0_84[0], csa3_0_84[1]])
In [5]:
import fault
class Main(m.Circuit):
io = m.IO(I=m.In(m.Bits[8]), O=m.Out(m.Bits[4]))
io.O @= popcount8(io.I)
tester = fault.PythonTester(Main)
assert tester(0xFF) == 8
assert tester(0xF0) == 4
assert tester(0xEE) == 6
In [6]:
m.compile('build/popcount8', Main, inline=True)
!cat build/popcount8.v
There is a more general version of PopCount in the Mantle library util.compressor.
In [ ]: