Back to the future

J

Jan Decaluwe

Since I started with RTL design in 1991, my goal has been to raise the
abstraction level as much as possible. Two key characteristics of my
design style are:

* using a single clocked process for both control and data operations
* using variables, including register inference from them

Since the early days I am convinced that this is a superior coding
style. Therefore, I always expected it would become the mainstream
style anytime soon. On this I have been dead wrong. In the past two
decades, the mainstream style has moved in the opposite direction. The
absolute low point was Cliff Cummings' guideline not to use blocking
assignments in a clocked process, which effectively bans variable
semantics for RTL designers.

However, now there may a new fact. For the first time, a high-profile
publication is in the making with a different message. It is
appropriately called "The Art of Good Design". Interestingly, it is
being written by Mike Keating, the co-author of the Reuse Methodology
Manual (RMM). I believe the RMM is partially responsible for the
"unstructured chaos" we are in (Mr. Keatings' words). So it's good to
see that he is changing his mind. I have the impression that the
change comes after a careful study and rewriting of real designs. Good
that someone takes the time to do this.

In a recent post on his blog, Mr. Keating announced a preview of
Chapter 3 of The Art of Good Design:

http://synopsysoc.org/futureofdesign/?p=35

In this chapter (after a false start on perl hacking stuff) he
acknowledges the superiority of the single clocked process to describe
an FSM with embedded data operations. Moreover, he discards the
guideline not to mix blocking and non-blocking assignments. He does
this implicitly, as all blocking assignments are encapsulated in
functions, but this doesn't fundamentally alter the conclusion.

He has yet to endorse variable semantics (blocking assignments) as a
generally valuable coding technique, including register inference. But
still, it's a good start. The idea to encapsulate logic systematically
in functions is interesting. Add variables and procedures to the mix,
and what you end up with is the purest form of abstract RTL design -
a.k.a. Mike Treseler's style!

Mike Keatings' blog is called "the future of design". I'd rather call
it "back to the future of design" :)

Jan

--
Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com
Python as a HDL: http://www.myhdl.org
VHDL development, the modern way: http://www.sigasi.com
Analog design automation: http://www.mephisto-da.com
World-class digital design: http://www.easics.com
 
J

Jason Zheng

Interesting post. But I bet you expected people to disagree with you,
didn't you?
Since the early days I am convinced that this is a superior coding
style. Therefore, I always expected it would become the mainstream
style anytime soon. On this I have been dead wrong. In the past two
decades, the mainstream style has moved in the opposite direction. The
absolute low point was Cliff Cummings' guideline not to use blocking
assignments in a clocked process, which effectively bans variable
semantics for RTL designers.

Cliff Cummings doesn't have a whip. He has a following because what he
pointed out in his papers make sense, are easy to follow, and, when
followed, make a designer's life very easy.

He has yet to endorse variable semantics (blocking assignments) as a
generally valuable coding technique, including register inference. But
still, it's a good start. The idea to encapsulate logic systematically
in functions is interesting. Add variables and procedures to the mix,
and what you end up with is the purest form of abstract RTL design -
a.k.a. Mike Treseler's style!

I believe that this is really in the eye of beholder. It's hard to make
conclusions without looking at some concrete examples. I personally
believe that with SystemVerilog, the blocking assignment issue has been
resolved. If you want to combine complex abstract logic with register
inference, just use local non-reg-type variables. They won't infer
extra flip-flops and have limited scope.

I think Mike Treseler's style makes it easy to auto-generate HDLs, but
not necessarily easy for designers and analysts. To me, RTL
implementation is 75% art, 25% science. I have yet to see something
molded to a systematic template that can be considered artful design.

~Jason Zheng
 
J

Jason Zheng

I think Mike Treseler's style makes it easy to auto-generate HDLs, but
not necessarily easy for designers and analysts. To me, RTL
implementation is 75% art, 25% science. I have yet to see something
molded to a systematic template that can be considered artful design.

