2
\$\begingroup\$

I am currently learning Verilog and trying to make a PRBS generator in Verilog. While doing that, I have tried different combinations as shown in the code and pictures below. Later on, I have to put it on an Artix A7 FPGA. I'll add a clock frequency divider circuit later on for the PRBS to show on a speed perceivable to human eye.

Which one is better for real life implementation? I am sure that due to reasons like space, time, energy or some other reason, one will be preferable over other.

Configurations are like

  1. Output is register and has non-blocking assignment to it
module prbs_gen(clk, prbs);
input clk;
output reg prbs;
reg[31:0] temp = 32'b11010101110101011101010111010101;
reg x;

always @(posedge clk)
    begin
    prbs <= temp[0];
    x = temp[1]^temp[0];
    temp = temp >> 1;
    temp[31] = x;
    end
endmodule
  1. Output is reg but has blocking assignment to it, inside always block
module prbs_gen(clk, prbs);
input clk;
output reg prbs;
reg[31:0] temp = 32'b11010101110101011101010111010101;
reg x;

always @(posedge clk)
    begin
    prbs = temp[0];
    x = temp[1]^temp[0];
    temp = temp >> 1;
    temp[31] = x;
    end
endmodule
  1. Output is reg and blocking assignment with "assign" keyword inside "always" block
module prbs_gen(clk, prbs);
input clk;
output reg prbs;
reg[31:0] temp = 32'b11010101110101011101010111010101;
reg x;

always @(posedge clk)
    begin
    assign prbs = temp[0];
    x = temp[1]^temp[0];
    temp = temp >> 1;
    temp[31] = x;
    end
endmodule
  1. Output type isn't specified, but the blocking assignment is out of the "always" block
module prbs_gen(clk, prbs);
input clk;
output prbs;
reg[31:0] temp = 32'b11010101110101011101010111010101;
reg x;

assign prbs = temp[0];
always @(posedge clk)
    begin
    x = temp[1]^temp[0];
    temp = temp >> 1;
    temp[31] = x;
    end
endmodule
  1. Output is reg and non-blocking assignment to it in "always" block
module prbs_gen(clk, prbs);
input clk;
output reg prbs;
reg[31:0] temp = 32'b11010101110101011101010111010101;
reg x;

always @(posedge clk)
    begin
    prbs <= temp[0];
    x = temp[1]^temp[0];
    temp = temp >> 1;
    temp[31] = x;
    end
endmodule

Common testbench code

module tb();
reg clk;
wire prbs;

prbs_gen uut(clk, prbs);
initial clk = 0;
always #5 clk = ~clk;
endmodule

Codes with waveforms below.

Output is register and has non-blocking assignment to it

Output is reg but has blocking assignment to it

Output is reg and blocking assignment with "assign" keyword

Output is reg, but the blocking assignment is out of the "always" block

Output is wire and blocking assignment with "assign" keyword

\$\endgroup\$
10
  • 2
    \$\begingroup\$ much better to put the code text into a code block (select and ctrl-k) than post pictures of it, then you get a scrollable copy/pastable window in your question that's much easier for potential answerers to handle, it's also text searchable so your question can be found by future users of search engines. \$\endgroup\$
    – Neil_UK
    Commented Jun 11, 2023 at 14:04
  • \$\begingroup\$ You might want to contrast the 'hardware' approach that takes an XOR of the all sequence defining bits, then shifts the result into the end of a shift register, with the 'software' approach that XORs each sequence defining bit in the shift register with the bit being shifted out. Subject to some straightforward symmetries, they produce the same sequence. \$\endgroup\$
    – Neil_UK
    Commented Jun 11, 2023 at 17:57
  • \$\begingroup\$ @Neil_UK okay I'll put the code as well. But I don't have the pictures now so I have to keep them as it is. \$\endgroup\$ Commented Jun 11, 2023 at 22:45
  • \$\begingroup\$ Don't mix blocking and non-blocking assignment, and don't mix blocking assignment and sequential element description, if you want to manage maintainable code/project. \$\endgroup\$
    – Light
    Commented Jun 12, 2023 at 2:35
  • \$\begingroup\$ And didn't your tool complain about that assign in always? \$\endgroup\$
    – Light
    Commented Jun 12, 2023 at 2:39

2 Answers 2

4
\$\begingroup\$

Your 5 code examples do not adhere to good Verilog coding practices. For example, the 1st example uses both blocking and nonblocking assignments inside the same always block. Generally, it is better to use nonblocking assignments to infer sequential logic. The paper Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill! offers detailed guidelines and explanations. In a nutshell:

