5
\$\begingroup\$

I few days ago when I got my FPGA I created a module to drive my 7-Segment display. I used only continuous assignments to drive the leds.

module set_number(input [3:0] x, output [6:0] seg);
    assign seg = x == 0 ? 7'b1000000 :
                 x == 1 ? 7'b1111001 :
                 x == 2 ? 7'b0100100 :
                 x == 3 ? 7'b0110000 :
                 x == 4 ? 7'b0011001 :
                 x == 5 ? 7'b0010010 :
                 x == 6 ? 7'b0000010 :
                 x == 7 ? 7'b1111000 :
                 x == 8 ? 7'b0000000 :
                 x == 9 ? 7'b0010000 : 
                          7'b1111111;
endmodule

I was reading a book and they have a 7-Seg display written like this using registers and a combinational always block.

module hex_to_sseg
(
 input  wire  [3:0]  hex,
 input  wire  dp,
 output  reg  [7:0]  sseg   // output active low
);

always @*
begin
   case(hex)
      4'h0:  sseg[6:0]  = 7'b0000001;
      4'h1:  sseg[6:0]  = 7'b1001111;
      4'h2:  sseg[6:0]  = 7'b0010010;
      4'h3:  sseg[6:0]  = 7'b0000110;
      4'h4:  sseg[6:0]  = 7'b1001100;
      4'h5:  sseg[6:0]  = 7'b0100100;
      4'h6:  sseg[6:0]  = 7'b0100000;
      4'h7:  sseg[6:0]  = 7'b0001111;
      4'h8:  sseg[6:0]  = 7'b0000000;
      4'h9:  sseg[6:0]  = 7'b0000100;
      4'ha:  sseg[6:0]  = 7'b0001000;
      4'hb:  sseg[6:0]  = 7'b1100000;
      4'hc:  sseg[6:0]  = 7'b0110001;
      4'hd:  sseg[6:0]  = 7'b1000010;
      4'he:  sseg[6:0]  = 7'b0110000;
      default:  sseg[6:0]  = 7'b0111000;   //4'hf
  endcase
  sseg[7] = dp;

Is there any practical difference between my approach with the wire and the book's approach with the register and combinational logic?

\$\endgroup\$

4 Answers 4

5
\$\begingroup\$

As transistor mentions in his answer, the logic represented by the two pieces of code you posted are not the same; it has differences in the bit ordering and the latter sample will display hex characters A-F.

But, you are asking about the difference between using a wire variable and an assign statement vs. a reg variable and an always @* construct.

One confusing thing about verilog is that using a reg data type in your code does not always imply that a register will be implemented in the synthesized logic.

In the cases you posted, both solutions would be implemented using combinatorial logic with no physical registers. It is very common to use the always @* construct to model combinatorial logic.

Sequential circuits, which will include physical registers (flip-flops), are also modeled using the always construct, but these circuits will have a posedge and/or negedge signal specified in the sensitivity list.

Of the cases you posted, I would personally prefer to use the always @* version of the code as I think that it more clearly shows the intent of the code; it is easy to see that it represents a decoding table.

\$\endgroup\$
1
  • \$\begingroup\$ This is exactly why SystemVerilog replaced the reg keyword with logic go.mentor.com/wire-vs-reg \$\endgroup\$
    – dave_59
    Commented Dec 27, 2015 at 4:58
4
\$\begingroup\$

Many synthesizers use treat conditional operator (?:) as explicit 2:1 mux. Nested conditional operators with synthesize as the design is write. In your case, it will be a chain of ten 2:1 muses. Here is a diagram of your code synthesized with Yosys 0.3.0 on edaplayground

with assign statement

By converting your code to a case statement it will synthesize like the following (also synthesized with Yosys 0.3.0 on edaplayground). In this case Yosys used a priority mux, but it could have just as easially picked a

with case statement in an always block

Functionally the two are identical. The case statement version will typically have better and more even timing. With the nested conditional operators, when x==0 the prorogation delay is 2 logic gates, when x>=9 it is 11 logic gates. The case statement version also gives the synthesizer more flexibility, allowing, the synthesizer to pick the best options for the situation factoring in available resources, resources needed for other logic, and timing requirements.

In general, it is better use a case statement and let the synthesizer pick the appropriate muxes.

\$\endgroup\$
3
\$\begingroup\$

There are a number of different ways to express the same description in Verilog. The continuous assign statement is good for writing an equation for a single signal, but it's not really RTL. If you wanted to assign multiple signals based on the the same set of inputs, then the always block let you show the flow of decisions made in a procedural block of code. This is a much better way of showing your intent by using a set of more human readable RTL statements.

\$\endgroup\$
2
\$\begingroup\$

If your concern is regarding the binary values then it looks OK. The example code has two differences.

  • The bit order is reversed. This can be rearranged to suit the PCB layout.
  • The book version will display full hex character set whereas yours only handles decimal digits.
  • The book seems to be using a different output register.
\$\endgroup\$
2
  • \$\begingroup\$ I understand those differences. I just don't get why the book uses an always block with a register when you can just do it much more simply with assign. \$\endgroup\$
    – chasep255
    Commented Dec 26, 2015 at 17:27
  • \$\begingroup\$ Sorry, I don't know about that. Let's see who else chips in. \$\endgroup\$
    – Transistor
    Commented Dec 26, 2015 at 17:29

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.