Philosophical placement of counter

Discussion in 'VHDL' started by Lango, Oct 17, 2009.

  1. Lango

    Lango Guest

    Hello. I normally have the control description (normally a fsm)
    separate from the datapath. Supposing I need a counter to time the
    duration of some of the control outputs, for example, enable this mux
    for 1000 cycles before doing anything else, would you declare the
    counter inside or outside of the fsm architecture?
     
    Lango, Oct 17, 2009
    #1
    1. Advertising

  2. Lango

    KJ Guest

    On Oct 17, 4:16 pm, Lango <> wrote:
    > Hello.  I normally have the control description (normally a fsm)
    > separate from the datapath.


    That's a religious thing, not a design requirement or anything that
    necessarily improves anything...but OK.

    > Supposing I need a counter to time the
    > duration of some of the control outputs, for example, enable this mux
    > for 1000 cycles before doing anything else, would you declare the
    > counter inside or outside of the fsm architecture?


    Depends on what you mean by 'fsm architecture'. For the sake of
    discussion, I'll assume that you mean a VHDL 'process'. Next, I
    wouldn't get philosophical about it but instead focus on the
    readability and maintainability of the design. A VHDL process that
    requires paging back and forth in order to be understood likely does
    not rank very high on the readability and maintainability score (which
    is subjective). Given no other information about what function is to
    be accomplished in the entity I would most likely start like the code
    I've shown below.

    If either the counter logic or the FSM logic (or other logic in the
    architecture) grew to the point where the process gets to be too long,
    I would likely break it up into separate clocked processes just to try
    to keep related things together and try to make it a bit easier to
    understand.

    Long processes can spawn use of numerous variables. While variables
    are a good thing, assignments to variables create a line in the sand.
    Prior to the assignment, the variable means one thing, after the
    assignment it means something else. Since code generally tends to
    grow over time and not shrink it can cause problems if the code starts
    to look a bit unwieldy since you can't easily move things around
    without destroying the function. In my example, I didn't use
    variables so the logic implementing the FSM and the logic implementing
    the counter could be rearranged as separate block for readability
    however I see fit...it's all happening in parallel.

    You having the take that control logic must be separated from control
    logic you've put up some artificial barriers that are philosophical
    but do not contribute to making a design more robust or maintainable.
    As I wrote it, the counter is separated from the control logic but not
    because of any philosophy. The two functions are loosely coupled (the
    FSM tells the counter to start, the counter tells the FSM when it's
    done) so the logic to implement each tends to not want to live inside
    the other.

    A more pragmatic approach that will lead to better design is to view
    sub-functions to see if they fit the model of 'request/acknowledge' or
    'start/complete' 'command/wait' (whichever works for you). You'll
    find that in many cases they do. In this case, the FSM commands the
    counter to start (via being in a particular state...or could've been
    as a separate signal if you prefer) and the counter then signals back
    when it completes (via the 'Done_Counting' signal). Once you take
    that viewpoint, you'll probably see that the logic for sub-functions
    naturally separates into more readable, maintainable code.

    Kevin Jennings
    ----
    architecture RTL of Something is
    type t_STATES is (Idle, Start_Count, Done);
    signal Current_State: t_STATES;
    constant MAX_COUNT: natural := 999;
    signal Counter: natural range 0 to MAX_COUNT;
    signal Done_Counting: std_ulogic;
    begin
    process(Clock)
    begin
    if rising_edge(Clock) then
    ------------
    -- FSM logic
    ------------
    if (Reset = '1') then
    Current_State <= Idle;
    else
    case Current_State is
    when Idle =>
    -- Code to wait for event to start timing
    when Start_Count =>
    if (Done_Counting = '1') then
    Current_State <= Done;
    end if;
    when Done =>
    -- Code to take us back to idle presumably
    end case;
    end if;

    ------------------------
    -- Output signal counter
    ------------------------
    if ((Current_State /= Start_Count)
    or (Counter = MAX_COUNT) then
    Counter <= 0;
    else
    Counter <= Counter + 1;
    end if;
    end process;

    Done_Counting <= '1' when (Counter = MAX_COUNT) else '0';
     
    KJ, Oct 18, 2009
    #2
    1. Advertising

  3. Lango

    Lango Guest

    On Oct 18, 7:33 am, KJ <> wrote:
    > On Oct 17, 4:16 pm, Lango <> wrote:
    >
    > > Hello.  I normally have the control description (normally a fsm)
    > > separate from the datapath.

    >
    > That's a religious thing, not a design requirement or anything that
    > necessarily improves anything...but OK.
    >
    > > Supposing I need a counter to time the
    > > duration of some of the control outputs, for example, enable this mux
    > > for 1000 cycles before doing anything else, would you declare the
    > > counter inside or outside of the fsm architecture?

    >
    > Depends on what you mean by 'fsm architecture'.  For the sake of
    > discussion, I'll assume that you mean a VHDL 'process'.  Next, I
    > wouldn't get philosophical about it but instead focus on the
    > readability and maintainability of the design.  A VHDL process that
    > requires paging back and forth in order to be understood likely does
    > not rank very high on the readability and maintainability score (which
    > is subjective).  Given no other information about what function is to
    > be accomplished in the entity I would most likely start like the code
    > I've shown below.
    >
    > If either the counter logic or the FSM logic (or other logic in the
    > architecture) grew to the point where the process gets to be too long,
    > I would likely break it up into separate clocked processes just to try
    > to keep related things together and try to make it a bit easier to
    > understand.
    >
    > Long processes can spawn use of numerous variables.  While variables
    > are a good thing, assignments to variables create a line in the sand.
    > Prior to the assignment, the variable means one thing, after the
    > assignment it means something else.  Since code generally tends to
    > grow over time and not shrink it can cause problems if the code starts
    > to look a bit unwieldy since you can't easily move things around
    > without destroying the function.  In my example, I didn't use
    > variables so the logic implementing the FSM and the logic implementing
    > the counter could be rearranged as separate block for readability
    > however I see fit...it's all happening in parallel.
    >
    > You having the take that control logic must be separated from control
    > logic you've put up some artificial barriers that are philosophical
    > but do not contribute to making a design more robust or maintainable.
    > As I wrote it, the counter is separated from the control logic but not
    > because of any philosophy.  The two functions are loosely coupled (the
    > FSM tells the counter to start, the counter tells the FSM when it's
    > done) so the logic to implement each tends to not want to live inside
    > the other.
    >
    > A more pragmatic approach that will lead to better design is to view
    > sub-functions to see if they fit the model of 'request/acknowledge' or
    > 'start/complete' 'command/wait' (whichever works for you).  You'll
    > find that in many cases they do.  In this case, the FSM commands the
    > counter to start (via being in a particular state...or could've been
    > as a separate signal if you prefer) and the counter then signals back
    > when it completes (via the 'Done_Counting' signal).  Once you take
    > that viewpoint, you'll probably see that the logic for sub-functions
    > naturally separates into more readable, maintainable code.
    >
    > Kevin Jennings
    > ----
    > architecture RTL of Something is
    >   type t_STATES is (Idle, Start_Count, Done);
    >   signal Current_State: t_STATES;
    >   constant MAX_COUNT: natural := 999;
    >   signal Counter:  natural range 0 to MAX_COUNT;
    >   signal Done_Counting: std_ulogic;
    > begin
    >   process(Clock)
    >   begin
    >     if rising_edge(Clock) then
    >       ------------
    >       -- FSM logic
    >       ------------
    >       if (Reset = '1') then
    >         Current_State <= Idle;
    >       else
    >         case Current_State is
    >           when Idle =>
    >             -- Code to wait for event to start timing
    >           when Start_Count =>
    >             if (Done_Counting = '1') then
    >               Current_State <= Done;
    >             end if;
    >           when Done =>
    >             -- Code to take us back to idle presumably
    >         end case;
    >       end if;
    >
    >       ------------------------
    >       -- Output signal counter
    >       ------------------------
    >       if ((Current_State /= Start_Count)
    >       or (Counter = MAX_COUNT) then
    >         Counter <= 0;
    >       else
    >         Counter <= Counter + 1;
    >       end if;
    >   end process;
    >
    > Done_Counting <= '1' when (Counter = MAX_COUNT) else '0';


    Thanks for your post. I mostly agree with you, I'd put the counter
    along with the fsm logic, as opposed to the datapath code placeholder,
    mostly because the 'passing data' never interacts with the counter, it
    is more like an 'fsm helper', so it is more readable if it described
    nearby. I however feel that having the control logic separate is
    usually helpful. This can happen in any levels, and this does not
    mean that there is no control logic in the blocks of code that make up
    what one normally considers the datapath. For example, a filter that
    is receiving a BT656 video stream and only outputs active video
    (filtering out blanking, etc) is seen by a higher level as a datapath
    block (filter), but its description may actually be composed of a
    datapath/fsm type of design as well. On a related note, lets say that
    when one block finishes a task, another block must be activated. One
    might feel tempted to connect the done signal of one to the start
    signal of the other, and the fsm wouldn't even see that net. However,
    I feel that that connection is more an optimization step than good
    design practice, simply because a future requirement might introduce
    additional conditions - when b1 finishes, then b2 starts, UNLESS blah
    blah blah - and inserting this new condition can be more easily added
    by modifying the fsm's code, than having to modify the datapath. Of
    course, there can be many exceptions, and I share the pragmatic point
    of view of just wirte code what works, although I sometimes find
    myself relocating code so that it becomes more modular and reusable,
    and somehow matches the mental structure that I have of the design,
    sometimes even at the slight expense of efficiency.
     
    Lango, Oct 19, 2009
    #3
  4. Lango

    Andy Guest

    I tend to put timers and state machines in the same process, but a
    little differently:

    process (clk) is -- omit reset for brevity
    variable timer natural range 0 to start_time;
    varible state is (start, waiting, done);
    begin
    if rising_edge(clk) then
    timeout := timer - 1 < 0;
    if not timeout then
    timer := timer - 1;
    end if;

    case state is
    when start=>
    if event1 then
    timer := start_time;
    state := waiting;
    end if;
    when waiting=>
    if timeout then
    state := done;
    end if;
    when done =>
    ...
    end case;
    end if;
    end process;

    I prefer NOT to have code outside of the state machine itself (the
    case statement) query a state variable. I prefer the state machine to
    tell that code to do something rather than have that code ask the
    state machine. This way, if i change up my state names, etc. it has no
    impact on other code. Likewise nothing but the state machine is
    relevant to state encoding optimizations.

    When used with these restrictions, a variable for the state works
    great, and it limits scope to the local process anyway.

    Andy
     
    Andy, Oct 19, 2009
    #4
  5. Andy wrote:
    > I tend to put timers and state machines in the same process, but a
    > little differently:


    Me too, but a little more differently.

    > process (clk) is -- omit reset for brevity
    > variable timer natural range 0 to start_time;
    > varible state is (start, waiting, done);
    > begin
    > if rising_edge(clk) then
    > timeout := timer - 1 < 0;
    > if not timeout then
    > timer := timer - 1;
    > end if;
    >
    > case state is
    > when start=>
    > if event1 then
    > timer := start_time;
    > state := waiting;
    > end if;
    > when waiting=>
    > if timeout then
    > state := done;
    > end if;
    > when done =>
    > ...
    > end case;
    > end if;
    > end process;


    Nice example of inferring rather than describing the counter wires.

    > I prefer NOT to have code outside of the state machine itself (the
    > case statement) query a state variable. I prefer the state machine to
    > tell that code to do something rather than have that code ask the
    > state machine. This way, if i change up my state names, etc. it has no
    > impact on other code. Likewise nothing but the state machine is
    > relevant to state encoding optimizations.


    I also like to keep as much as possible inside the box
    and handshake at as high a level as I can stand,
    because all of the internal wiring is inferred for free.

    > When used with these restrictions, a variable for the state works
    > great, and it limits scope to the local process anyway.


    I will note that it works great for the counter as well,
    and since Lango put 'philosophy' in the subject,
    I will claim that I might just as well
    declare all of my counter related registers as
    variables in the same process.

    So what's the downside?

    > KJ wrote:


    >> Long processes can spawn use of numerous variables.
    >> While variables are a good thing, assignments to variables
    >> create a line in the sand. Prior to the assignment,
    >> the variable means one thing, after the assignment
    >> it means something else.


    Which is another way of saying that variable updates are immediate.
    This does mean that I can't make a movable output process as in KJ's
    example:

    > Done_Counting <= '1' when (Counter = MAX_COUNT) else '0';


    because a variable, say done_counting_v describes
    the D side rather than the Q side of the output.
    I can say done_port <= done_v; either at the bottom
    of the process or at just the right place inside.

    So what's the upside to dumping the
    cozy schematic/netlist feel while I'm inside the box?

    1. The synthesis code reads like
    (choose one: C, perl, python, java, <your_fave>, ...)
    so it is easier to test software math models
    without tying up a license.
    2. It is easy to collect blocks of code (aka: refactor)
    into vhdl functions or procedures.
    3. Tracing code in modelsim actually does something useful.
    4. Wires for free.

    -- Mike Treseler
     
    Mike Treseler, Oct 19, 2009
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Dave Rahardja
    Replies:
    11
    Views:
    512
    Risto Lankinen
    Aug 7, 2003
  2. Maurice LING
    Replies:
    2
    Views:
    298
    Paul Rubin
    Jun 3, 2005
  3. Patricia Shanahan
    Replies:
    15
    Views:
    463
    Daniel Pitts
    Mar 6, 2007
  4. Giannis Papadopoulos

    void vs void* (philosophical question)

    Giannis Papadopoulos, Jun 30, 2006, in forum: C Programming
    Replies:
    18
    Views:
    514
    Dave Thompson
    Jul 10, 2006
  5. Madhav
    Replies:
    4
    Views:
    376
    Jerry Coffin
    Mar 13, 2006
Loading...

Share This Page