Source code for hwtLib.examples.showcase0

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
The class :class:`~.Showcase0` can be converted to various target formats as
can be seen in "main" of this file.

There are several examples:

.. literalinclude:: /../hwtLib/examples/showcase0.v
   :language: verilog
   :caption: Verilog Std IEEE 1364-2001
   :linenos:

.. literalinclude:: /../hwtLib/examples/showcase0.vhd
   :language: vhdl
   :caption: VHDL IEEE Std 1076-2002
   :linenos:

.. literalinclude:: /../hwtLib/examples/showcase0.cpp
   :language: cpp
   :caption: SystemC IEEE Std 1666-2011
   :linenos:

.. literalinclude:: /../hwtLib/examples/showcase0.hwt.py
   :language: python
   :caption: HWT :class:`hwt.synthesizer.unit.Unit` class definition
   :linenos:
"""

from hwt.code import If, Concat, Switch
from hwt.hdl.types.bits import Bits
from hwt.interfaces.std import Signal, VectSignal
from hwt.interfaces.utils import addClkRstn
from hwt.synthesizer.hObjList import HObjList
from hwt.synthesizer.unit import Unit
from hwtLib.types.ctypes import uint32_t, int32_t, uint8_t, int8_t


[docs]def foo(condition0, statements, condition1, fallback0, fallback1): """ Python functions used as macro """ return If(condition0, statements ).Elif(condition1, fallback0, ).Else( fallback1 )
[docs]class Showcase0(Unit): """ Every HW component class has to be derived from :class:`hwt.synthesizer.unit.Unit` class .. hwt-autodoc:: """ # note that class doc string is also converted to generated HDL
[docs] def __init__(self): # constructor can be overloaded but parent one has to be called super(Showcase0, self).__init__()
[docs] def _declr(self): """ In this function collecting of public interfaces is performed on every attribute assignment. Instances of Interface or :class:`hwt.synthesizer.unit.Unit` are recognized by :class:`hwt.synthesizer.unit.Unit` instance and are used as public interface of this unit. Master interfaces are marked by "._m()", meaning of master direction is specified in interface class. For simple signal master direction means output. """ self.a = Signal(dtype=uint32_t) self.b = Signal(dtype=int32_t) # behavior same as uint32_t (which is Bits(32, signed=False)) self.c = Signal(dtype=Bits(32))._m() # VectSignal is just shortcut for Signal(dtype=Bits(...)) self.fitted = VectSignal(16)._m() self.contOut = VectSignal(32)._m() # this signal will have no driver and it will be considered to be an input self.d = VectSignal(32) # names of public ports can not be same because they need to be accessible from parent self.e = Signal() self.f = Signal()._m() self.g = VectSignal(8)._m() # this function just instantiate clk and rstn interface # main purpose is to unify names of clock and reset signals addClkRstn(self) # HObjList is just regular list, it is used to tell Unit/Interface # to look inside while searching for nested Interface/Unit instances self.cmp = HObjList( Signal() for _ in range(6) )._m() self.h = VectSignal(8)._m() self.i = VectSignal(2) self.j = VectSignal(8)._m() # collision with hdl keywords are automatically resolved and fixed # as well as case sensitivity care and other collisions in target HDL self.out = Signal()._m() self.output = Signal()._m() self.sc_signal = VectSignal(8)._m() self.k = VectSignal(32)._m()
[docs] def _impl(self): """ Purpose of this method In this method all public interfaces and configuration has been made and they can not be edited. """ # create local variable to make code shorter a = self.a b = self.b # "call" is overloaded to do assignment # it means c = a + b in target HDL # type conversion is can be done by _auto_cast or _reinterpret_cast method call self.c(a + b._auto_cast(a._dtype)) # width of signals is not same, this would raise TypeError on regular assignment, # this behavior can be overriden by calling connect with fit=True self.fitted(a, fit=True) # every signal/value has _dtype attribute which is parent type # most of the types have physical size, bit_lenght returns size of this type in bits assert self.a._dtype.bit_length() == 32 # it is possible to create signal explicitly by calling ._sig method # result of every operator is signal const_private_signal = self._sig("const_private_signal", dtype=uint32_t, def_val=123) self.contOut(const_private_signal) # this signal will be optimized out because it has no effect on any output # self.d will remain because it is part of interface self._sig("optimizedOut", dtype=uint32_t, def_val=123) # by _reg function usual d-register can be instantiated # to be able to use this this unit has to have clock defined # (you can force any signal as clock if you call self._ctx._reg directly) # default type is BIT r = self._reg("r", def_val=0) # HDL If statement is object # ~ is negation operator If(~r, # you can directly assign to register and it will assign to its next value # (assigned value appears in it in second clk tick) r(self.e) ) # again signals has to affect output or they will be optimized out self.f(r) # instead of and, or, xor use &, |, ^ because they are overridden to do the job tmp0 = a[1] & b[1] tmp1 = (a[0] ^ b[0]) | a[1] # bit concatenation is done by Concat function, python like slicing supported self.g(Concat(tmp0, tmp1, a[6:])) # results of comparison operators assigned to bits of cmp signal cmp = self.cmp cmp[0](a < 4) cmp[1](a > 4) cmp[2](b <= 4) cmp[3](b >= 4) cmp[4](b != 4) # _eq() is used as ==, # overriding == would have many unintended consequences in python # (it would make all signals unhashable) cmp[5](b._eq(4)) h = self.h # all statements are just objects statements0 = h(0) statements1 = h(1) statements2 = h(2) statements3 = foo(r, statements0, a[1], statements1, statements2) assert isinstance(statements3, If) If(a[2], # also when there is not value specified in the branch of dataflow # (in this case there is missing else branch) this signal will become latched statements3 ) # all statements like If, Switch, For and others are in hwt.code # names of generated signals are patched to avoid collisions automatically r0 = self._reg("r", Bits(2), def_val=0) r1 = self._reg("r", Bits(2), def_val=0) r0(self.i) r1(r0) # type of signal can be array as well, this allow to create memories like BRAM... # ROM will be synchronous ROM in this case rom = self._sig("rom", uint8_t[4], def_val=[i for i in range(4)]) If(self.clk._onRisingEdge(), self.j(rom[r1]) ) self.out(0) # None is converted to value with zero validity mask # same as self.output._dtype.from_py(0, vld_mask=0) self.output(None) # statements are code-generator frendly stm = \ Switch(a).Case(1, self.sc_signal(0) ).Case(2, self.sc_signal(1) ) compileTimeCondition = True if compileTimeCondition: stm.Case(3, self.sc_signal(3) ).Default( self.sc_signal(4) ) # ram working on falling edge of clk # note that rams are usually working on rising edge fRam = self._sig("fallingEdgeRam", int8_t[4]) If(self.clk._onFallingEdge(), # fit can extend signal and also shrink it fRam[r1](a, fit=True), self.k(fRam[r1]._unsigned(), fit=True) )
if __name__ == "__main__": # alias python main function from pprint import pprint from hwt.synthesizer.utils import to_rtl_str from hwt.serializer.hwt import HwtSerializer from hwt.serializer.vhdl import Vhdl2008Serializer from hwt.serializer.verilog import VerilogSerializer from hwt.serializer.systemC import SystemCSerializer from hwt.serializer.resourceAnalyzer.analyzer import ResourceAnalyzer from hwt.synthesizer.utils import synthesised # * new instance has to be created every time because to_rtl_str modifies the unit # * serializers are using templates which can be customized # serialized code is trying to be human and git friendly print(to_rtl_str(Showcase0(), serializer_cls=HwtSerializer)) print(to_rtl_str(Showcase0(), serializer_cls=Vhdl2008Serializer)) print(to_rtl_str(Showcase0(), serializer_cls=VerilogSerializer)) print(to_rtl_str(Showcase0(), serializer_cls=SystemCSerializer)) u = Showcase0() ra = ResourceAnalyzer() synthesised(u) ra.visit_Unit(u) pprint(ra.report())