0
\$\begingroup\$

Assume we have a module with 32 bits output like this:

module ModuleLow(foo,...);
output [31:0] foo;

Now we want to use it in another module ( a very simple example!):

module ModuleHigh( ..,reset,..);
input reset;

wire [31:0] fooWire;
reg  [31:0] fooReg;
ModuleLow module1(.foo(fooWire), ...)

always @(posedge GCLK)
 if(reset)
    begin
      fooReg<= fooWire;   // TRIAL-1: fooReg<=  12345.....;  => 2.3ns
    end
 else
     fooReg<=fooReg+1;    //TRIAL-2 :  fooReg<=fooWire+1; => 18ns

This is a very common method of passing a value between modules ( wire -> reg ). But in my case , it leads to a 2028 bit wire that noticeably reduces the speed of a Spartan-III down to 12ns.

I tried these:

1- When I replace the statement fooReg<= fooWire with a number ( like: fooReg<=12345....; TRIAL-1 in the code) , the performance jumps high (GCLK timing constraint value <2.5 ns) .

2- When I use the wire itself ( using fooReg<=fooWire+1; TRIAL-2 in the code example) the performance drops even more (18ns)

From these experiments I concluded that it is much more design friendly to use registers inside a block instead of wires ( routing and DRC problems? ).

I was thinking if there is a way to omit that intermediate wire between modules . This can remove the wiring in "TRIAL-1" part ( initial assignment ) that leads to a higher performance. Something like this:

ModuleLow module1(.foo(fooReg), ...); // using registry without a wire.

I think this is illegal in Verilog (ISE WebPack v14.7 gives error as lvalue assignment problem) but I am looking for some trick or something if exists.

\$\endgroup\$
  • \$\begingroup\$ "2028 bit wire": that sounds like an error? How do you know that's happened? \$\endgroup\$ – pjc50 Nov 20 '13 at 14:59
  • \$\begingroup\$ @pjc50 : yes it was a typo:it is 2048 bits : wire[7:0] fooWire[255:0]; \$\endgroup\$ – Aug Nov 20 '13 at 15:24
  • 1
    \$\begingroup\$ That's an array of 256 wires! Why not just wire [31:0] fooWire; ? Why is that not in your example code? If you try and build a 2048 bit wide adder of course it will be slow. \$\endgroup\$ – pjc50 Nov 20 '13 at 15:31
  • \$\begingroup\$ @pjc50 I couldnt put my code here because it is around 700 lines!.It is just an example. Actually the adder just receives only 8 bits in each clock cycle ( All the 256 wires are set into 256 registers at the initial time and then incrementally fed into the adder). The overall performance is good ( 5.7 ns) but as I need 200MHz, I am trying to bring it down to 5ns. \$\endgroup\$ – Aug Nov 20 '13 at 15:55
  • 2
    \$\begingroup\$ (the issue is that your example is misleading as it doesn't show the extremely wide bus that you're talking about in the text which is relevant to the slowdown) \$\endgroup\$ – pjc50 Nov 20 '13 at 17:05
2
\$\begingroup\$

Your conclusion is not correct. It doesn't matter to the hardware whether you use a reg or a wire, the issues you are discussing are part of the Verilog syntax.

The reason your design gets so much faster when you replace fooWire with a number is that your logic isn't really doing anything and it all gets optimized away. The assignment of fooWire to fooReg requires that signals actually propagate from one part of the chip to another, and that takes time. Changing the assignment of fooWire to fooWire+1 forces the tools to create a 32-bit adder and insert it in the delay path, so of course the design will get slower.

By the way, it's register, not registry.

\$\endgroup\$
  • \$\begingroup\$ That's correct for the TRIAL-1, but the main question remains: If I can get rid of " fooReg<= fooWire; ", it goes so fast ( the signal will be present "near" the adder after optimization instead of moving along the chip. the proof is TRIAL-2 ) \$\endgroup\$ – Aug Nov 20 '13 at 14:11
  • \$\begingroup\$ Sorry, but your statements don't make sense to me. Of course if you remove functionality from your design it will run faster. Focusing on the wire vs. reg is missing the point...it's all the same in hardware. \$\endgroup\$ – Joe Hass Nov 20 '13 at 14:50
2
\$\begingroup\$

I'll just answer one of your three main questions here, since this got long enough just focusing on the one question.

When I replace the statement fooReg<= fooWire with a number ( like: fooReg<=12345....; TRIAL-1 in the code) , the performance jumps high (GCLK timing constraint value <2.5 ns) .

I guess you're asking, "why is this?" Let's look at your code:

always @(posedge GCLK)
 if(reset)
    begin
      fooReg<= fooWire;   
    end
 else
     fooReg<=fooReg+1;    

This code does not properly represent a register with RESET. A RESET input (aka PRESET/CLEAR) clears the output to a fixed value, either 1 or 0, every time it is used. Your code sets the output to equal an input value which might change each time the reset signal is seen.

Your code could also be written as

always @(posedge GCLK)
  fooReg <= SEL ? fooWire : fooReg+1;

where I changed the name of reset to SEL to emphasize it's acting as the select line on a multiplexer.

When you change the code to

always @(posedge GCLK)
 if(reset)
    begin
      fooReg<= 12345;  
    end
 else ...

Then you are actually implementing a reset circuit, and the code is able to synthesize to use the hardware reset lines in the flip-flops that hold fooReg.

Using the hardware reset can be faster for several reasons:

  • the reset signal can be carried on a global reset interconnect instead of normal wires.
  • A layer of logic (the multiplexer) is eliminated between the adder (fooReg+1) and the register.
  • A counter with reset is probably available as a highly optimized predefined block, not subject to the whims of the tool's place and route optimization process.
\$\endgroup\$
  • \$\begingroup\$ Thank you! your suggestion for changing reset to a selector improved the performance ( from 5.7ns to 5.3ns) . Now I am working on using a block ram but I don't know if a block ram is more module ( IP CORE) is more efficient than an array of registers or not \$\endgroup\$ – Aug Nov 20 '13 at 17:46
  • \$\begingroup\$ BlockRAM will be more resource efficient (because you'd be using dedicated blocks instead of LUTs that could be used for other things) but require you to design the rest of your system to use the BlockRAM interface correctly. (e.g., assert WE at the correct time, etc) I doubt if it will be faster. 5 ns is very fast for an FPGA. \$\endgroup\$ – The Photon Nov 20 '13 at 18:11
  • \$\begingroup\$ Just one more irrelevant question!: After some more modifications in pin assignment and using your guidelines, now I have reached 5.18ns =194MHz. Can I use 200MHz with that? Spartan III is tolerant enough to accept this 3% error or I should reach the exact 5ns? \$\endgroup\$ – Aug Nov 20 '13 at 19:24
  • \$\begingroup\$ For a one-off you can always try it and see. But remember it might stop working if the temperature changes or something. I wouldn't ship it to a custsomer with even a 3% shortfall (especially because you're probably looking at post-synthesis timing, not post-place-and-route). \$\endgroup\$ – The Photon Nov 20 '13 at 20:00
1
\$\begingroup\$

This answer will just talk about one part of your question,

ModuleLow module1(.foo(fooReg), ...); // using registry without a wire.

The main issue here is that if fooReg is a register, you want all the assignments to that register to be in the same always block.

You can't (or at least don't want to) have one always block in ModuleLow that sets the initial value, and another always block in ModuleHigh that increments it when the clock edge arrives.

Possibly such a thing could be made to work in simulation (using tasks rather than modules) but it would most likely break synthesis, and also confuse the heck out of anyone reading your code (including you if you come back to it after a couple days).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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