Hate to reply to myself, but what I was really referring to the "put
everything in a giant always block, and use functions for logic" style.
 
B

Benjamin Couillard

There is also a good article on a VHDL-design methodology using 2
processes per file. In the article, the combinational part and
sequential parts are separated. I'm not convinced it's necessary to
separate them but the ideas in the article are pretty good.

http://www.gaisler.com/doc/vhdl2proc.pdf

On general I would say that smaller blocks are easier to understand,
but sometimes, smaller means more obfuscation which goes against the
idea of making the design easier to understand.
 
J

Jan Decaluwe

Jason said:
Interesting post. But I bet you expected people to disagree with you,
didn't you?

Well, yes, I hope I'm not being trivial if that's what you mean.
I personally
believe that with SystemVerilog, the blocking assignment issue has been
resolved. If you want to combine complex abstract logic with register
inference, just use local non-reg-type variables. They won't infer
extra flip-flops and have limited scope.

But surely they would be assigned using blocking assignments, while
the non-local regs would presumably be assigned using non-blocking
assignments. Perfectly fine with me, but that style does violate
that infamous guideline. Why don't we join forces and finally
get rid of it :)

Jan

--
Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com
Python as a HDL: http://www.myhdl.org
VHDL development, the modern way: http://www.sigasi.com
Analog design automation: http://www.mephisto-da.com
World-class digital design: http://www.easics.com
 
M

Mike Treseler

Jan said:
Since I started with RTL design in 1991, my goal has been to raise the
abstraction level as much as possible. Two key characteristics of my
design style are:
* using a single clocked process for both control and data operations
* using variables, including register inference from them
Since the early days I am convinced that this is a superior coding
style. Therefore, I always expected it would become the mainstream
style anytime soon. On this I have been dead wrong.

The upside to this fact is that a few hdl designers will enjoy
an unfair advantage as long as confusion continues to be promoted by the
vendors.
> I have the impression that the
change comes after a careful study and rewriting of real designs. Good
that someone takes the time to do this.

Yes, papers that do real A-B testing
of style issues are rare because it does take a lot of time.
My informal tests have convinced me that synthesis does not care
if I code the same design in one process per register or one process
per entity.
In a recent post on his blog, Mr. Keating announced a preview of
Chapter 3 of The Art of Good Design:
http://synopsysoc.org/futureofdesign/?p=35
In this chapter (after a false start on perl hacking stuff)

That "synatatic fluff" of multiple processes is what got me started.
Mike Keatings' blog is called "the future of design". I'd rather call
it "back to the future of design" :)

Thanks for the link.
If brand S is coming around, that is good news.
That style is what trickles down to the fpga vendors' training material.

-- Mike Treseler
 
M

Mike Treseler

Benjamin said:
There is also a good article on a VHDL-design methodology using 2
processes per file. In the article, the combinational part and
sequential parts are separated. I'm not convinced it's necessary to
separate them but the ideas in the article are pretty good.

http://www.gaisler.com/doc/vhdl2proc.pdf

Thanks for the link.
Sections 5.1 and 5.2 are excellent.
I would substitute vhdl functions
for the asynchronous processes in the
examples.

-- Mike Treseler
 
J

Jan Decaluwe

Benjamin said:
There is also a good article on a VHDL-design methodology using 2
processes per file. In the article, the combinational part and
sequential parts are separated. I'm not convinced it's necessary to
separate them but the ideas in the article are pretty good.

Indeed, the separation is not necessary. I don't see how it would
change Gaisler's message. It's a pity though, because as you say his ideas
are good: moving to higher abstraction levels in general, and
the use of record types and variable-centric design in particular.

Transforming his style into a single-process style would be trivial,
but still significant. Once there, new possibilities pop up, in
particular elegant coding solutions that may result in register
inference from variables directly.

Jan

--
Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com
Python as a HDL: http://www.myhdl.org
VHDL development, the modern way: http://www.sigasi.com
Analog design automation: http://www.mephisto-da.com
World-class digital design: http://www.easics.com
 