In general, the answer is simulation related. Ignoring the above guidelines can still infer the correct synthesized logic, but the pre-synthesis simulation might not match the behavior of the synthesized circuit.

Your examples give you the simulation results you were expecting. It will be interesting to see what results you get on your FPGA. Since your code is short (only 4 lines in the always block), you may get the FPGA results you expect. But, with larger designs, it will become more important to stick to these guiddelines.

blocking assignment with "assign" keyword inside "always" block

This is known as a procedural continuous assignment, and it is not a common coding style. You can read more about it in the IEEE Std 1800-2017, section 10.6 Procedural continuous assignments. You should avoid this. In fact, this syntax is scheduled for deprecation.


Here are some minor tips which would make your code easier to understand and maintain.

You can use underscore separators for the 32-bit constant you use to initialize temp. It is common to separate groups of 4 bits:

reg[31:0] temp = 32'b1101_0101_1101_0101_1101_0101_1101_0101;

It is even easier to understand using hexadecimal notation:

reg [31:0] temp = 32'hd5d5_d5d5;

It is confusing to name a signal x since "x" is also used to represent the Verilog "unknown" value.

Simplify the module port declaration by using ANSI-style ports:

module prbs_gen (
    input clk,
    output reg prbs
);

I think this code is equivalent to yours while adhering to the guidelines:

module prbs_gen (
    input clk,
    output reg prbs
);
reg [31:0] temp = 32'hd5d5_d5d5;

always @(posedge clk) begin
    prbs <= temp[0];
    temp <= {(temp[1]^temp[0]), temp[31:1]};
end
endmodule
\$\endgroup\$
3
  • \$\begingroup\$ thanks for telling the good practices. \$\endgroup\$ Commented Jun 13, 2023 at 4:51
  • \$\begingroup\$ in the code you have posted, correct me if I'm wrong. At first clock edge temp[0] is stored somewhere, {(temp[1]^temp[0]), temp[31:1]} is stored after the clock pulse values are assigned to prbs and temp. I actually don't know the way/order statements are executed in Verilog ( like blocking is instant assignment, non-blocking is after the block is (and other always blocks if any) is executed, what is computed earlier initial block or always etc.). If I could know that, coding will get easier with lesser lines. \$\endgroup\$ Commented Jun 13, 2023 at 5:02
  • \$\begingroup\$ @ItachiUchiha: The paper has a good description of some of the aspects of nonblocking assignment behavior. There are many more resources available on the topic as well. \$\endgroup\$
    – toolic
    Commented Jun 15, 2023 at 10:26
1
\$\begingroup\$

Verilog is a hardware description language. Since you are just beginning to learn Verilog coding, I highly recommend drawing a circuit diagram of the hardware first before writing out Verilog code. Here is the circuit diagram that I came up with.

enter image description here

Neatly divide your Verilog code into combinational logic and sequential logic. Prefer assign statements for simple combinational logic. Use always blocks with non-blocking statements for sequential logic. Keep your always block simple. Of course these are dealt with in detail in the paper toolic shared in the other answer.

module prbs_gen(    
    input clk,
    output reg prbs);

   reg [31:0] temp = 32'b1101_0101_1101_0101_1101_0101_1101_0101;
   wire temp_xor;

   // Combinational logic
   assign prbs = temp[0];
   assign temp_xor = temp[0] ^ temp[1];

   // Sequential logic (Shift register)
   always @ (posedge clk)
   begin
      temp[0] <= temp[1];
      temp[1] <= temp[2];
      temp[2] <= temp[3];
              .
              .
              .
      temp[30] <= temp[31];
      temp[31] <= temp_xor;
   end

endmodule

The above code is a direct translation of the circuit diagram I drew and a verbose code. Of course we could make the always block concise by using the following statements

   always @ (posedge clk)
      temp <= {temp_xor, temp[31:1]};

OR maybe even this would work and synthesize

   always @ (posedge clk)
      temp <= temp >> 1;

Even though the last piece of code would work, I would not recommend it, especially for a beginner, because it's very hard to connect the above piece of code with the circuit diagram.

Lastly, regarding the question on space, time, energy for the different codes. Although your codes are different, you are just describing the same piece of hardware in different ways. So most likely all of your codes could have the same area, and power. Taking less time or space or power depends on how you construct your hardware circuit diagram and not by the coding style. Verilog supports different coding styles to describe the same hardware.

\$\endgroup\$

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.