Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a documentation for CombInit #228

Merged
merged 6 commits into from
Nov 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions source/SpinalHDL/Semantic/assignments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,66 @@ Combinatorial loops

SpinalHDL checks that there are no combinatorial loops (latches) in your design.
If one is detected, it raises an error and SpinalHDL will print the path of the loop.

CombInit
--------

``CombInit`` can be used to copy a signal and its current combinatorial assignments. The main use-case is to be able to overwrite the copied later, without impacting the original signal.

.. code-block:: scala

val a = UInt(8 bits)
a := 1

val b = a
when(sel) {
b := 2
// At this point, a and b are evaluated to 2: they reference the same signal
}

val c = UInt(8 bits)
c := 1

val d = CombInit(c)
// Here c and d are evaluated to 1
when(sel) {
d := 2
// At this point c === 1 and d === 2.
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add an example here for one of the major use-cases which is to make helper functions that break the reference to the original signal. That would explain what to use it for, I found it's pretty rare in "normal" HDL code. A dumb example would be:

// note that condition is an elaboration time constant
def invertedIf(b: Bits, condition: Boolean): Bits = if(condition) { ~b } else { CombInit(b) }

where we shouldn't return just b in the false case as that would create weird behavior (if true the return value can't influence the original b, if false it can)
Do you think that makes sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestions.
I like your invertedIf example, I was looking for a simple realistic use case : this is it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 6222df6 for a first try.

If we look at the resulting Verilog, ``b`` is not present. Since it is a copy of ``a`` by reference, these variables designate the same Verilog wire.

.. code-block:: verilog

always @(*) begin
a = 8'h01;
if(sel) begin
a = 8'h02;
end
end

assign c = 8'h01;
always @(*) begin
d = c;
if(sel) begin
d = 8'h02;
end
end

``CombInit`` is particularly helpful in helper functions to ensure that the returned value is not referencing an input.

.. code-block:: scala

// note that condition is an elaboration time constant
def invertedIf(b: Bits, condition: Boolean): Bits = if(condition) { ~b } else { CombInit(b) }

val a2 = invertedIf(a1, c)

when(sel) {
a2 := 0
}

Without ``CombInit``, if ``c`` == false (but not if ``c`` == true), ``a1`` and ``a2`` reference the same signal and the zero assignment is also applied to ``a1``.
With ``CombInit`` we have a coherent behaviour whatever the ``c`` value.