J

Jason Zheng

Jonathan,
I'm not sure I know what difference SystemVerilog makes
to this. You've always been able to declare local variables
in any named begin...end; SV adds the ability to declare in
an anonymous begin...end, but that has little impact (at least,
for synthesis) - it makes the locals completely inaccessible
from outside the block, which is obviously good, but synthesis
has always enforced that anyhow because of its refusal to handle
hierarchical (dotted) names.

Yes I was referring to the anonymous sequential blocks. Thanks for
clearing that up. I understand your point about not having much impact
on the synthesis, but not requiring named block is something that makes
SystemVerilog a more "pure" implementation of local variables.
I also don't know what you mean by "non-reg-type". Are you thinking
of the distinction (from an early version of SV, no longer relevant)
between "logic" and "reg"? That difference is now, in IEEE SV-2005,
purely syntactic; the two data types have identical behaviour.

I should explain this. My habit is to use non-reg and non-logic
variables (integer) to act as temp. variables so as to avoid confusion:

logic [3:0] A;

always_ff @ (posedge CLK or negedge RSTn)
if (~RSTn)
A <= 4'h0;
else
begin
integer i;
i = D * D;
i = i - D;
A <= i;
end

It's just a way to remind the readers that A is the inference target,
and i is really acting as a temp variable. You could have use logic
type for i, but it would be less obvious.
Finally, I don't know of any SystemVerilog feature that mandates
no register inference from local variables. In the SV code below,
which makes complete sense for synthesis, variable Q looks like
a perfectly good register to me:

Again, I think part of the problem is to avoid confusing other folks
that might be reading the code later. I have to concede that I
originally thought there were special rules about non-reg-type
inferences, but after reading your post and experimenting some, I
realized that this is really just a style issue.
Please forgive me if I've missed your point, but in my role
as fully-paid-up SystemVerilog wonk I wouldn't want folk
going away from this thread with a misleading impression.

I appreciate your critique. I am not assuming an expert role by posting
on this forum, only just another engineer who's always wanting to learn.
 
A

Andy

Mr Keating's articles are an excellent read; I read the other chapters
too. It's about time someone wrote a book on applying well known SW
engineering techniques to HDL.

I'd heard of, but also forgotten, the "Rule of Seven" with regards to
hierarchical organization. This fits well with my experience.

I disagree with the use of the pre-processor. There are ways to skin
that cat in VHDL without bringing another syntax and tool into the
mix. Besides, what happens when you don't want something reset (like a
distributed RAM that cannot be reset)?

Use of functions for controlling scope in combinatorial logic (inside
clocked processes) is an excellent idea, as long as the complexity
really needs it. The choice between One Big Process w/o functions, OBP
w/functions, and/or several smaller processes can be pretty blurry
depending on the case at hand.

Andy
 
M

Mike Treseler

Jason said:
I believe that this is really in the eye of beholder. It's hard to make
conclusions without looking at some concrete examples.

Here's mine:
http://mysite.verizon.net/miketreseler/count_enable.v
http://mysite.verizon.net/miketreseler/count_enable.vhd
http://mysite.verizon.net/miketreseler/count_enable.pdf
I think Mike Treseler's style makes it easy to auto-generate HDLs, but
not necessarily easy for designers and analysts.

Synthesis generates netlists of primitive elements *from* the HDL code.
I write the HDL myself.

-- Mike Treseler
 
A

Andy

Mike,

I'm struggling to understand the real benefit of your subprograms.
They are not controlling any scope issues, and they don't appear to
help document any functional information (structural, yes, but not
functional). it would seem to me to be less verbose, and no less clear
to dispense with the overhead of subprograms and inline the code in
the process statement region, even if you included line comments like
"-- update regs here".

I can see the benefit of an init_regs procedure if your process
template supported synchronous or asynchronous resets (not at the same
time, but maybe decided by a generic).

