<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 PopCount8
In this tutorial, we show how to construct a circuit to compute an 8-bit PopCount (population count).
Step1:  In this example, we are going to use the built-in fulladder from Mantle.
Step2:  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.
Step3:  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.
Step4:  Test bench
Let's test this using fault
<ASSISTANT_TASK:>
Python Code:
import magma as m
Explanation: PopCount8
In this tutorial, we show how to construct a circuit to compute an 8-bit PopCount (population count).
End of explanation
from mantle import FullAdder
Explanation: In this example, we are going to use the built-in fulladder from Mantle.
End of explanation
# 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))
Explanation: 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.
End of explanation
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]])
Explanation: 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.
End of explanation
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
m.compile('build/popcount8', Main, inline=True)
!cat build/popcount8.v
Explanation: Test bench
Let's test this using fault
End of explanation
<END_TASK>