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
asreg
and notwire
? At first I had declared it aswire
, thinking that since the assignment intosum
is "fictitious" (in any case, it's not a "material" assignment), I figuredwire
would be just like a placeholder... but the compiler gave me an error that I cannot assign to awire
. It compiles now when I changed toreg
, and it seems to work, but I'm still a tad uncomfortable.
-
1\$\begingroup\$ Does this answer your question? Difference between blocking and nonblocking assignment Verilog \$\endgroup\$Elliot Alderson– Elliot Alderson2021年11月15日 17:12:05 +00:00Commented 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– Cal-linux2021年11月15日 18:07:13 +00:00Commented 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– Mitu Raj2021年11月16日 04:25:43 +00:00Commented Nov 16, 2021 at 4:25
1 Answer 1
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
.
-
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– awjlogan2021年11月29日 13:30:57 +00:00Commented 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– pc3e2021年11月29日 13:35:42 +00:00Commented 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– awjlogan2021年11月29日 13:39:37 +00:00Commented 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– Cal-linux2021年11月30日 14:20:50 +00:00Commented 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– Cal-linux2021年11月30日 14:23:10 +00:00Commented Nov 30, 2021 at 14:23