I'm also curious why the implementation results in 6 bit registers,
when, as far as I can tell, only 5 bit registers are needed? Is it
because of the reset of all 6 bits? Maybe if you removed the "clear
carry" statements to the bottom of the procedure, it would optimize
out the register bits? Synthesis ought to be able to figure that out,
but I guess not. I prefer integers for this anyway; much easier to
deal with carry logic, and you can convert back to slv in the port
update section.

Andy
 
M

Mike Treseler

Andy said:
I'm struggling to understand the real benefit of your subprograms.


I break the code into init, update_regs, and update ports
to allow changing the reset style

(v_rst, a_rst, s_rst, so_rst)

without otherwise touching the code.

It also makes breaking the template
slightly less likely during edits.
But this is certainly a style option, not a requirement.
Notice in the .v example, I use comments instead.
I'm also curious why the implementation results in 6 bit registers,
when, as far as I can tell, only 5 bit registers are needed?

That's just the RTL view.
One of the bits becomes an asynchronous carry out after synthesis.
I just describe the value updates. Synthesis works out wires vs regs.

-- Mike Treseler
 
A

Andy

to allow changing the reset style

 (v_rst, a_rst, s_rst, so_rst)

without otherwise touching the code.

That's one area from the Keating's article that I'm interested in:
ability to control (from a generic, etc.) whether async or sync reset
gets implemented. It may be as simple as coding both with separate
reset signals, and at the highest level ensuring one and only one is
actually used, with the other constantly driven inactive (e.g. with a
generic-controlled generate). I'd have to experiment to see if that is
possible. Preliminary, local optimization may not be able to ignore
the async reset, and therefore fail to implement the sync reset
optimally if it is the ultimate one used. Maybe gating the two signals
locally with the generic would give it a better clue. Nevertheless,
implementing both is pretty trivial to maintain if the reset
assignments are in one procedure. The sensitivity list issue is not a
problem if there are no events on the async reset signal (or at least
none that make it true).

Whether to implement async reset with the typical "if reset ... elsif
rising_edge... end if" template, or the "if rising_edge...end if; if
reset...end if" template is really a matter of preference.

The former will warn you if you missed a reset assignment, but will
implement extra feedback (clock disable) muxes on missed
(intentionally or not) reset assignments. The latter is more flexible
and will allow you to simply not reset a register if not needed/
allowed (with no feedback mux implemented), but it won't warn you
about (unintentionally) missed ones either.
That's just the RTL view.
One of the bits becomes an asynchronous carry out after synthesis.
I just describe the value updates. Synthesis works out wires vs regs.

OK, thanks for the clarification. When using the "if count - 1 < 0
then" trick with natural subtypes, the RTL view from Synplify shows
the combinatorial carry bit without the extra register, and that was
throwing me off.

Andy
 
A

Andy

I wonder when Gaisler's article was written? It appears to be a
chapter from a book, but I have not found it.

His support for using a record for internal registers is somewhat
negated if a single process is used.

The only thing that has kept me from using record ports is the
inability to have user defined (per element) port modes applicable to
record types. Since I don't recommend component declarations &
instantiations (preferring direct entity instantiations), the primary
advantage to records on ports is to simplify the plumbing of complex
buses through multiple levels of hierarchy. But if the records have to
be separated for in/out/inout ports, that means that separate record
types for the same entire bus have to be defined for master and/or
slave destinations, not to mention the translations required between
those types, virtually eliminating the encapsulation benefit.

Depending on when this was written (e.g. Synopsys did not fully
support entity instantiations until at least after 2001), this may
have been as bleeding edge as he could go, and the move to entity
instantiations and single, clocked process style may not have caught
on by that time. Nevertheless Gaisler clearly demonstrates important
SW engineering aspects applicable to HDL based hardware design, and
gets his reader to start thinking along those lines.

Andy
 
A

Amal

It is quite easy to use macros to do both in Verilog. Using macros
and pre-processor you can:

