Source code for hwtLib.examples.hierarchy.rippleadder
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from hwt.code import Concat
from hwt.hObjList import HObjList
from hwt.hdl.types.bits import HBits
from hwt.hwIOs.std import HwIOSignal, HwIOVectSignal
from hwt.hwModule import HwModule
from hwt.hwParam import HwParam
from hwt.pyUtils.typingFuture import override
from hwt.serializer.mode import serializeParamsUniq
[docs]
@serializeParamsUniq
class FullAdder(HwModule):
"""
.. hwt-autodoc::
"""
@override
def hwDeclr(self):
self.a = HwIOSignal()
self.b = HwIOSignal()
self.ci = HwIOSignal()
self.s = HwIOSignal()._m()
self.co = HwIOSignal()._m()
@override
def hwImpl(self):
# [note] it is usually better to copy commonly used self properties because it makes code shorter
a, b, ci = self.a, self.b, self.ci
self.s(a ^ b ^ ci)
self.co(a & b | a & ci | b & ci)
# [wrong] the class is missing serializeParamsUniq decorator,
# this means that it will be serialized as a new module/entity for each instance,
# but it is not required because instances with same p_wordlength are the same
[docs]
class RippleAdder0(HwModule):
"""
.. hwt-autodoc::
"""
@override
def hwConfig(self):
# [possible improvement] you can specify the type directly, this may be more futureproof
# in this case we could also control if the data type should be signed/unsigned
self.p_wordlength = HwParam(4)
@override
def hwDeclr(self):
self.ci = HwIOSignal()
self.a = HwIOVectSignal(self.p_wordlength)
self.b = HwIOVectSignal(self.p_wordlength)
self.s = HwIOVectSignal(self.p_wordlength)._m()
self.co = HwIOSignal()._m()
# [wrong] manually instantiated child components (it is better to use HObjList)
self.fa0 = FullAdder()
self.fa1 = FullAdder()
self.fa2 = FullAdder()
self.fa3 = FullAdder()
@override
def hwImpl(self):
# [wrong] HwIOVectSignal is an Interface sub-class, it is ment to be used for IO of the component
# it works but it has significant limitations, you should use self._sig() wihich handles name collisions
# and has more confort API for clock/reset/default value specifications
self.c = HwIOVectSignal(self.p_wordlength + 1)
self.c[0](self.ci)
self.co(self.c[self.p_wordlength])
# [wrong] manually unrolled/hardcoded for loop
u_fa0 = self.fa0
u_fa1 = self.fa1
u_fa2 = self.fa2
u_fa3 = self.fa3
u_fa0.a(self.a[0])
u_fa1.a(self.a[1])
u_fa2.a(self.a[2])
u_fa3.a(self.a[3])
u_fa0.b(self.b[0])
u_fa1.b(self.b[1])
u_fa2.b(self.b[2])
u_fa3.b(self.b[3])
# [wrong] Why use bits of "c" singal if we can connect ports directly
u_fa0.ci(self.c[0])
self.c[1](u_fa0.co)
u_fa1.ci(self.c[1])
self.c[2](u_fa1.co)
u_fa2.ci(self.c[2])
self.c[3](u_fa2.co)
u_fa3.ci(self.c[3])
self.c[4](u_fa3.co)
# [wrong] why to assing each bit separately if we can assing it all it once from concat of fa.s io
self.s[0](u_fa0.s)
self.s[1](u_fa1.s)
self.s[2](u_fa2.s)
self.s[3](u_fa3.s)
[docs]
@serializeParamsUniq
class RippleAdder1(HwModule):
"""
.. hwt-autodoc::
"""
@override
def hwConfig(self):
self.p_wordlength = HwParam(4)
@override
def hwDeclr(self):
self.ci = HwIOSignal()
self.a = HwIOVectSignal(self.p_wordlength)
self.b = HwIOVectSignal(self.p_wordlength)
self.s = HwIOVectSignal(self.p_wordlength)._m()
self.co = HwIOSignal()._m()
self.fa = HObjList([
FullAdder() for _ in range(self.p_wordlength)
])
@override
def hwImpl(self):
c = self._sig("c", HBits(self.p_wordlength + 1))
c[0](self.ci)
for bitidx, fa in enumerate(self.fa):
fa.a(self.a[bitidx])
fa.b(self.b[bitidx])
fa.ci(c[bitidx])
# not like in verilog, port is just another signal, direction of assignment does matter
c[bitidx + 1](fa.co)
self.s[bitidx](fa.s)
self.co(c[self.p_wordlength])
[docs]
@serializeParamsUniq
class RippleAdder2(HwModule):
"""
.. hwt-autodoc::
"""
@override
def hwConfig(self):
self.p_wordlength = HwParam(4)
@override
def hwDeclr(self):
self.ci = HwIOSignal()
# [possible improvement] you can use io = lambda : HwIOVectSignal(self.p_wordlength) as macro
# so you do not have to repeat same code
self.a = HwIOVectSignal(self.p_wordlength)
self.b = HwIOVectSignal(self.p_wordlength)
self.s = HwIOVectSignal(self.p_wordlength)._m()
self.co = HwIOSignal()._m()
@override
def hwImpl(self):
# [wrong] it is useless to use an extra signal to connect ports, because it can be connected directly
c = self._sig("c", HBits(self.p_wordlength + 1))
lci = [FullAdder() for _ in range(self.p_wordlength)]
self.fa = HObjList(lci)
c[0](self.ci)
for bitIdx in range(self.p_wordlength):
# [wrong] python iteration using range and indexing is slower than using enumerate()
fa = lci[bitIdx]
fa.a(self.a[bitIdx])
fa.b(self.b[bitIdx])
fa.ci(c[bitIdx])
# not like in verilog, port is just another signal, direction of assignment does matter
c[bitIdx + 1](fa.co)
self.s[bitIdx](fa.s)
self.co(c[self.p_wordlength])
[docs]
@serializeParamsUniq
class RippleAdder3(HwModule):
"""
.. hwt-autodoc::
"""
@override
def hwConfig(self):
self.p_wordlength = HwParam(4)
@override
def hwDeclr(self):
self.ci = HwIOSignal()
self.a = HwIOVectSignal(self.p_wordlength)
self.b = HwIOVectSignal(self.p_wordlength)
self.s = HwIOVectSignal(self.p_wordlength)._m()
self.co = HwIOSignal()._m()
@override
def hwImpl(self):
carry = self.ci
# [note] HObjList can be restered with or without items, however we need it in adwance because we
# need registered FullAdder adder instances, because we need it's IO
fa_list = self.fa = HObjList()
for a, b in zip(self.a, self.b):
# [note] componnets do not have to be declared in hwDeclr(), but it is better
# because the configuration of component can be still modified
# after hwDeclr() in hwImpl() the configuration of component is locked imediately after registration
fa = FullAdder()
# [note] the component have to be registered in order to spot the IO
# the registration is done by assining to a property ot his object e.g. self.fa0 = fa
# or by adding to some already registered object, in this case HObjList instance
fa_list.append(fa)
fa.a(a)
fa.b(b)
fa.ci(carry)
carry = fa.co
# [note] we have to reverse because of downto indexing
self.s(Concat(*reversed([fa.s for fa in fa_list])))
self.co(carry)
if __name__ == "__main__":
from hwt.synth import to_rtl_str
m1 = RippleAdder1()
m2 = RippleAdder2()
m3 = RippleAdder3()
from hwt.serializer.verilog import VerilogSerializer
print(to_rtl_str(m1, serializer_cls=VerilogSerializer))
print(to_rtl_str(m2, serializer_cls=VerilogSerializer))
print(to_rtl_str(m3, serializer_cls=VerilogSerializer))