4
\$\begingroup\$

The context is: I have 16-bit registers v1 and v2. These contain signed (2's complement) 16-bit values, but I decided to stay away from the keyword signed, and I'm explicitly handling sign-bits where needed.

At some point, I want to store the average of these two values, with a 3/4 factor applied, into the register output (thus, I want to end up with ouptut containing ((value1+value2)/2)*3/4). Rounding errors are expected and acceptable, but overflow isn't.

I figured I'd declare a variable sum to assign the result of 1.5*value1 + 1.5*value2 to simplify things; in part, the need for simplification is why that intermediate result requires 18-bits. Having assigned sum, I then simply take its upper 16 bits and that's my result.

In my mind, inside the always @(posedge some_clk), inside an if for the particular condition, I would assign sum with a blocking assignment, and in the line after, I assign output with a non-blocking (concurrent) assignment. Something like this:

reg[15:0] v1;
reg[15:0] v2;
reg[17:0] sum;

// ···

always @(posedge some_clk)
begin
    // ···
    if (counter == some_value)
    begin
        sum =   {v1[15],v1[15],v1[15:0]}         // this is v1, with sign extended to make it 18-bit wide
              + {v1[15],v1[15],v1[15],v1[15:1]}  // this is v1/2, sign extended to make it 18-bit wide
              + { ··· (same thing, with v2) } ···
        output <= sum[17:2];  // This is sum/4 (sign-bit works as is)
    end
    // ···
end

Two questions:

  • Is the use of blocking vs. non-blocking assignments correct, and recommended for these types of situations where one wants to split some complex expression into steps with intermediate results?
  • One detail I'm not sure I understand: why do I need to declare sum as reg and not wire? At first I had declared it as wire, thinking that since the assignment into sum is "fictitious" (in any case, it's not a "material" assignment), I figured wire would be just like a placeholder... but the compiler gave me an error that I cannot assign to a wire. It compiles now when I changed to reg, and it seems to work, but I'm still a tad uncomfortable.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Does this answer your question? Difference between blocking and nonblocking assignment Verilog \$\endgroup\$ Commented Nov 15, 2021 at 17:12
  • 1
    \$\begingroup\$ Yes and no. I had actually seen that post before posting my question; and as per my understanding, the code that I wrote is correct and should work as I expect it to work. But then, everywhere I see, it seems like everyone insists that one should very rigidly, almost religiously [my interpretation] use blocking statements exclusively for combinational blocks, and non-blocking statements exclusively for sequential statements (intended to correspond to storage in FFs). But that contradicts my understanding of the issue (and IMO it also contradicts the explanation by The Photon) \$\endgroup\$
    – Cal-linux
    Commented Nov 15, 2021 at 18:07
  • 2
    \$\begingroup\$ Declare 'sum' as wire and assign ... or there is also something called 'let' in SV to 'substitute' for complex expressions which have to be used in another expression. \$\endgroup\$
    – Mitu Raj
    Commented Nov 16, 2021 at 4:25

1 Answer 1

2
\$\begingroup\$

Despite what others have said, the code you posted is perfectly fine and will synthesize correctly in any modern synthesis tool. I have seen books that recommend this style. You could declare the temporary variable (sum) inside the always-block to make the intent more clear.

That being said, most people would instead use an assign statement outside of the always block, or use a two-process style (with one combinatorial always block for logic and one synchronous always block for flip-flops). There are some advantages of these styles:

  • Old synthesis tools were very primitive and would have a problem with any coding style out of the ordinary.
  • These styles will be more familiar to many people, and there will be fewer raised eyebrows.
  • Some companies use lint-tools that complain about any use of blocking assignments in clocked always blocks.
  • With certain rarely used compile options turned on, some synthesis tools (including Design Compiler) will implement what's referred to as "hanging latches" for sum in your code. These other styles avoid that.

For your second question, the simple answer is that nets (wire objects) cannot be procedurally assigned. The distinction between wire and reg in Verilog is confusing. Many (if not most) people avoid wire altogether and declare everything as reg (or more commonly logic, if you can use SystemVerilog.) Another alternative is to declare everything that needs to be assigned inside always/initial blocks as reg and everything else as wire.

\$\endgroup\$
6
  • 2
    \$\begingroup\$ To be a little pedantic, the logic type was introduced in SystemVerilog - hopefully everyone's using SV for new things, but there's obviously plenty of legacy stuff out there. I would always advocate separating combinatorial and sequential - makes the intent much easier to follow. :) \$\endgroup\$
    – awjlogan
    Commented Nov 29, 2021 at 13:30
  • \$\begingroup\$ Sure, I noticed that the question was not tagged with system-verilog, however it appears be pretty common to use logic even for code that is supposed to be Verilog-compatible. Simulators that only support Verilog (e.g., Icarus Verilog) do support the logic-keyword. \$\endgroup\$
    – pc3e
    Commented Nov 29, 2021 at 13:35
  • 1
    \$\begingroup\$ :/ That way lies undefined behaviour in my mind. I'd try to keep them separate - Verilog compiles in SV, but (should) not go the other way. \$\endgroup\$
    – awjlogan
    Commented Nov 29, 2021 at 13:39
  • \$\begingroup\$ Thank you @pc3e ‒ this is an excellent answer. I had been tempted to answer my own question saying that I stumbled into functions (doing a related stackoverflow search), and ended up changing my code to use the function (which I thought is an even better solution for what I was trying to do). But then, that also felt like another case of not answering the question and instead showing what IMO is a better solution. Your comments about the advantages of alternative approaches are greatly appreciated as well! \$\endgroup\$
    – Cal-linux
    Commented Nov 30, 2021 at 14:20
  • \$\begingroup\$ @awjlogan ‒ I don't think your comment is in the least pedantic; when I started reading the answer, I was going to comment that yes, I always use logic when using SystemVerilog (actually, to be 100% honest, when I started reading, what crossed my mind was: "what? logic is also available in classic Verilog?? silly me, all this time thinking that it was only in SV!" :‒) ) \$\endgroup\$
    – Cal-linux
    Commented Nov 30, 2021 at 14:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.