// For ASync reset
`define RESET posedge reset,

// For Sync reset
`define RESET

always @( RESET posedge clock ) begin: p_register
if ( reset ) begin
...
end else begin
...
end
end : p_register


In Verilog/SystemVerilog/VHDL using parameters/generics and generate
statement is a bit more verbose:


// Verilog 2001/SystemVerilog
parameter bit RESET_ASYNC = 0 // 0: SYNC, 1: ASYNC

generate
if RESET_ASYNC begin : g_ASync

always @( posedge reset, posedge clock ) begin: p_register
if ( reset ) begin
...
end else begin
...
end
end : p_register

end else begin

always @( posedge clock ) begin: p_register
if ( reset ) begin
...
end else begin
...
end
end : p_register

end
endgenerate


// VHDL
generic RESET_ASYNC : bit := '0'; // '0': Sync, '1': ASync

g_ASync: if RESET_ASYNC begin

p_register: process( reset, clock ) begin
if ( reset='1' ) then
...
elsif ( rising_edge(clock) ) then
...
end if;
endprocess p_register;

end g_ASync;

g_Sync: if not RESET_ASYNC begin

p_register: process( clock ) begin
if ( rising_edge(clock) ) then
if ( reset='1' ) then
...
else
...
end if;
end if;
endprocess p_register;

end g_Sync;

-- Amal
 
A

Andy

// VHDL
generic RESET_ASYNC : bit := '0';  // '0': Sync, '1': ASync

g_ASync: if RESET_ASYNC begin

  p_register: process( reset, clock ) begin
    if ( reset='1' ) then
      ...
    elsif ( rising_edge(clock) ) then
      ...
    end if;
  endprocess p_register;

end g_ASync;

g_Sync: if not RESET_ASYNC begin

  p_register: process( clock ) begin
    if ( rising_edge(clock) ) then
      if ( reset='1' ) then
        ...
      else
        ...
      end if;
    end if;
  endprocess p_register;

end g_Sync;


I was thinking more along the lines of the following for VHDL (GAR is
generic):

process (clk, rst) is
variable ...
procedure init (...) is
...
end procedure init;
begin
if rst and GAR then
init;
elsif rising_edge(clk) then
if rst and not GAR then
init;
else
...
end if;
end if;
end process;

or, if you wanted selective initialization of individual registers
(without feedback muxes):

process (clk, rst) is
variable ...
procedure init (...) is
-- ...
end procedure init;
begin
if rising_edge(clk) then
-- normal sync processing
-- ...
if rst and not GAR then
init;
end if;
end if;
if rst and GAR then
init;
end if;
end process;

Note I haven't tried this yet, so the synthesis tool may have a
problem recognizing that it does not have to use an async and sync
reset, then optimize one away later, which could result in sub-optimal
sync reset implementation (using AND logic).

Andy
 
M

Mike Treseler

That's one area from the Keating's article that I'm interested in:
ability to control (from a generic, etc.) whether async or sync reset
gets implemented.


The generic template cases I have tested are below.

-- Mike Treseler



------------------------------------------------------------------------
procedure template_v_rst is -- My default.
begin -- a_rst is logically equivalent
if reset = '1' then -- Assumes synched trailing edge reset pulse
init_regs; -- reg_v := init_c; Variables only,
elsif rising_edge(clock) then
update_regs; -- reg_v := f(reg_v);Variables only
end if; -- Synchronous init optional
-- (state_v = idle_c)
update_ports; -- will infer port wires ok for reset and clock
end procedure template_v_rst; -- out_port <= reg_v; ports only
------------------------------------------------------------------------
procedure template_s_rst is -- for use in template comparisons
begin
if rising_edge(clock) then
if reset = '1' then
init_regs;
update_ports;
else
update_regs;
update_ports;
end if;
end if;
end procedure template_s_rst;
-----------------------------------------------------------------------
procedure template_a_rst is -- for use in template comparisons
begin -- Has proven equivalent to v_rst for synthesis.
if reset = '1' then
init_regs;
update_ports;
elsif rising_edge(clock) then
update_regs;
update_ports;
end if;
end procedure template_a_rst;
---------------------------------------------------------------------
procedure template_so_rst is -- Optional Reset by Andy
begin -- Compatible with non-resettable blocks
if rising_edge(clock) then
update_regs;
end if;
if reset = '1' then
init_regs;
end if;
update_ports;
end procedure template_so_rst;
--------------------------------------------------------------------
begin -- process main
case template_g is
when a_rst => template_a_rst;
when s_rst => template_s_rst;
when so_rst => template_so_rst;
when others => template_v_rst;
end case;
end process main;
 
C

Colin Paul Gloster

On Fri, 10 Jul 2009, Jan Decaluwe posted:

|-----------------------------------------------------------------------|
|"Since I started with RTL design in 1991, my goal has been to raise the|
|abstraction level as much as possible. Two key characteristics of my |
|design style are: |
| |
|* using a single clocked process for both control and data operations |
|* using variables, including register inference from them |
| |
|Since the early days I am convinced that this is a superior coding |
|style." |
|-----------------------------------------------------------------------|

Why?

|----------------------------------------------------------------------|
|" Therefore, I always expected it would become the mainstream |
|style anytime soon. On this I have been dead wrong. In the past two |
|decades, the mainstream style has moved in the opposite direction. The|
|absolute low point was Cliff Cummings' guideline not to use blocking |
|assignments in a clocked process, which effectively bans variable |
|semantics for RTL designers. |
| |
|However, now there may a new fact. For the first time, a high-profile |
|publication is in the making with a different message. It is |
|appropriately called "The Art of Good Design". Interestingly, it is |
|being written by Mike Keating, the co-author of the Reuse Methodology |
|Manual (RMM). I believe the RMM is partially responsible for the |
|"unstructured chaos" we are in (Mr. Keatings' words). So it's good to |
|see that he is changing his mind." |
|----------------------------------------------------------------------|

How do you know that he has repented instead of noticed a marketing
possibility, like Grady Booch?

|----------------------------------------------------------------------|
|" I have the impression that the |
|change comes after a careful study and rewriting of real designs. Good|
|that someone takes the time to do this." |
|----------------------------------------------------------------------|

"Reuse Methodology Manual for System-on-a-Chip" was supposedly based
on experience of real designs.

|----------------------------------------------------------------------|
|"In a recent post on his blog, Mr. Keating announced a preview of |
|Chapter 3 of The Art of Good Design: |
| |
|http://synopsysoc.org/futureofdesign/?p=35 |
| |
|[..] Moreover, he discards the |
|guideline not to mix blocking and non-blocking assignments. He does |
|this implicitly, as all blocking assignments are encapsulated in |
|functions, but this doesn't fundamentally alter the conclusion. |
| |
|[..]" |
|----------------------------------------------------------------------|

I do not have time to read his blog now, but perhaps he has not
conciously discarded that rule. I remember a lecturer who insisted
that shared memory should always be used instead of message passing,
so students whose project was rejected because of message passing
pointed out with glee that the lecturer accidentally used message
passing in a later lecture.

I have seen much SystemC(R) code which was presented as an example of
object orientation which was not at all like OO.

Regards,
Paul Colin Gloster
 
M

Mike Treseler

On Fri, 10 Jul 2009, Jan Decaluwe posted:

|-----------------------------------------------------------------------|
|"Since I started with RTL design in 1991, my goal has been to raise the|
|abstraction level as much as possible. Two key characteristics of my |
|design style are: |
| |
|* using a single clocked process for both control and data operations |
|* using variables, including register inference from them |
| |
|Since the early days I am convinced that this is a superior coding |
|style." |
|-----------------------------------------------------------------------|



I find it easy to follow sequential
code with variable registers.
I don't have to fire up the simulator
or sketch boxes and arrows just
to figure out the design intent.

-- Mike Treseler
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top